-
Notifications
You must be signed in to change notification settings - Fork 1
/
page.tsx
152 lines (138 loc) · 7.41 KB
/
page.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import { db } from "@/lib/db";
import { StationCard } from "./_components/station-card";
import { cn, haversineDistance, executeQueryWithSentry } from "@/lib/utils";
import Loading from "./loading";
import Link from "next/link";
import { buttonVariants } from "@/components/ui/button";
import { Navigation } from "lucide-react";
import Balance from "react-wrap-balancer"
import { stations as stationsSchema } from "@/lib/db/schema";
import { sql, lte } from 'drizzle-orm'
export default async function SearchPage({
searchParams
}: {
searchParams?: { [key: string]: string | string[] | undefined };
}) {
const { lat, long, fuelType, radius, tankSize, sortBy } = searchParams as { [key: string]: string };
let parsedLat: number | null = null;
let parsedLong: number | null = null;
const parsedRadius = parseFloat(radius ?? "0");
const parsedTankSize = parseFloat(tankSize ?? "0");
// Try to parse latitude and longitude
if (lat && long) {
parsedLat = parseFloat(lat);
parsedLong = parseFloat(long);
// Check if parsing was successful
if (isNaN(parsedLat) || isNaN(parsedLong)) {
parsedLat = null;
parsedLong = null;
}
}
if (parsedLat !== null && parsedLong !== null && fuelType && parsedRadius && parsedTankSize && sortBy) {
const findStationsQuery = db.query.stations.findMany({
extras: {
distance: sql<number>`
6371 * acos(
cos(radians(${parsedLat}))
* cos(radians(${stationsSchema.latitude}))
* cos(radians(${stationsSchema.longitude}) - radians(${parsedLong}))
+ sin(radians(${parsedLat}))
* sin(radians(${stationsSchema.latitude}))
)
`.as('distance')
},
where: (lte(sql`distance`, parsedRadius)),
with: {
prices: {
// Trying to reduce the size returned as more prices are retrieved by ordering
// by ID assuming the later prices are more recent and then limiting to 15.
orderBy: (prices, { desc }) => [desc(prices.id)],
limit: 10
}
},
});
const stations = await executeQueryWithSentry(findStationsQuery);
if (stations.length === 0) {
// TODO: Update to use something like https://github.com/rapideditor/country-coder when supporting more states.
// Rough bounds for NSW.
if (parsedLat < -37.505 || parsedLat > -28.157 || parsedLong < 140.999 || parsedLong > 153.552) {
return (
<div className="flex flex-col items-center justify-center gap-y-4 h-full">
<div className="flex flex-col gap-y-2 text-center">
<h2 className="text-2xl font-bold">No fuel stations found</h2>
<Balance className="text-gray-500 mx-auto flex max-w-[980px] flex-col items-center">You are likely outside of New South Wales, Australia, which is currently the only area supported. You can click the button below to check out fuel prices in Sydney to see what it looks like!</Balance>
<Link href="/search?lat=-33.8930404&long=151.2765367" className={cn(buttonVariants())}>
<Navigation className="mr-1 h-4 w-4" /> Check out fuel prices in Sydney
</Link>
</div>
</div>
)
}
// TODO: Use more descriptive return for not found at different stages.
return (
<div className="flex flex-col items-center justify-center gap-y-4 h-full">
<div className="flex flex-col gap-y-2 text-center">
<h2 className="text-2xl font-bold">No fuel stations found</h2>
<Balance className="text-gray-500 mx-auto flex max-w-[980px] flex-col items-center">Try increasing the search radius. You can click the button below to check out fuel prices in Sydney to see what it looks like!</Balance>
<Link href="/search?lat=-33.8930404&long=151.2765367" className={cn(buttonVariants())}>
<Navigation className="mr-1 h-4 w-4" /> Check out fuel prices in Sydney
</Link>
</div>
</div>
)
}
const stationsWithDistance = stations.map(station => {
const uniquePrices = station.prices.reduce((acc: { [key: string]: any }, price) => {
if (!acc[price.fuelType] || new Date(price.lastUpdatedUTC) > new Date(acc[price.fuelType].lastUpdatedUTC)) {
acc[price.fuelType] = price;
}
return acc;
}, {});
return {
...station,
distance: stations.find(s => s.id === station.id)?.distance ?? 0,
prices: Object.values(uniquePrices)
};
})
.filter(station => station.prices.some(price => price.fuelType === fuelType))
.sort((a, b) => {
if (sortBy === "distance") {
// sort by distance
return a.distance - b.distance;
} else {
// sort by price or if sortBy is neither "price" nor "distance"
const priceA = a.prices.find(price => price.fuelType === fuelType)?.price ?? Infinity;
const priceB = b.prices.find(price => price.fuelType === fuelType)?.price ?? Infinity;
return priceA - priceB;
}
})
.slice(0, 20);
if (stationsWithDistance.length === 0) {
return (
<div className="flex flex-col items-center justify-center gap-y-4 h-full">
<div className="flex flex-col gap-y-2 text-center">
<h2 className="text-2xl font-bold">No fuel stations found</h2>
<Balance className="text-gray-500 mx-auto flex max-w-[980px] flex-col items-center">Try increasing the search radius. You can click the button below to check out fuel prices in Sydney to see what it looks like!</Balance>
<Link href="/search?lat=-33.8930404&long=151.2765367" className={cn(buttonVariants())}>
<Navigation className="mr-1 h-4 w-4" /> Check out fuel prices in Sydney
</Link>
</div>
</div>
)
}
const lowestPrice = stationsWithDistance.reduce((lowest, station) => {
let stationPrice = station.prices.find(price => price.fuelType === fuelType)!.price;
if (stationPrice < lowest) {
return stationPrice;
} else {
return lowest;
}
}, Infinity);
return (
<div className="grid gap-2 md:gap-4 grid-cols-1 md:grid-cols-3 lg:grid-cols-4">
{stationsWithDistance?.map(station => (<StationCard key={station.id} station={station} primaryFuelType={fuelType} lowestPrice={lowestPrice} tankSize={parsedTankSize} />))}
</div>
)
}
return <Loading />
}