diff --git a/backend/src/main/kotlin/hu/bme/sch/cmsch/component/location/LocationService.kt b/backend/src/main/kotlin/hu/bme/sch/cmsch/component/location/LocationService.kt index db0b5645..27e22ab7 100644 --- a/backend/src/main/kotlin/hu/bme/sch/cmsch/component/location/LocationService.kt +++ b/backend/src/main/kotlin/hu/bme/sch/cmsch/component/location/LocationService.kt @@ -37,7 +37,7 @@ class LocationService( if (userEntity.role.value >= RoleType.STAFF.value) { tokenToLocationMapping[locationDto.token] = LocationEntity( - id = 0, + id = userEntity.id, userId = userEntity.id, userName = userEntity.fullName, alias = userEntity.alias, @@ -113,16 +113,31 @@ class LocationService( .forEach { token -> tokenToLocationMapping[token]?.let { val user = userRepository.findByCmschId(startupPropertyConfig.profileQrPrefix + token) - it.userId = user.get().id - it.userName = user.get().fullName - it.alias = user.get().alias - it.groupName = user.get().groupName + if (user.isPresent) { + val u = user.get() + it.id = u.id + it.userId = u.id + it.userName = u.fullName + it.alias = u.alias + it.groupName = u.groupName + } } } } fun findLocationsOfGroup(groupId: Int): List { - return tokenToLocationMapping.values.filter { it.id == groupId } + val locations = tokenToLocationMapping.values.toList() + if (locations.isEmpty()) return emptyList() + + val userIds = locations.map { it.userId } + val userIdsInGroup = transactionManager.transaction(readOnly = true) { + userRepository.findAllById(userIds) + .filter { it.group?.id == groupId } + .map { it.id } + .toSet() + } + + return locations.filter { it.userId in userIdsInGroup } } fun findLocationsOfGroupName(group: String): List { diff --git a/frontend/src/common-components/map/MapContent.tsx b/frontend/src/common-components/map/MapContent.tsx index 24227994..040210a3 100644 --- a/frontend/src/common-components/map/MapContent.tsx +++ b/frontend/src/common-components/map/MapContent.tsx @@ -2,16 +2,18 @@ import { useToast } from '@/hooks/use-toast' import { l } from '@/util/language' import { type MapDataItemView, MapMarkerShape } from '@/util/views/map.view' import { Map, Marker, ZoomControl } from 'pigeon-maps' -import { useEffect } from 'react' +import { useEffect, useMemo } from 'react' import { useGeolocated } from 'react-geolocated' import { MapMarker } from './MapMarker' interface MapContentProps { showUserLocation: boolean mapData: MapDataItemView[] + className?: string + height?: number } -export function MapContent({ showUserLocation, mapData }: MapContentProps) { +export function MapContent({ showUserLocation, mapData, className, height = 400 }: MapContentProps) { const { toast } = useToast() const { coords, getPosition, isGeolocationAvailable, isGeolocationEnabled } = useGeolocated({ @@ -22,7 +24,16 @@ export function MapContent({ showUserLocation, mapData }: MapContentProps) { suppressLocationOnMount: true, watchPosition: showUserLocation }) - const center: [number, number] = coords ? [coords.latitude, coords.longitude] : [47.47303, 19.0531] + + const markersCenter = useMemo(() => { + if (!mapData || mapData.length === 0) return null + const latSum = mapData.reduce((acc, curr) => acc + curr.latitude, 0) + const lonSum = mapData.reduce((acc, curr) => acc + curr.longitude, 0) + return [latSum / mapData.length, lonSum / mapData.length] as [number, number] + }, [mapData]) + + const fallbackCenter: [number, number] = markersCenter ?? [47.47303, 19.0531] + const center: [number, number] = showUserLocation && coords ? [coords.latitude, coords.longitude] : fallbackCenter useEffect(() => { if (showUserLocation) getPosition() @@ -35,19 +46,27 @@ export function MapContent({ showUserLocation, mapData }: MapContentProps) { }, [showUserLocation, isGeolocationAvailable, isGeolocationEnabled, toast]) return ( - - - {mapData.map((mapDataItem) => ( - - - - ))} - {coords && ( - - - - )} - +
+ + + {mapData.map((mapDataItem) => ( + + + + ))} + {showUserLocation && coords && ( + + + + )} + +
) } diff --git a/frontend/src/pages/map/map.page.tsx b/frontend/src/pages/map/map.page.tsx index a5349963..28af9c01 100644 --- a/frontend/src/pages/map/map.page.tsx +++ b/frontend/src/pages/map/map.page.tsx @@ -1,17 +1,33 @@ import { useConfigContext } from '@/api/contexts/config/ConfigContext' import { useLocationQuery } from '@/api/hooks/location/useLocationQuery' +import { KirDevLogo } from '@/assets/kir-dev-logo' import { CmschPage } from '@/common-components/layout/CmschPage' import { MapContent } from '@/common-components/map/MapContent' import Markdown from '@/common-components/Markdown' import { Checkbox } from '@/components/ui/checkbox' import { Label } from '@/components/ui/label' +import { cn } from '@/lib/utils' import { l } from '@/util/language' -import { useState } from 'react' +import { X } from 'lucide-react' +import { useEffect, useState } from 'react' export default function MapPage() { const [showUserLocation, setShowUserLocation] = useState(false) + const [fullScreen, setFullScreen] = useState(false) + const [fullscreenHeight, setFullscreenHeight] = useState(() => (typeof window !== 'undefined' ? window.innerHeight : 800)) const locationQuery = useLocationQuery() - const component = useConfigContext()?.components?.location + const config = useConfigContext() + const component = config?.components?.location + const devWebsiteUrl = config?.components?.footer?.devWebsiteUrl || 'https://kir-dev.hu/project/cmsch' + + useEffect(() => { + if (typeof window === 'undefined') return + const handleResize = () => { + setFullscreenHeight(window.innerHeight) + } + window.addEventListener('resize', handleResize) + return () => window.removeEventListener('resize', handleResize) + }, []) return ( @@ -21,13 +37,49 @@ export default function MapPage() { )} -
- setShowUserLocation(!!checked)} /> - +
+
+ setShowUserLocation(!!checked)} /> + +
+
+ setFullScreen(!!checked)} /> + +
+
+
+ {fullScreen && ( + <> + +
+ + + +
+ + )} +
-

{l('location-description')}

{l('location-privacy')}

{component?.bottomMessage && (