Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion backend/market/models.py
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very very nit, but this change is out of scope with the PR since this touches backend. Totally fine for now, but generally you want the scope of a PR to focus on a single thing

Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import hashlib
import hmac
import math

from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator
Expand Down Expand Up @@ -146,7 +148,11 @@ def _calculate_approximate_location(self, latitude, longitude):

lat_str = f"{float(latitude):.9f}"
lon_str = f"{float(longitude):.9f}"
seed = hashlib.md5(f"{lat_str}{lon_str}".encode()).hexdigest()
seed = hmac.new(
settings.SECRET_KEY.encode(),
f"{lat_str}{lon_str}".encode(),
hashlib.sha256,
).hexdigest()

offset_factor = int(seed[:8], 16) / 0xFFFFFFFF

Expand Down
21 changes: 15 additions & 6 deletions backend/market/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,17 +104,26 @@ class SubletDataSerializer(ModelSerializer):

class Meta:
model = Sublet
fields = ["street_address", "beds", "baths", "start_date", "end_date",
"latitude", "longitude"]
fields = [
"street_address",
"beds",
"baths",
"start_date",
"end_date",
"latitude",
"longitude",
]

def get_latitude(self, obj):
if obj.approximate_location is not None:
return float(obj.approximate_location[0])
approx_lat, _ = obj.approximate_location
if approx_lat is not None:
return float(approx_lat)
return None

def get_longitude(self, obj):
if obj.approximate_location is not None:
return float(obj.approximate_location[1])
_, approx_lon = obj.approximate_location
if approx_lon is not None:
return float(approx_lon)
return None

# Unified serializer for all listing types (Items and Sublets); used for CRUD operations
Expand Down
20 changes: 20 additions & 0 deletions frontend/components/listings/detail/ListingDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ListingImageGallery } from "@/components/listings/detail/ListingImageGa
import { ListingInfo } from "@/components/listings/detail/ListingInfo";
import { UserCard } from "@/components/listings/detail/UserCard";
import { BackButton } from "@/components/listings/detail/BackButton";
import { SubletMap } from "@/components/listings/detail/SubletMap";

interface Props {
listing: Item | Sublet;
Expand Down Expand Up @@ -55,6 +56,11 @@ export const ListingDetail = ({ listing, initialIsFavorited }: Props) => {
toggleFavoriteMutation.mutate(!isFavorited);
};

const subletCoords =
listingType === "sublet" ? listing.additional_data : null;
const hasLocation =
subletCoords?.latitude != null && subletCoords?.longitude != null;

return (
<div className="mx-auto flex w-full max-w-[96rem] flex-col p-8 px-4 sm:px-12">
<div className="mb-4 flex items-center justify-between">
Expand Down Expand Up @@ -83,6 +89,20 @@ export const ListingDetail = ({ listing, initialIsFavorited }: Props) => {
{...listing.additional_data}
/>
<UserCard user={listing.seller} label={listingOwnerLabel} />
{hasLocation && (
<div className="space-y-3">
<div>
<h2 className="text-lg font-semibold">{"Where you'll be living"}</h2>
<p className="text-sm text-gray-500">
Approximate location shown. The exact location will be shared once you connect with the owner.
</p>
</div>
<SubletMap
latitude={subletCoords.latitude!}
longitude={subletCoords.longitude!}
/>
</div>
)}
<ListingActions
listingId={listing.id}
listingPrice={listing.price}
Expand Down
18 changes: 18 additions & 0 deletions frontend/components/listings/detail/SubletMap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"use client";

import dynamic from "next/dynamic";

interface Props {
latitude: number;
longitude: number;
}

const LazyMap = dynamic(
() =>
import("@/components/listings/detail/SubletMapContent").then((m) => m.SubletMapContent),
{ ssr: false },
);

export const SubletMap = ({ latitude, longitude }: Props) => {
return <LazyMap latitude={latitude} longitude={longitude} />;
};
37 changes: 37 additions & 0 deletions frontend/components/listings/detail/SubletMapContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"use client";

import "leaflet/dist/leaflet.css";
import { MapContainer, TileLayer, Circle } from "react-leaflet";

interface Props {
latitude: number;
longitude: number;
}

const CIRCLE_RADIUS_METERS = 200;

export const SubletMapContent = ({ latitude, longitude }: Props) => {
return (
<MapContainer
center={[latitude, longitude]}
zoom={15}
scrollWheelZoom={false}
className="z-0 h-72 w-full rounded-lg sm:h-80"
>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Circle
center={[latitude, longitude]}
radius={CIRCLE_RADIUS_METERS}
pathOptions={{
color: "#3b82f6",
fillColor: "#3b82f6",
fillOpacity: 0.2,
weight: 2,
}}
/>
</MapContainer>
);
};
1 change: 1 addition & 0 deletions frontend/lib/constants.ts
Comment thread
alisx255 marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const BASE_URL =

export const API_BASE_URL =
process.env.NODE_ENV === "production" ? "REPLACE WITH PROD API URL" : "http://backend:8000"; // can't be localhost because server fetch happens in container


export const PLATFORM_URL = process.env.PLATFORM_URL;
export const CLIENT_ID = process.env.CLIENT_ID;
Expand Down
4 changes: 2 additions & 2 deletions frontend/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ export type ItemAdditionalData = {

export type SubletAdditionalData = {
street_address: string;
latitude: number;
longitude: number;
beds: number;
baths: number;
start_date: string;
end_date: string;
latitude: number;
longitude: number;
};

// ------------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"jose": "^3.20.4",
"leaflet": "^1.9.4",
"lucide-react": "^0.546.0",
"next": "15.5.4",
"next-themes": "^0.4.6",
Expand All @@ -36,13 +37,15 @@
"react-dom": "19.1.0",
"react-hook-form": "^7.69.0",
"react-intersection-observer": "^9.16.0",
"react-leaflet": "^5.0.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.3.1",
"zod": "^4.2.1"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/leaflet": "^1.9.21",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
Expand Down
53 changes: 53 additions & 0 deletions frontend/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading