Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into erik/add-at-fair-page…
Browse files Browse the repository at this point in the history
…. small change to features.ts and at the fair link in navbar
  • Loading branch information
SortHvit committed Nov 9, 2024
2 parents d498cf8 + 06d657d commit 9e9ec59
Show file tree
Hide file tree
Showing 35 changed files with 4,665 additions and 218 deletions.
Binary file added public/map_icons/disability.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/map_icons/door.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/map_icons/exit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/map_icons/stair.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/map_icons/wc.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion src/app/student/_components/MultiSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import { ScrollArea } from "@/components/ui/scroll-area"
import { ChevronDown, X } from "lucide-react"
import { useRef, useState } from "react"

// TODO:
// - keyboard navigation?

export default function MultiSelect({
filter,
onChange
Expand Down Expand Up @@ -108,7 +111,7 @@ export default function MultiSelect({
role="option"
aria-selected={isSelected(item)}
key={item.id}
className="flex min-w-32 cursor-default items-center gap-2 p-2 pl-3 hover:bg-emerald-950 hover:text-melon-700"
className="flex min-w-32 cursor-default items-center gap-2 p-2 pl-3 text-left hover:bg-emerald-950 hover:text-melon-700"
onClick={() => onSelectionChange(item)}>
<Checkbox checked={isSelected(item)} tabIndex={-1} />
<span>{item.name}</span>
Expand Down
22 changes: 13 additions & 9 deletions src/app/student/events/_components/EventDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export default function EventDetails({
event: Event
className?: string
}) {
const today = Date.now() / 1000
return (
<div className={cn("mx-auto max-w-[600px] lg:max-w-[1000px]", className)}>
<Page.Header>{event.name}</Page.Header>
Expand Down Expand Up @@ -68,12 +69,10 @@ export default function EventDetails({
label="Time"
value={`${formatTimestampAsTime(event.event_start)} - ${formatTimestampAsTime(event.event_end)}`}
icon={<Clock size={16} />}></InfoBoxItem>

{/* Separator */}
{(event.food || event.fee) && (
<div className="h-[1px] w-full bg-stone-400"></div>
)}

{/* Bottom row */}
<InfoBoxItem
label="Food"
Expand All @@ -83,25 +82,30 @@ export default function EventDetails({
label="Fee"
value={`${event.fee} kr`}
icon={<Coins size={16} />}></InfoBoxItem>

{event.open_for_signup_student && event.registration_end && (
<p className="-mb-1 mt-3 text-xs text-stone-400">
Registration closes{" "}
{formatTimestampAsDate(event.registration_end)}
</p>
)}

{/* Signup */}
{event.open_for_signup_student ? (
{event.open_for_signup_student &&
today < (event.registration_end ?? event.event_start) ? (
<Link href={event.signup_link ?? ""}>
<Button className="w-full">Sign Up</Button>
</Link>
) : (
<Button disabled>
Registration closed{" "}
{event.registration_end
? formatTimestampAsDate(event.registration_end)
: ""}
{today < (event.registration_end ?? event.event_start) ? (
<> Signup opening soon ! </>
) : (
<>
Registration closed
{event.registration_end
? formatTimestampAsDate(event.registration_end)
: ""}
</>
)}
</Button>
)}
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/app/student/events/_components/EventItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function EventItem({ event }: { event: Event }) {
<Image
width={200}
height={200}
className="h-full max-h-48 w-full rounded-t-lg object-cover sm:h-48 sm:w-48 sm:rounded-l-lg sm:rounded-tr-none "
className="h-full max-h-48 w-full rounded-t-lg object-contain sm:h-48 sm:w-48 sm:rounded-l-lg sm:rounded-tr-none "
src={image_url}
alt=""
/>
Expand Down
2 changes: 1 addition & 1 deletion src/app/student/map/_components/LocationSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default function SelectLocation({
setActiveBoothId: (id: number | null) => void
}) {
return (
<div className="absolute top-2 justify-self-center rounded-full sm:right-2">
<div className="absolute right-2 top-2 justify-self-center rounded-full">
<Select
value={locationId}
onValueChange={(id: LocationId) => {
Expand Down
53 changes: 47 additions & 6 deletions src/app/student/map/_components/MainView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@

import LocationSelect from "@/app/student/map/_components/LocationSelect"
import { MapComponent } from "@/app/student/map/_components/MapComponent"
import { QuestionnaireForm } from "@/app/student/map/_components/QuestionnaireForm"
import Sidebar from "@/app/student/map/_components/Sidebar"
import EditorMapComponent from "@/app/student/map/editor/EditorMapComponent"
import { Exhibitor } from "@/components/shared/hooks/api/useExhibitors"
import { useScreenSize } from "@/components/shared/hooks/useScreenSize"
import { useSurveyData } from "@/components/shared/hooks/useSurveyData"
import Modal from "@/components/shared/Modal"
import { Button } from "@/components/ui/button"
import { useSearchParams } from "next/navigation"
import { useState } from "react"
import { Filter } from "lucide-react"
import { useRouter, useSearchParams } from "next/navigation"
import { useEffect, useState } from "react"
import { Booth, BoothID, BoothMap } from "../lib/booths"
import {
defaultLocation,
Expand All @@ -25,12 +30,16 @@ export default function MainView({
boothsById: BoothMap
exhibitorsById: Map<number, Exhibitor>
}) {
// url: /student/map?floor=[nymble/1|nymble/2|nymble/3|library]&lat=[number]&lng=[number]&zoom=[number]
// if floor is not provided or is invalid, default to nymble/1
// url: /student/map?floor=[nymble/2|nymble/3|library]&lat=[number]&lng=[number]&zoom=[number]
// if floor is not provided or is invalid, default to nymble/2
// if lat, lng or zoom is not provided, default to location center

const searchParams = useSearchParams()
const { width } = useScreenSize()
const router = useRouter()
const { surveyData, isSurveyDataLoaded } = useSurveyData()

const floorUrlString = searchParams.get("floor") ?? "nymble/1"
const floorUrlString = searchParams.get("floor") ?? "nymble/2"
const [locationId, setLocationId] = useState<LocationId>(
validLocationId(floorUrlString) ? floorUrlString : defaultLocation.id
)
Expand All @@ -51,9 +60,27 @@ export default function MainView({
const [filteredBooths, setFilteredBooths] = useState<Booth[]>(
Array.from(boothsById.values())
)

const [openSurvey, setOpenSurvey] = useState(false)
const [editorMode, setEditorMode] = useState(false)

useEffect(() => {
if (isSurveyDataLoaded) setOpenSurvey(!surveyData)
}, [surveyData, isSurveyDataLoaded])

useEffect(() => {
// A new survey page for filter when using mobile
if (openSurvey && width && width < 768) {
router.push("/student/map/survey")
}
}, [width])

const handleClickFilter = () => {
setOpenSurvey(prev => !prev)
if (width && width < 768) {
router.push("/student/map/survey")
}
}

return (
<div className="relative flex h-full w-full">
{!editorMode ? (
Expand Down Expand Up @@ -96,6 +123,20 @@ export default function MainView({
</Button>
)}

{/* Questions Modal for filter when using PC*/}
<Modal
open={openSurvey}
setOpen={setOpenSurvey}
className="max-w-[780px] bg-gradient-to-br from-emerald-950 via-stone-900 to-stone-900 p-0">
<QuestionnaireForm onClose={() => setOpenSurvey(false)} />
</Modal>

<Button
className="absolute top-2 ml-2 justify-self-center rounded-full sm:right-2 sm:top-20"
onClick={handleClickFilter}>
<Filter />
</Button>

<LocationSelect
locationId={locationId}
setLocationId={setLocationId}
Expand Down
116 changes: 70 additions & 46 deletions src/app/student/map/_components/MapComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { BoothPopup } from "@/app/student/map/_components/BoothPopup"
import {
BoothID,
geoJsonBoothDataByLocation,
geoJsonBuildingData
geoJsonBoothDataByLocation
} from "@/app/student/map/lib/booths"
import { Location } from "@/app/student/map/lib/locations"
import { useFeatureState } from "@/components/shared/hooks/useFeatureState"
import { useGeoJsonPlanData } from "@/components/shared/hooks/useGeoJsonPlanData"
import "maplibre-gl/dist/maplibre-gl.css"
import { MutableRefObject, useEffect, useMemo, useRef, useState } from "react"
import { useEffect, useMemo, useRef, useState } from "react"
import {
Layer,
MapLayerMouseEvent,
Expand All @@ -16,41 +17,17 @@ import {
} from "react-map-gl/maplibre"
import { BoothMap, GeoJsonBooth } from "../lib/booths"
import {
addMapIconAssets,
backgroundLayerStyle,
boothLayerStyle,
buildingLayerStyle
buildingLayerStyle,
lineLayerStyle,
roomLayerStyle,
routeLayerStyle,
symbolLayerStyle
} from "../lib/config"
import { BoothMarker } from "./BoothMarker"

// Keep mapbox feature state in sync with component state
// to allow for styling of the features
function useFeatureState(
mapRef: MutableRefObject<MapRef | null>,
boothIds: BoothID[],
stateKey: "active" | "hover" | "filtered"
) {
useEffect(() => {
const map = mapRef.current
if (map == null || boothIds.length === 0) return

for (const boothId of boothIds) {
map.setFeatureState(
{ source: "booths", id: boothId },
{ [stateKey]: true }
)
}

return () => {
for (const boothId of boothIds) {
map.setFeatureState(
{ source: "booths", id: boothId },
{ [stateKey]: false }
)
}
}
}, [boothIds, stateKey])
}

export function MapComponent({
boothsById,
location,
Expand Down Expand Up @@ -81,17 +58,28 @@ export function MapComponent({
center: [longitude, latitude],
zoom: zoom
})
})
setMarkerScale(0.6)
}, [location])

useEffect(() => {
// Load icon assets for points location
if (mapRef && !mapRef.current?.hasImage("exit-icon")) {
addMapIconAssets(mapRef)
}
}, [mapRef.current])

//Change layer style data source based on selected location
const [geoJsonPlanData, geoJsonPlanRoutesData, geoJsonPlanRoomsData] =
useGeoJsonPlanData(location)

// Fly to selected booth on change
useEffect(() => {
if (activeBoothId == null) return
const booth = boothsById.get(activeBoothId)
if (!booth) return

mapRef.current?.flyTo({
center: booth.center as [number, number],
zoom: 18.5,
zoom: 21,
speed: 0.8
})
}, [activeBoothId, boothsById])
Expand Down Expand Up @@ -123,10 +111,14 @@ export function MapComponent({
}
}

function onBoothMouseEnter(e: MapLayerMouseEvent) {
// Avoid delays in booth switching
function onBoothMouseMove(e: MapLayerMouseEvent) {
const feature = e.features?.[0] as GeoJsonBooth | undefined
if (feature) {
setHoveredBoothId(feature.properties.id)
const boothId = feature.properties.id
if (boothId !== hoveredBoothId) {
setHoveredBoothId(boothId)
}
}
}

Expand All @@ -140,7 +132,7 @@ export function MapComponent({
function onZoomChange() {
const zoom = mapRef.current?.getZoom()
if (zoom === undefined) return
const scale = Math.max(0.3, Math.min(2, 1 + (zoom - 18) * 0.3))
const scale = Math.max(0.3, Math.min(2, 1 + (zoom - 20) * 0.5))
setMarkerScale(scale)
}

Expand All @@ -149,21 +141,38 @@ export function MapComponent({
<MapboxMap
ref={mapRef}
onClick={onMapClick}
onMouseEnter={onBoothMouseEnter}
onMouseMove={onBoothMouseMove}
onMouseLeave={onBoothMouseLeave}
onZoom={onZoomChange}
interactiveLayerIds={["booths"]}
initialViewState={initialView}
cursor={"auto"}
minZoom={16}
maxZoom={20}
minZoom={17}
maxZoom={22}
maxBounds={[
[18.063, 59.345],
[18.079, 59.35]
]}
mapStyle="https://api.maptiler.com/maps/977e9770-60b4-4b8a-94e9-a9fa8db4c68d/style.json?key=57xj41WPFBbOEWiVSSwL">
<Layer {...backgroundLayerStyle}></Layer>

{/** Order sensitive! */}
<Source
id="buildings"
type="geojson"
promoteId={"id"}
data={geoJsonPlanData}>
<Layer {...buildingLayerStyle}></Layer>
</Source>

<Source
id="rooms"
type="geojson"
promoteId={"id"}
data={geoJsonPlanRoomsData}>
<Layer {...roomLayerStyle}></Layer>
</Source>

<Source
id="booths"
type="geojson"
Expand All @@ -173,15 +182,30 @@ export function MapComponent({
</Source>

<Source
id="buildings"
id="nymble-plan-style"
type="geojson"
promoteId={"id"}
data={geoJsonBuildingData}>
<Layer {...buildingLayerStyle}></Layer>
data={geoJsonPlanData}>
<Layer {...lineLayerStyle}></Layer>
</Source>

{markers}
<Source
id="nymble-plan-routes"
type="geojson"
promoteId={"id"}
data={geoJsonPlanRoutesData}>
<Layer {...routeLayerStyle}></Layer>
</Source>

<Source
id="nymble-plan-points"
type="geojson"
promoteId={"id"}
data={geoJsonPlanData}>
<Layer {...symbolLayerStyle}></Layer>
</Source>

{markers}
{activeBooth && <BoothPopup key={activeBooth.id} booth={activeBooth} />}
</MapboxMap>
</div>
Expand Down
Loading

0 comments on commit 9e9ec59

Please sign in to comment.