mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 10:42:08 +00:00
153 lines
4.2 KiB
TypeScript
153 lines
4.2 KiB
TypeScript
import { GetServerSideProps, NextPage } from 'next';
|
|
import { useMemo, useState } from 'react';
|
|
import Map, {
|
|
FullscreenControl,
|
|
Marker,
|
|
NavigationControl,
|
|
Popup,
|
|
ScaleControl,
|
|
} from 'react-map-gl';
|
|
import 'mapbox-gl/dist/mapbox-gl.css';
|
|
import DBClient from '@/prisma/DBClient';
|
|
|
|
import LocationMarker from '@/components/ui/LocationMarker';
|
|
import Link from 'next/link';
|
|
import Head from 'next/head';
|
|
import useGeolocation from '@/hooks/useGeolocation';
|
|
|
|
type MapStyles = Record<'light' | 'dark', `mapbox://styles/mapbox/${string}`>;
|
|
|
|
interface BreweryMapPageProps {
|
|
breweries: {
|
|
location: {
|
|
city: string;
|
|
stateOrProvince: string | null;
|
|
country: string | null;
|
|
coordinates: number[];
|
|
};
|
|
id: string;
|
|
name: string;
|
|
}[];
|
|
}
|
|
|
|
const BreweryMapPage: NextPage<BreweryMapPageProps> = ({ breweries }) => {
|
|
const windowIsDefined = typeof window !== 'undefined';
|
|
const themeIsDefined = windowIsDefined && !!window.localStorage.getItem('theme');
|
|
|
|
const [popupInfo, setPopupInfo] = useState<BreweryMapPageProps['breweries'][0] | null>(
|
|
null,
|
|
);
|
|
|
|
const theme = (
|
|
windowIsDefined && themeIsDefined ? window.localStorage.getItem('theme') : 'light'
|
|
) as 'light' | 'dark';
|
|
|
|
const mapStyles: MapStyles = {
|
|
light: 'mapbox://styles/mapbox/light-v10',
|
|
dark: 'mapbox://styles/mapbox/dark-v11',
|
|
};
|
|
|
|
const pins = useMemo(
|
|
() => (
|
|
<>
|
|
{breweries.map((brewery) => {
|
|
const [longitude, latitude] = brewery.location.coordinates;
|
|
return (
|
|
<Marker
|
|
latitude={latitude}
|
|
longitude={longitude}
|
|
key={brewery.id}
|
|
onClick={(e) => {
|
|
e.originalEvent.stopPropagation();
|
|
setPopupInfo(brewery);
|
|
}}
|
|
>
|
|
<LocationMarker size="md" color="blue" />
|
|
</Marker>
|
|
);
|
|
})}
|
|
</>
|
|
),
|
|
[breweries],
|
|
);
|
|
|
|
const { coords, error } = useGeolocation();
|
|
|
|
const userLocationPin = useMemo(
|
|
() =>
|
|
coords && !error ? (
|
|
<Marker latitude={coords.latitude} longitude={coords.longitude}>
|
|
<LocationMarker size="lg" color="red" />
|
|
</Marker>
|
|
) : null,
|
|
[coords, error],
|
|
);
|
|
|
|
return (
|
|
<>
|
|
<Head>
|
|
<title>Brewery Map | The Biergarten App</title>
|
|
<meta
|
|
name="description"
|
|
content="Find breweries near you and around the world."
|
|
/>
|
|
</Head>
|
|
<div className="h-full">
|
|
<Map
|
|
initialViewState={{ zoom: 2 }}
|
|
style={{ width: '100%', height: '100%' }}
|
|
mapStyle={mapStyles[theme]}
|
|
mapboxAccessToken={process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN}
|
|
scrollZoom
|
|
>
|
|
<FullscreenControl position="top-left" />
|
|
<NavigationControl position="top-left" />
|
|
<ScaleControl />
|
|
{pins}
|
|
{userLocationPin}
|
|
{popupInfo && (
|
|
<Popup
|
|
anchor="bottom"
|
|
longitude={popupInfo.location.coordinates[0]}
|
|
latitude={popupInfo.location.coordinates[1]}
|
|
onClose={() => setPopupInfo(null)}
|
|
>
|
|
<div className="flex flex-col text-black ">
|
|
<Link
|
|
className="link-hover link text-base font-bold"
|
|
href={`/breweries/${popupInfo.id}`}
|
|
>
|
|
{popupInfo.name}
|
|
</Link>
|
|
<p className="text-base">
|
|
{popupInfo.location.city}
|
|
{popupInfo.location.stateOrProvince
|
|
? `, ${popupInfo.location.stateOrProvince}`
|
|
: ''}
|
|
{popupInfo.location.country ? `, ${popupInfo.location.country}` : ''}
|
|
</p>
|
|
</div>
|
|
</Popup>
|
|
)}
|
|
</Map>
|
|
</div>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default BreweryMapPage;
|
|
|
|
export const getServerSideProps: GetServerSideProps<BreweryMapPageProps> = async () => {
|
|
const breweries = await DBClient.instance.breweryPost.findMany({
|
|
select: {
|
|
location: {
|
|
select: { coordinates: true, city: true, country: true, stateOrProvince: true },
|
|
},
|
|
id: true,
|
|
name: true,
|
|
},
|
|
});
|
|
|
|
return { props: { breweries } };
|
|
};
|