diff --git a/apps/expo/src/app/driver/become-driver.tsx b/apps/expo/src/app/driver/become-driver.tsx
index 06fd42d..9a903df 100644
--- a/apps/expo/src/app/driver/become-driver.tsx
+++ b/apps/expo/src/app/driver/become-driver.tsx
@@ -1,20 +1,32 @@
import React, { useState } from "react";
-import { Image, Modal, ScrollView, Text, TouchableOpacity, View } from "react-native";
-import { useRouter } from "expo-router";
import {
- AntDesign,
-} from "@expo/vector-icons";
+ Image,
+ LogBox,
+ Modal,
+ Text,
+ TouchableOpacity,
+ View,
+} from "react-native";
+import { SafeAreaView } from "react-native-safe-area-context";
+import { useRouter } from "expo-router";
+import { AntDesign, Ionicons } from "@expo/vector-icons";
+
import { api } from "~/utils/api";
-import { RiderNavbar } from "~/components/Navbar/RiderNavbar";
-const DriverProfile = () => {
+LogBox.ignoreLogs([
+ "TRPCClientError: You are not a driver yet",
+ "Modal with 'formSheet' presentation style and 'transparent' value is not supported.",
+]);
+
+const BecomeDriver = () => {
const router = useRouter();
const [visible, setVisible] = useState(false);
const [capacity, setCapacity] = useState(1);
+ const utils = api.useContext();
const riderQuery = api.rider.profile.useQuery();
const becomeDriver = api.rider.becomeDriver.useMutation({
onSuccess: () => {
- router.push('/driver/profile');
+ void utils.driver.profile.refetch();
},
});
@@ -22,73 +34,91 @@ const DriverProfile = () => {
<>
{
setVisible(!visible);
}}
+ presentationStyle="formSheet"
+ transparent={true}
>
-
- Passenger Capacity
-
-
+
+
+ Passenger Capacity
+
+
+
{capacity}
-
- setCapacity(capacity<10 ? capacity+1 : capacity)}>
+
+
+ setCapacity(capacity < 10 ? capacity + 1 : capacity)
+ }
+ >
➕
- setCapacity(capacity>1 ? capacity-1 : capacity)}>
+
+ setCapacity(capacity > 1 ? capacity - 1 : capacity)
+ }
+ >
➖
-
- {
setVisible(!visible);
- becomeDriver.mutate({capacity: capacity});
+ becomeDriver.mutate({ capacity: capacity });
}}
>
Confirm
-
-
- Profile
-
-
-
-
+
+
+ void router.back()}
+ >
+
+
+
+
-
-
- {riderQuery.data?.name}
-
-
-
- You are not a driver yet.{"\n"}Become driver to earn extra money!
- setVisible(true)}>
-
- Become Driver
+
+
+ You are not a driver yet.{"\n"}Become driver to earn extra
+ money!
+
+ setVisible(true)}
+ >
+
+
+ Become Driver
+
+
+
-
+
-
-
-
+
>
);
};
-export default DriverProfile;
+export default BecomeDriver;
diff --git a/apps/expo/src/app/driver/home.tsx b/apps/expo/src/app/driver/home.tsx
index 3b28203..9163b31 100644
--- a/apps/expo/src/app/driver/home.tsx
+++ b/apps/expo/src/app/driver/home.tsx
@@ -1,14 +1,166 @@
-import { Text } from "react-native";
+import {
+ ScrollView,
+ Text,
+ TextInput,
+ TouchableOpacity,
+ View,
+} from "react-native";
+import { type Region } from "react-native-maps";
import { SafeAreaView } from "react-native-safe-area-context";
+import { Link } from "expo-router";
+import { Feather } from "@expo/vector-icons";
+import DateTimePicker from "@react-native-community/datetimepicker";
+import { atom, useAtom } from "jotai";
+import { api } from "~/utils/api";
import { DriverNavbar } from "~/components/Navbar/DriverNavbar";
+export type Location = {
+ name: string;
+ longitude: number;
+ latitude: number;
+};
+
+export const createLocationsAtom = atom([]);
+export const addLocationAtom = atom(null);
+export const addLocationNameAtom = atom("");
+
+const MILLIS_PER_DAY = 1000 * 86400;
+export const dateAtom = atom(new Date(Date.now() + MILLIS_PER_DAY));
+
const HomePage = () => {
+ const [locations, setLocations] = useAtom(createLocationsAtom);
+ const [addLocation, setAddLocation] = useAtom(addLocationAtom);
+ const [locationName, setLocationName] = useAtom(addLocationNameAtom);
+ const [date, setDate] = useAtom(dateAtom);
+ const addRideMutation = api.driver.create.useMutation();
+
+ const handleAddLocation = () => {
+ setLocations((prev) => [
+ ...prev,
+ {
+ name: locationName,
+ longitude: addLocation?.longitude ?? 0,
+ latitude: addLocation?.latitude ?? 0,
+ },
+ ]);
+ setLocationName("");
+ setAddLocation(null);
+ };
+
+ const handleAddRide = () => {
+ addRideMutation.mutate({
+ departAt: date,
+ locations,
+ });
+ };
+
return (
-
- Home Page
+ <>
+
+
+ Trip
+
+
+
+
+
+ Create a trip !
+
+ setLocationName(text)}
+ />
+
+
+ {addLocation ? (
+
+ ({addLocation?.latitude.toFixed(4)},{" "}
+ {addLocation?.longitude.toFixed(4)})
+
+ ) : (
+
+ Location
+
+ )}
+
+
+ handleAddLocation()}
+ >
+ Add a location
+
+
+
+
+
+
+
+ Depart Time
+ {
+ const currentDate = selectedDate || date;
+ setDate(currentDate);
+ }}
+ display="default"
+ />
+
+
+
+
+ {locations.map((location, id) => (
+
+
+
+ {id + 1}.
+
+
+
+ {location.name}
+
+
+ ({location.latitude.toFixed(4)},{" "}
+ {location.longitude.toFixed(4)})
+
+
+
+
+ setLocations((prev) => {
+ const newLocations = [...prev];
+ newLocations.splice(id, 1);
+ return newLocations;
+ })
+ }
+ >
+
+
+
+ ))}
+
+
+
+ handleAddRide()}
+ >
+
+ Add ride
+
+
+
-
+ >
);
};
diff --git a/apps/expo/src/app/driver/profile.tsx b/apps/expo/src/app/driver/profile.tsx
index 5ae61e6..4c3dd8d 100644
--- a/apps/expo/src/app/driver/profile.tsx
+++ b/apps/expo/src/app/driver/profile.tsx
@@ -1,6 +1,7 @@
import React, { useState } from "react";
import { Image, ScrollView, Text, TouchableOpacity, View } from "react-native";
-import { Link, useRouter } from "expo-router";
+import { SafeAreaView } from "react-native-safe-area-context";
+import { Link } from "expo-router";
import {
Feather,
FontAwesome5,
@@ -15,26 +16,44 @@ import { DriverNavbar } from "~/components/Navbar/DriverNavbar";
import { RatingStars } from "~/components/RatingStars";
import { useObjectState } from "~/hooks/useObjectState";
import SignOut from "~/screens/auth/SignOut";
+import BecomeDriver from "./become-driver";
type ModalTypes = "Bio" | "Rules" | "Capacity" | "none";
-const DriverProfile = () => {
+const ProfilePage = () => {
const [visibleModal, setVisibleModal] = useState("none");
const [profile, updateProfile] = useObjectState({
bio: "",
rules: "",
capacity: "",
});
- const router = useRouter();
const profileQuery = api.driver.profile.useQuery(undefined, {
onSuccess: (data) => {
updateProfile({ ...data, capacity: data.capacity.toString() });
},
+ onError: (err) => {
+ console.log("here is an error", err);
+ },
+ retry(failureCount, error) {
+ if (error.data?.code === "UNAUTHORIZED") return false;
+ return failureCount < 2;
+ },
});
+ if (profileQuery.isLoading) {
+ return (
+
+
+
+ );
+ }
+
if (profileQuery.data == null) {
- router.push("/driver/become-driver");
+ return ;
}
return (
@@ -158,4 +177,4 @@ const DriverProfile = () => {
);
};
-export default DriverProfile;
+export default ProfilePage;
diff --git a/apps/expo/src/app/driver/set-location.tsx b/apps/expo/src/app/driver/set-location.tsx
new file mode 100644
index 0000000..f0e6a79
--- /dev/null
+++ b/apps/expo/src/app/driver/set-location.tsx
@@ -0,0 +1,21 @@
+import { useAtomValue, useSetAtom } from "jotai";
+
+import { LocationPicker } from "~/components/LocationPicker";
+import { addLocationAtom, createLocationsAtom, type Location } from "./home";
+
+const locationToRegion = (location: Location) => ({
+ latitude: location.latitude,
+ longitude: location.longitude,
+ latitudeDelta: 0.09,
+ longitudeDelta: 0.09,
+});
+
+const SetLocationPage = () => {
+ const setAddLocation = useSetAtom(addLocationAtom);
+ const locations = useAtomValue(createLocationsAtom);
+ const regions = locations.map(locationToRegion);
+
+ return ;
+};
+
+export default SetLocationPage;
diff --git a/apps/expo/src/app/index.tsx b/apps/expo/src/app/index.tsx
index f55e12d..7c865ca 100644
--- a/apps/expo/src/app/index.tsx
+++ b/apps/expo/src/app/index.tsx
@@ -1,9 +1,10 @@
import React from "react";
import { Pressable, Text, View } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
-import { Link } from "expo-router";
+import { Link, Redirect } from "expo-router";
const IndexPage = () => {
+ return ;
return (
diff --git a/apps/expo/src/app/rider/history.tsx b/apps/expo/src/app/rider/history.tsx
index 8d18bf5..f4c4581 100644
--- a/apps/expo/src/app/rider/history.tsx
+++ b/apps/expo/src/app/rider/history.tsx
@@ -8,7 +8,7 @@ import { api } from "~/utils/api";
import { HistoryItem } from "~/components/HistoryItem";
const HistoryPage = () => {
- const historyQuery = api.rider.rideHistory.useQuery();
+ const historyQuery = api.rider.rideHistory.useQuery({ limit: 10 });
const router = useRouter();
return (
diff --git a/apps/expo/src/app/rider/home.tsx b/apps/expo/src/app/rider/home.tsx
index f0ae1a8..4748cf1 100644
--- a/apps/expo/src/app/rider/home.tsx
+++ b/apps/expo/src/app/rider/home.tsx
@@ -49,6 +49,7 @@ const HomePage = () => {
Recent
{recentQuery.data?.map(({ id, source, destination, departAt }) => (
{
router.push("/rider/search");
setDataAtom(departAt);
@@ -72,6 +73,7 @@ const HomePage = () => {
Favorite
{favoriteQuery.data?.map(({ id, source, destination, departAt }) => (
{
router.push("/rider/search");
setDataAtom(departAt);
diff --git a/apps/expo/src/components/HistoryModal.tsx b/apps/expo/src/components/HistoryModal.tsx
index 0f6f4b3..015dc07 100644
--- a/apps/expo/src/components/HistoryModal.tsx
+++ b/apps/expo/src/components/HistoryModal.tsx
@@ -58,6 +58,7 @@ export const HistoryModal = (props: HistoryModalProps) => {
{new Array(5).fill(null).map((_, i) => (
{
numberOfLines={10}
onChangeText={setCommentInput}
value={commentInput}
- className="text-md mx-2 rounded-lg bg-white px-5"
+ className="text-md mx-2 w-full rounded-lg bg-gray-200 px-5"
placeholder={commentInput}
/>
diff --git a/apps/expo/src/components/LocationPicker.tsx b/apps/expo/src/components/LocationPicker.tsx
new file mode 100644
index 0000000..20e1460
--- /dev/null
+++ b/apps/expo/src/components/LocationPicker.tsx
@@ -0,0 +1,109 @@
+import { useEffect, useRef, useState } from "react";
+import { Image, TouchableOpacity, View } from "react-native";
+import MapView, {
+ Marker,
+ PROVIDER_GOOGLE,
+ type Region,
+} from "react-native-maps";
+import { SafeAreaView } from "react-native-safe-area-context";
+import {
+ getCurrentPositionAsync,
+ requestForegroundPermissionsAsync,
+} from "expo-location";
+import { useRouter } from "expo-router";
+import { FontAwesome, Ionicons } from "@expo/vector-icons";
+
+type LocationPickerProps = {
+ locations: Region[];
+ setLocation: (location: Region) => void;
+};
+
+export const LocationPicker = ({
+ setLocation,
+ locations,
+}: LocationPickerProps) => {
+ const [region, setRegion] = useState(null);
+ const mapRef = useRef(null);
+
+ useEffect(() => {
+ const setCurrentLocation = async () => {
+ if (locations[0] != null) {
+ setRegion(locations[0]);
+ return;
+ }
+
+ const { status } = await requestForegroundPermissionsAsync();
+ if (status !== "granted") {
+ return;
+ }
+
+ const currentLocation = await getCurrentPositionAsync({});
+ setRegion({
+ latitude: currentLocation.coords.latitude,
+ longitude: currentLocation.coords.longitude,
+ latitudeDelta: 0.09,
+ longitudeDelta: 0.09,
+ });
+ };
+ void setCurrentLocation();
+ }, []);
+
+ const router = useRouter();
+ const handleClick = ({ region }: { region: Region | null }) => {
+ if (region == null) return;
+ setLocation(region);
+ router.back();
+ };
+
+ if (!region) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+
+ router.back()}
+ >
+
+
+
+
+ setRegion(newRegion)}
+ >
+
+ {locations.map((location, index) => (
+
+ ))}
+
+
+ handleClick({ region })}
+ />
+
+
+ );
+};
diff --git a/apps/expo/src/components/RatingStars.tsx b/apps/expo/src/components/RatingStars.tsx
index 814fb40..fdf35e2 100644
--- a/apps/expo/src/components/RatingStars.tsx
+++ b/apps/expo/src/components/RatingStars.tsx
@@ -10,6 +10,7 @@ export const RatingStars = ({ stars }: RatingStarsProps) => {
{new Array(5).fill(null).map((_, i) => (
{
const { registerItemProps, setModalVisible } = props;
return (
setModalVisible(true)}>
-
-
-
-
-
+
+
+
{(registerItemProps.type === "driver-passengers" ||
registerItemProps.type === "rider-approved") && (
-
+
{registerItemProps.person.name}
)}
{(registerItemProps.type === "driver-pending" ||
registerItemProps.type === "rider-pending") && (
-
+
{registerItemProps.person.name}
)}
@@ -59,13 +57,9 @@ const RegisterSubItem = (props: RegisterSubItemProps) => {
{registerItemProps.departAt.toDateString()}
+ {registerItemProps.source.name}
- ({registerItemProps.source.latitude},{" "}
- {registerItemProps.source.longitude})
-
-
- ({registerItemProps.desiredDestination.latitude},{" "}
- {registerItemProps.desiredDestination.longitude})
+ {registerItemProps.desiredDestination.name}
$ {registerItemProps.price}
diff --git a/apps/expo/src/components/RideModal.tsx b/apps/expo/src/components/RideModal.tsx
index 7c6d8d4..473eaee 100644
--- a/apps/expo/src/components/RideModal.tsx
+++ b/apps/expo/src/components/RideModal.tsx
@@ -1,5 +1,12 @@
import React, { type Dispatch, type SetStateAction } from "react";
-import { Image, Modal, Text, TouchableOpacity, View } from "react-native";
+import {
+ Image,
+ LogBox,
+ Modal,
+ Text,
+ TouchableOpacity,
+ View,
+} from "react-native";
import MapView, { PROVIDER_GOOGLE } from "react-native-maps";
import {
AntDesign,
@@ -12,6 +19,10 @@ import {
import { api } from "~/utils/api";
import type { RegisterItemProps } from "./RegisterItem";
+LogBox.ignoreLogs([
+ "Modal with 'formSheet' presentation style and 'transparent' value is not supported.",
+]);
+
export type RideModalProps = {
id: string;
type: string;
@@ -23,8 +34,18 @@ export type RideModalProps = {
export const RideModal = (props: RideModalProps) => {
const utils = api.useContext();
const { id, type, modalVisible, setModalVisible, registerItemProps } = props;
- const cancelRideMutation = api.rider.leaveRide.useMutation();
- const driverMutation = api.driver.manageRider.useMutation();
+ const cancelRideMutation = api.rider.leaveRide.useMutation({
+ onSuccess: () => {
+ void utils.driver.approvedRider.invalidate();
+ void utils.driver.pendingRider.invalidate();
+ },
+ });
+ const driverMutation = api.driver.manageRider.useMutation({
+ onSuccess: () => {
+ void utils.driver.approvedRider.invalidate();
+ void utils.driver.pendingRider.invalidate();
+ },
+ });
const finishRide = api.driver.finishRide.useMutation({
onSuccess: () => {
void utils.driver.approvedRider.invalidate();
@@ -37,12 +58,13 @@ export const RideModal = (props: RideModalProps) => {
{
setModalVisible(!modalVisible);
}}
>
-
+
@@ -74,12 +96,10 @@ export const RideModal = (props: RideModalProps) => {
{registerItemProps.departAt.toDateString()}
- ({registerItemProps.source.latitude},{" "}
- {registerItemProps.source.longitude})
+ {registerItemProps.source.name}
- ({registerItemProps.desiredDestination.latitude},{" "}
- {registerItemProps.desiredDestination.longitude})
+ {registerItemProps.desiredDestination.name}
$ {registerItemProps.price}
@@ -112,26 +132,6 @@ export const RideModal = (props: RideModalProps) => {
});
setModalVisible(!modalVisible);
}}
- >
-
-
-
-
- Cancel
-
-
-
- {
- finishRide.mutate({
- rideId: id,
- });
- setModalVisible(!modalVisible);
- }}
>
diff --git a/apps/expo/src/components/SearchModal.tsx b/apps/expo/src/components/SearchModal.tsx
index 1cc9b62..0bf5962 100644
--- a/apps/expo/src/components/SearchModal.tsx
+++ b/apps/expo/src/components/SearchModal.tsx
@@ -1,6 +1,6 @@
import React, { type Dispatch, type SetStateAction } from "react";
import { Image, Modal, Text, TouchableOpacity, View } from "react-native";
-import MapView, { PROVIDER_GOOGLE } from "react-native-maps";
+import MapView, { Marker, PROVIDER_GOOGLE } from "react-native-maps";
import {
AntDesign,
EvilIcons,
@@ -82,12 +82,15 @@ export const SearchModal = (props: SearchModalProps) => {
className="h-96 w-96"
showsUserLocation
initialRegion={{
- latitude: 24.8148,
- longitude: 120.9675,
+ latitude: props.travel.source.latitude,
+ longitude: props.travel.source.longitude,
latitudeDelta: 0.09,
longitudeDelta: 0.09,
}}
- >
+ >
+
+
+
@@ -96,8 +99,8 @@ export const SearchModal = (props: SearchModalProps) => {
onPress={() => {
applyRideMutation.mutate({
rideId: travel.id,
- source,
- destination,
+ sourceId: travel.source.id,
+ destinationId: travel.desiredDestination.id,
});
setModalVisible(!modalVisible);
}}
diff --git a/packages/api/src/router/driver.ts b/packages/api/src/router/driver.ts
index 8f69ec7..27e5eed 100644
--- a/packages/api/src/router/driver.ts
+++ b/packages/api/src/router/driver.ts
@@ -4,7 +4,7 @@ import { z } from "zod";
import { DriverServiceErrors } from "@rideplus/internal";
-import { location } from "../schema/location";
+import { namedLocation } from "../schema/location";
import { createTRPCRouter, protectedProcedure } from "../trpc";
export const driverRouter = createTRPCRouter({
@@ -145,7 +145,7 @@ export const driverRouter = createTRPCRouter({
create: protectedProcedure
.input(
z.object({
- locations: z.array(location()),
+ locations: z.array(namedLocation()),
departAt: z.date(),
}),
)
@@ -180,7 +180,7 @@ export const driverRouter = createTRPCRouter({
const status = input.action === "approve" ? "APPROVED" : "CANCELLED";
const passengerRide = await ctx.driverService.manageRider({
driverId: ctx.auth.userId,
- driverRideId: input.rideId,
+ passengerRideId: input.rideId,
passengerId: input.riderId,
status,
});
@@ -193,10 +193,10 @@ export const driverRouter = createTRPCRouter({
message: "You are not a driver yet",
});
})
- .with({ error: DriverServiceErrors.DRIVER_RIDE_NOT_FOUND }, () => {
+ .with({ error: DriverServiceErrors.PASSANGER_RIDE_NOT_FOUND }, () => {
throw new TRPCError({
code: "NOT_FOUND",
- message: "Driver ride not found",
+ message: "Passenger ride not found",
});
})
.exhaustive();
@@ -223,12 +223,24 @@ export const driverRouter = createTRPCRouter({
message: "You are not a driver yet",
});
})
+ .with({ error: DriverServiceErrors.PASSANGER_RIDE_NOT_FOUND }, () => {
+ throw new TRPCError({
+ code: "NOT_FOUND",
+ message: "Passenger ride not found",
+ });
+ })
.with({ error: DriverServiceErrors.DRIVER_RIDE_NOT_FOUND }, () => {
throw new TRPCError({
code: "NOT_FOUND",
message: "Driver ride not found",
});
})
+ .with({ error: DriverServiceErrors.NOT_OWNED_RIDE }, () => {
+ throw new TRPCError({
+ code: "FORBIDDEN",
+ message: "You are not the owner of this ride",
+ });
+ })
.exhaustive();
return result;
diff --git a/packages/api/src/router/rider.ts b/packages/api/src/router/rider.ts
index 6a63570..aba2e7f 100644
--- a/packages/api/src/router/rider.ts
+++ b/packages/api/src/router/rider.ts
@@ -258,16 +258,16 @@ export const riderRouter = createTRPCRouter({
.input(
z.object({
rideId: z.string(),
- source: location(),
- destination: location(),
+ sourceId: z.string(),
+ destinationId: z.string(),
}),
)
.mutation(async ({ ctx, input }) => {
const passengerRide = await ctx.passengerService.applyRide({
passengerId: ctx.auth.userId,
driverRideId: input.rideId,
- source: input.source,
- destination: input.destination,
+ sourceId: input.sourceId,
+ destinationId: input.destinationId,
});
const result = match(passengerRide)
diff --git a/packages/api/src/schema/location.ts b/packages/api/src/schema/location.ts
index e9470ea..97f4797 100644
--- a/packages/api/src/schema/location.ts
+++ b/packages/api/src/schema/location.ts
@@ -6,3 +6,10 @@ export const location = () => {
longitude: z.number(),
});
};
+export const namedLocation = () => {
+ return z.object({
+ latitude: z.number(),
+ longitude: z.number(),
+ name: z.string().min(1),
+ });
+};
diff --git a/packages/internal/src/core/domain/driverRide.ts b/packages/internal/src/core/domain/driverRide.ts
index d013caa..b925cc8 100644
--- a/packages/internal/src/core/domain/driverRide.ts
+++ b/packages/internal/src/core/domain/driverRide.ts
@@ -1,11 +1,11 @@
-import { type NamedLocation } from "./location";
+import { type NamedLocationWithId } from "./location";
export type DriverRideStatus = "OPEN" | "CLOSED";
export type DriverRide = {
id: string;
driverId: string;
- locations: NamedLocation[];
+ locations: NamedLocationWithId[];
status: DriverRideStatus;
departAt: Date;
};
diff --git a/packages/internal/src/core/domain/location.ts b/packages/internal/src/core/domain/location.ts
index cdccd80..3f0a3b8 100644
--- a/packages/internal/src/core/domain/location.ts
+++ b/packages/internal/src/core/domain/location.ts
@@ -5,4 +5,12 @@ export type Location = {
longitude: number;
};
+export type LocationWithId = Expand<
+ {
+ id: string;
+ } & Location
+>;
+
export type NamedLocation = Expand;
+
+export type NamedLocationWithId = Expand;
diff --git a/packages/internal/src/core/ports/driverRideRepository.ts b/packages/internal/src/core/ports/driverRideRepository.ts
index 85366dd..e216291 100644
--- a/packages/internal/src/core/ports/driverRideRepository.ts
+++ b/packages/internal/src/core/ports/driverRideRepository.ts
@@ -1,9 +1,17 @@
-import { type DriverRide } from "../../core/domain/driverRide";
-import { type MakeOptional } from "../../types/magic";
-import { type Location } from "../domain/location";
+import {
+ type DriverRide,
+ type DriverRideStatus,
+} from "../../core/domain/driverRide";
+import { type Location, type NamedLocation } from "../domain/location";
import { type RideReview } from "../domain/rideReview";
-export type SaveDriverRideInput = MakeOptional;
+export type SaveDriverRideInput = {
+ id?: string;
+ driverId: string;
+ locations: NamedLocation[];
+ departAt: Date;
+ status?: DriverRideStatus;
+};
export type FindByNearbyLocationsInput = {
source: Location;
diff --git a/packages/internal/src/core/ports/locationRepository.ts b/packages/internal/src/core/ports/locationRepository.ts
index 16ea558..90a8b26 100644
--- a/packages/internal/src/core/ports/locationRepository.ts
+++ b/packages/internal/src/core/ports/locationRepository.ts
@@ -3,7 +3,7 @@ import { type Location, type NamedLocation } from "../../core/domain/location";
export type LocationWithDistance = NamedLocation & { distance: number };
export type LocationRepository = {
- findName: (location: Location[]) => Promise;
+ findName: (locationIds: string[]) => Promise;
findNearby(
location: Location,
maxDistance: number,
diff --git a/packages/internal/src/core/ports/passengerRideRepository.ts b/packages/internal/src/core/ports/passengerRideRepository.ts
index 443b5b7..742b2bb 100644
--- a/packages/internal/src/core/ports/passengerRideRepository.ts
+++ b/packages/internal/src/core/ports/passengerRideRepository.ts
@@ -34,4 +34,5 @@ export type PassengerRideRepository = {
driverId: string,
status: PassengerRideStatus,
): Promise;
+ findById(id: string): Promise;
};
diff --git a/packages/internal/src/core/service/driverService.ts b/packages/internal/src/core/service/driverService.ts
index 396d01b..3ab810f 100644
--- a/packages/internal/src/core/service/driverService.ts
+++ b/packages/internal/src/core/service/driverService.ts
@@ -1,6 +1,6 @@
import { type PassengerRideStatus } from "@prisma/client";
-import { type Location } from "../../core/domain/location";
+import { type NamedLocation } from "../../core/domain/location";
import { error, success } from "../../types/result";
import {
type DriverRepository,
@@ -30,25 +30,26 @@ type EditProfileInput = {
type CreateDriverRideInput = {
driverId: string;
departAt: Date;
- locations: Location[];
+ locations: NamedLocation[];
};
type ManagePassengerInput = {
driverId: string;
passengerId: string;
- driverRideId: string;
+ passengerRideId: string;
status: PassengerRideStatus;
};
export const DriverServiceErrors = {
PROVIDER_USER_NOT_FOUND: "provider user not found",
NOT_A_DRIVER: "not a driver",
+ NOT_OWNED_RIDE: "not owned ride",
DRIVER_RIDE_NOT_FOUND: "driver ride not found",
+ PASSANGER_RIDE_NOT_FOUND: "passenger ride not found",
} as const;
export const createDriverService = (deps: DriverServiceDeps) => {
- const { driverRides, drivers, passengerRides, locations, users, reviews } =
- deps;
+ const { driverRides, drivers, passengerRides, users, reviews } = deps;
return {
getProfile: async (driverId: string) => {
@@ -97,12 +98,10 @@ export const createDriverService = (deps: DriverServiceDeps) => {
if (driver == null) {
return error(DriverServiceErrors.NOT_A_DRIVER);
}
-
- const namedLocation = await locations.findName(input.locations);
const newDriverRide = await driverRides.save({
driverId: input.driverId,
departAt: input.departAt,
- locations: namedLocation,
+ locations: input.locations,
});
return success(newDriverRide);
},
@@ -113,21 +112,20 @@ export const createDriverService = (deps: DriverServiceDeps) => {
return error(DriverServiceErrors.NOT_A_DRIVER);
}
- const driverRide = await passengerRides.findByDriverRideId(
- input.driverRideId,
- input.passengerId,
+ const passengerRide = await passengerRides.findById(
+ input.passengerRideId,
);
- if (driverRide == null) {
- return error(DriverServiceErrors.DRIVER_RIDE_NOT_FOUND);
+ if (passengerRide == null) {
+ return error(DriverServiceErrors.PASSANGER_RIDE_NOT_FOUND);
}
const newPassengerRide = await passengerRides.save({
- id: driverRide.id,
+ id: passengerRide.id,
status: input.status,
- locations: driverRide.locations,
- driverId: driverRide.driverId,
- passengerId: driverRide.passengerId,
- driverRideId: driverRide.driverRideId,
+ locations: passengerRide.locations,
+ driverId: passengerRide.driverId,
+ passengerId: passengerRide.passengerId,
+ driverRideId: passengerRide.driverRideId,
});
return success(newPassengerRide);
@@ -231,17 +229,26 @@ export const createDriverService = (deps: DriverServiceDeps) => {
return success(pendingPassengersInfo);
},
- finishRide: async (driverId: string, driverRideId: string) => {
+ finishRide: async (driverId: string, passengerRideId: string) => {
const driver = await drivers.findById(driverId);
if (driver == null) {
return error(DriverServiceErrors.NOT_A_DRIVER);
}
- const driverRide = await driverRides.findById(driverRideId);
+ const passengerRide = await passengerRides.findById(passengerRideId);
+ if (passengerRide == null) {
+ return error(DriverServiceErrors.PASSANGER_RIDE_NOT_FOUND);
+ }
+
+ const driverRide = await driverRides.findById(passengerRide.driverRideId);
if (driverRide == null) {
return error(DriverServiceErrors.DRIVER_RIDE_NOT_FOUND);
}
+ if (driverRide.driverId !== driverId) {
+ return error(DriverServiceErrors.NOT_OWNED_RIDE);
+ }
+
const finishedDriverRide = await driverRides.save({
id: driverRide.id,
departAt: driverRide.departAt,
diff --git a/packages/internal/src/core/service/passengerService.ts b/packages/internal/src/core/service/passengerService.ts
index 59e8cac..1735378 100644
--- a/packages/internal/src/core/service/passengerService.ts
+++ b/packages/internal/src/core/service/passengerService.ts
@@ -28,8 +28,8 @@ type EditProfileInput = {
type ApplyRideInput = {
driverRideId: string;
passengerId: string;
- source: Location;
- destination: Location;
+ sourceId: string;
+ destinationId: string;
};
type LeaveRideInput = {
@@ -121,8 +121,8 @@ export const createPassengerService = (deps: PassengerServiceDeps) => {
}
const namedLocations = await locations.findName([
- input.source,
- input.destination,
+ input.sourceId,
+ input.destinationId,
]);
try {
@@ -290,6 +290,8 @@ export const createPassengerService = (deps: PassengerServiceDeps) => {
searchNearbyRides: async (input: SearchNearbyRidesInput) => {
const rides = await driverRides.findByNearbyLocations(input);
+ console.log(rides);
+
const driverIds = rides.map((ride) => ride.driverId);
const passengerIds = rides.flatMap((ride) =>
ride.passengers.map((passenger) => passenger.id),
diff --git a/packages/internal/src/repositories/prismaDriverRideRepo.ts b/packages/internal/src/repositories/prismaDriverRideRepo.ts
index 7afb2a3..235858d 100644
--- a/packages/internal/src/repositories/prismaDriverRideRepo.ts
+++ b/packages/internal/src/repositories/prismaDriverRideRepo.ts
@@ -16,7 +16,7 @@ const locationToRangeFilter = (location: Location, range: number) => {
};
};
-const LONG_LAT_OFFSET = 0.005;
+const LONG_LAT_OFFSET = 0.01;
export const createPrismaDriverRideRepo = (
prisma: PrismaClient,
@@ -77,7 +77,10 @@ export const createPrismaDriverRideRepo = (
locations: {
createMany: {
data: input.locations.map((location, index) => ({
- ...location,
+ driverRideId: input.id!,
+ latitude: location.latitude,
+ longitude: location.longitude,
+ name: location.name,
serialNumber: index,
})),
},
diff --git a/packages/internal/src/repositories/prismaLocationRepo.ts b/packages/internal/src/repositories/prismaLocationRepo.ts
index 9bf6fe2..19a6182 100644
--- a/packages/internal/src/repositories/prismaLocationRepo.ts
+++ b/packages/internal/src/repositories/prismaLocationRepo.ts
@@ -11,25 +11,20 @@ export const createPrismaLocationRepo = (
const uniqueLocations = [...new Set(locations)];
const namedLocations = await prisma.location.findMany({
where: {
- AND: {
- latitude: {
- in: uniqueLocations.map((location) => location.latitude),
- },
- longitude: {
- in: uniqueLocations.map((location) => location.longitude),
- },
+ id: {
+ in: uniqueLocations,
},
},
});
const locationMap = namedLocations.reduce((acc, location) => {
- acc[`${location.latitude},${location.longitude}`] = location;
+ acc[location.id] = location;
return acc;
}, {} as Record);
- return locations.map((location) => {
- const key = `${location.latitude},${location.longitude}`;
- return locationMap[key] ?? { ...location, name: "unknown" };
+ return locations.map((locationId) => {
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ return locationMap[locationId]!;
});
},
diff --git a/packages/internal/src/repositories/prismaPassengerRideRepo.ts b/packages/internal/src/repositories/prismaPassengerRideRepo.ts
index d1278b0..a7ea195 100644
--- a/packages/internal/src/repositories/prismaPassengerRideRepo.ts
+++ b/packages/internal/src/repositories/prismaPassengerRideRepo.ts
@@ -158,5 +158,23 @@ export const createPrismaPassengerRideRepo = (
return rides;
},
+
+ findById: async (id) => {
+ const ride = await prisma.passengerRide.findUnique({
+ where: {
+ id,
+ },
+ include: {
+ driverRide: {
+ select: {
+ departAt: true,
+ },
+ },
+ locations: true,
+ },
+ });
+
+ return ride;
+ },
};
};