Skip to content

Commit 6e0252a

Browse files
author
Anthony Li
committed
Add Leaflet map to sublet page
1 parent 244d1aa commit 6e0252a

8 files changed

Lines changed: 139 additions & 1 deletion

File tree

docker-compose.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@ services:
6868
- penn-marketplace-frontend-pnpm-store:/app/.pnpm-store
6969
environment:
7070
- NODE_ENV=development
71+
# Browser on your machine → API on published port
7172
- NEXT_PUBLIC_API_URL=http://localhost:8000
73+
# Next server inside this container → Django service name
74+
- API_BASE_URL=http://backend:8000
7275

7376
volumes:
7477
penn-marketplace-postgres-data:

frontend/components/listings/detail/ListingDetail.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { ListingImageGallery } from "@/components/listings/detail/ListingImageGa
55
import { ListingInfo } from "@/components/listings/detail/ListingInfo";
66
import { UserCard } from "@/components/listings/detail/UserCard";
77
import { BackButton } from "@/components/listings/detail/BackButton";
8+
import { SubletMap } from "@/components/listings/detail/SubletMap";
89

910
interface Props {
1011
listing: Item | Sublet;
@@ -15,6 +16,11 @@ export const ListingDetail = ({ listing }: Props) => {
1516
const priceLabel = listingType === "sublet" ? "/mo" : undefined;
1617
const listingOwnerLabel = listingType === "item" ? "Seller" : "Owner";
1718

19+
const subletCoords =
20+
listingType === "sublet" ? listing.additional_data : null;
21+
const hasLocation =
22+
subletCoords?.latitude != null && subletCoords?.longitude != null;
23+
1824
return (
1925
<div className="mx-auto flex w-full max-w-[96rem] flex-col p-8 px-4 sm:px-12">
2026
<div className="mb-4 flex items-center justify-between">
@@ -41,6 +47,15 @@ export const ListingDetail = ({ listing }: Props) => {
4147
priceLabel={priceLabel}
4248
listingOwnerLabel={listingOwnerLabel}
4349
/>
50+
{hasLocation && (
51+
<div className="space-y-3">
52+
<h2 className="text-lg font-semibold">Where you'll be living</h2>
53+
<SubletMap
54+
latitude={subletCoords.latitude!}
55+
longitude={subletCoords.longitude!}
56+
/>
57+
</div>
58+
)}
4459
</div>
4560
</div>
4661
</div>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"use client";
2+
3+
import dynamic from "next/dynamic";
4+
5+
interface Props {
6+
latitude: number;
7+
longitude: number;
8+
}
9+
10+
const LazyMap = dynamic(
11+
() =>
12+
import("./SubletMapContent").then((m) => m.SubletMapContent),
13+
{ ssr: false },
14+
);
15+
16+
export const SubletMap = ({ latitude, longitude }: Props) => {
17+
return <LazyMap latitude={latitude} longitude={longitude} />;
18+
};
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"use client";
2+
3+
import "leaflet/dist/leaflet.css";
4+
import { MapContainer, TileLayer, Circle } from "react-leaflet";
5+
6+
interface Props {
7+
latitude: number;
8+
longitude: number;
9+
}
10+
11+
const CIRCLE_RADIUS_METERS = 200;
12+
13+
export const SubletMapContent = ({ latitude, longitude }: Props) => {
14+
return (
15+
<MapContainer
16+
center={[latitude, longitude]}
17+
zoom={15}
18+
scrollWheelZoom={false}
19+
className="z-0 h-72 w-full rounded-lg sm:h-80"
20+
>
21+
<TileLayer
22+
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
23+
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
24+
/>
25+
<Circle
26+
center={[latitude, longitude]}
27+
radius={CIRCLE_RADIUS_METERS}
28+
pathOptions={{
29+
color: "#3b82f6",
30+
fillColor: "#3b82f6",
31+
fillOpacity: 0.2,
32+
weight: 2,
33+
}}
34+
/>
35+
</MapContainer>
36+
);
37+
};

frontend/lib/constants.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,15 @@ import {
99
export const BASE_URL =
1010
process.env.NODE_ENV === "production" ? "REPLACE WITH PROD BASE URL" : "http://localhost:3000";
1111

12+
/**
13+
* Server-side fetches (RSC, Route Handlers, middleware).
14+
* - Local `pnpm dev`: default http://localhost:8000 (backend on host port).
15+
* - Docker frontend service: set API_BASE_URL=http://backend:8000 in compose.
16+
*/
1217
export const API_BASE_URL =
13-
process.env.NODE_ENV === "production" ? "REPLACE WITH PROD API URL" : "http://backend:8000"; // can't be localhost because server fetch happens in container
18+
process.env.NODE_ENV === "production"
19+
? process.env.NEXT_PUBLIC_API_URL ?? "REPLACE WITH PROD API URL"
20+
: (process.env.API_BASE_URL ?? "http://localhost:8000");
1421

1522
export const PLATFORM_URL = process.env.PLATFORM_URL;
1623
export const CLIENT_ID = process.env.CLIENT_ID;

frontend/lib/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ export type SubletAdditionalData = {
4444
baths: number;
4545
start_date: string;
4646
end_date: string;
47+
latitude: number | null;
48+
longitude: number | null;
4749
};
4850

4951
// ------------------------------------------------------------

frontend/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"clsx": "^2.1.1",
2929
"date-fns": "^4.1.0",
3030
"jose": "^3.20.4",
31+
"leaflet": "^1.9.4",
3132
"lucide-react": "^0.546.0",
3233
"next": "15.5.4",
3334
"next-themes": "^0.4.6",
@@ -36,13 +37,15 @@
3637
"react-dom": "19.1.0",
3738
"react-hook-form": "^7.69.0",
3839
"react-intersection-observer": "^9.16.0",
40+
"react-leaflet": "^5.0.0",
3941
"sonner": "^2.0.7",
4042
"tailwind-merge": "^3.3.1",
4143
"zod": "^4.2.1"
4244
},
4345
"devDependencies": {
4446
"@eslint/eslintrc": "^3",
4547
"@tailwindcss/postcss": "^4",
48+
"@types/leaflet": "^1.9.21",
4649
"@types/node": "^20",
4750
"@types/react": "^19",
4851
"@types/react-dom": "^19",

frontend/pnpm-lock.yaml

Lines changed: 53 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)