feat(SW-325): added additional poi groups

This commit is contained in:
Erik Tiekstra
2024-09-26 15:20:22 +02:00
parent 947ceb1736
commit fe607f640c
17 changed files with 254 additions and 123 deletions

View File

@@ -80,7 +80,8 @@ export default function MapContent({
className={`${styles.poi} ${activePoi === poi.name ? styles.active : ""}`}
>
<PoiMarker
category={poi.category}
group={poi.group}
categoryName={poi.categoryName}
className={styles.poiMarker}
size={activePoi === poi.name ? 20 : 16}
/>

View File

@@ -20,12 +20,10 @@ export default function Sidebar({
}: SidebarProps) {
const intl = useIntl()
const [isFullScreenSidebar, setIsFullScreenSidebar] = useState(false)
const poiCategories = new Set(
pointsOfInterest.map(({ category }) => category)
)
const poisInCategories = Array.from(poiCategories).map((category) => ({
category,
pois: pointsOfInterest.filter((poi) => poi.category === category),
const poiGroups = new Set(pointsOfInterest.map(({ group }) => group))
const poisInGroups = Array.from(poiGroups).map((group) => ({
group,
pois: pointsOfInterest.filter((poi) => poi.group === group),
}))
function toggleFullScreenSidebar() {
@@ -60,9 +58,9 @@ export default function Sidebar({
)}
</Title>
{poisInCategories.map(({ category, pois }) =>
{poisInGroups.map(({ group, pois }) =>
pois.length ? (
<div key={category} className={styles.poiGroup}>
<div key={group} className={styles.poiGroup}>
<Body
color="black"
textTransform="bold"
@@ -70,8 +68,8 @@ export default function Sidebar({
asChild
>
<h3>
<PoiMarker category={category} />
{intl.formatMessage({ id: category })}
<PoiMarker group={group} />
{intl.formatMessage({ id: group })}
</h3>
</Body>
<ul className={styles.poiList}>

View File

@@ -39,7 +39,12 @@ export default function MapCard({ hotelName, pois }: MapCardProps) {
<ul className={styles.poiList}>
{pois.map((poi) => (
<li key={poi.name} className={styles.poiItem}>
<PoiMarker category={poi.category} skipBackground size={20} />
<PoiMarker
group={poi.group}
categoryName={poi.categoryName}
skipBackground
size={20}
/>
<Body color="black">{poi.name}</Body>
<Caption>{poi.distance} km</Caption>
</li>

View File

@@ -0,0 +1,40 @@
import { iconVariants } from "./variants"
import type { IconProps } from "@/types/components/icon"
export default function AirplaneIcon({
className,
color,
...props
}: IconProps) {
const classNames = iconVariants({ className, color })
return (
<svg
className={classNames}
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
{...props}
>
<mask
id="mask0_4597_1552"
style={{ maskType: "alpha" }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="24"
height="24"
>
<rect width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_4597_1552)">
<path
d="M9.9251 21.125L7.4501 16.525L2.8501 14.05L4.6251 12.3L8.2501 12.925L10.8001 10.375L2.8751 7L4.9751 4.85L14.6001 6.55L17.7001 3.45C18.0834 3.06667 18.5584 2.875 19.1251 2.875C19.6918 2.875 20.1668 3.06667 20.5501 3.45C20.9334 3.83333 21.1251 4.30417 21.1251 4.8625C21.1251 5.42083 20.9334 5.89167 20.5501 6.275L17.4251 9.4L19.1251 19L17.0001 21.125L13.6001 13.2L11.0501 15.75L11.7001 19.35L9.9251 21.125Z"
fill="#26201E"
/>
</g>
</svg>
)
}

View File

@@ -0,0 +1,40 @@
import { iconVariants } from "./variants"
import type { IconProps } from "@/types/components/icon"
export default function BusinessIcon({
className,
color,
...props
}: IconProps) {
const classNames = iconVariants({ className, color })
return (
<svg
className={classNames}
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
{...props}
>
<mask
id="mask0_69_3397"
style={{ maskType: "alpha" }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="24"
height="24"
>
<rect width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_69_3397)">
<path
d="M4.125 20.75C3.60937 20.75 3.16796 20.5664 2.80077 20.1992C2.43359 19.832 2.25 19.3906 2.25 18.875V7.875C2.25 7.35937 2.43359 6.91796 2.80077 6.55078C3.16796 6.18359 3.60937 6 4.125 6H8.15V4.1239C8.15 3.60797 8.33359 3.16667 8.70078 2.8C9.06796 2.43333 9.50937 2.25 10.025 2.25H13.975C14.4906 2.25 14.932 2.43359 15.2992 2.80078C15.6664 3.16796 15.85 3.60937 15.85 4.125V6H19.875C20.3906 6 20.832 6.18359 21.1992 6.55078C21.5664 6.91796 21.75 7.35937 21.75 7.875V18.875C21.75 19.3906 21.5664 19.832 21.1992 20.1992C20.832 20.5664 20.3906 20.75 19.875 20.75H4.125ZM10.025 6H13.975V4.125H10.025V6ZM19.875 14.925H14.8625V15.9125C14.8625 16.1708 14.7708 16.3917 14.5875 16.575C14.4042 16.7583 14.1833 16.85 13.925 16.85H10.075C9.81667 16.85 9.59583 16.7583 9.4125 16.575C9.22917 16.3917 9.1375 16.1708 9.1375 15.9125V14.925H4.125V18.875H19.875V14.925ZM11.0125 14.975H12.9875V13H11.0125V14.975ZM4.125 13.05H9.1375V12.0625C9.1375 11.8042 9.22917 11.5833 9.4125 11.4C9.59583 11.2167 9.81667 11.125 10.075 11.125H13.925C14.1833 11.125 14.4042 11.2167 14.5875 11.4C14.7708 11.5833 14.8625 11.8042 14.8625 12.0625V13.05H19.875V7.875H4.125V13.05Z"
fill="#26201E"
/>
</g>
</svg>
)
}

View File

@@ -8,16 +8,29 @@ export default function CameraIcon({ className, color, ...props }: IconProps) {
<svg
className={classNames}
xmlns="http://www.w3.org/2000/svg"
width="20"
height="18"
viewBox="0 0 20 18"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
{...props}
>
<path
d="M10 14.375C11.225 14.375 12.2625 13.95 13.1125 13.1C13.9625 12.25 14.3875 11.2125 14.3875 9.9875C14.3875 8.7625 13.9625 7.725 13.1125 6.875C12.2625 6.025 11.225 5.6 10 5.6C8.775 5.6 7.7375 6.025 6.8875 6.875C6.0375 7.725 5.6125 8.7625 5.6125 9.9875C5.6125 11.2125 6.0375 12.25 6.8875 13.1C7.7375 13.95 8.775 14.375 10 14.375ZM9.9949 12.5C9.28997 12.5 8.69583 12.2583 8.2125 11.775C7.72917 11.2917 7.4875 10.6975 7.4875 9.9926C7.4875 9.28767 7.72917 8.69183 8.2125 8.2051C8.69583 7.71837 9.28997 7.475 9.9949 7.475C10.6998 7.475 11.2957 7.71837 11.7824 8.2051C12.2691 8.69183 12.5125 9.28767 12.5125 9.9926C12.5125 10.6975 12.2691 11.2917 11.7824 11.775C11.2957 12.2583 10.6998 12.5 9.9949 12.5ZM2.125 17.75C1.60937 17.75 1.16796 17.5664 0.800775 17.1992C0.433592 16.832 0.25 16.3906 0.25 15.875V4.1125C0.25 3.59687 0.433592 3.15546 0.800775 2.78828C1.16796 2.42109 1.60937 2.2375 2.125 2.2375H5.275L6.5125 0.875C6.68652 0.6827 6.89613 0.530459 7.14133 0.418275C7.38651 0.306092 7.64357 0.25 7.9125 0.25H12.0875C12.3564 0.25 12.6135 0.306092 12.8587 0.418275C13.1039 0.530459 13.3135 0.6827 13.4875 0.875L14.725 2.2375H17.875C18.3906 2.2375 18.832 2.42109 19.1992 2.78828C19.5664 3.15546 19.75 3.59687 19.75 4.1125V15.875C19.75 16.3906 19.5664 16.832 19.1992 17.1992C18.832 17.5664 18.3906 17.75 17.875 17.75H2.125ZM2.125 15.875H17.875V4.1125H13.8853L12.0875 2.125H7.9162L6.125 4.1125H2.125V15.875Z"
fill="#26201E"
/>
<mask
id="mask0_69_3288"
style={{ maskType: "alpha" }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="24"
height="24"
>
<rect width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_69_3288)">
<path
d="M12 17.375C13.225 17.375 14.2625 16.95 15.1125 16.1C15.9625 15.25 16.3875 14.2125 16.3875 12.9875C16.3875 11.7625 15.9625 10.725 15.1125 9.875C14.2625 9.025 13.225 8.6 12 8.6C10.775 8.6 9.7375 9.025 8.8875 9.875C8.0375 10.725 7.6125 11.7625 7.6125 12.9875C7.6125 14.2125 8.0375 15.25 8.8875 16.1C9.7375 16.95 10.775 17.375 12 17.375ZM11.9949 15.5C11.29 15.5 10.6958 15.2583 10.2125 14.775C9.72917 14.2917 9.4875 13.6975 9.4875 12.9926C9.4875 12.2877 9.72917 11.6918 10.2125 11.2051C10.6958 10.7184 11.29 10.475 11.9949 10.475C12.6998 10.475 13.2957 10.7184 13.7824 11.2051C14.2691 11.6918 14.5125 12.2877 14.5125 12.9926C14.5125 13.6975 14.2691 14.2917 13.7824 14.775C13.2957 15.2583 12.6998 15.5 11.9949 15.5ZM4.125 20.75C3.60937 20.75 3.16796 20.5664 2.80077 20.1992C2.43359 19.832 2.25 19.3906 2.25 18.875V7.1125C2.25 6.59687 2.43359 6.15546 2.80077 5.78828C3.16796 5.42109 3.60937 5.2375 4.125 5.2375H7.275L8.5125 3.875C8.68652 3.6827 8.89613 3.53046 9.14133 3.41828C9.38651 3.30609 9.64357 3.25 9.9125 3.25H14.0875C14.3564 3.25 14.6135 3.30609 14.8587 3.41828C15.1039 3.53046 15.3135 3.6827 15.4875 3.875L16.725 5.2375H19.875C20.3906 5.2375 20.832 5.42109 21.1992 5.78828C21.5664 6.15546 21.75 6.59687 21.75 7.1125V18.875C21.75 19.3906 21.5664 19.832 21.1992 20.1992C20.832 20.5664 20.3906 20.75 19.875 20.75H4.125ZM4.125 18.875H19.875V7.1125H15.8853L14.0875 5.125H9.9162L8.125 7.1125H4.125V18.875Z"
fill="#26201E"
/>
</g>
</svg>
)
}

View File

@@ -6,9 +6,11 @@ import TripAdvisorIcon from "./TripAdvisor"
import {
AccessibilityIcon,
AccountCircleIcon,
AirplaneIcon,
ArrowRightIcon,
BarIcon,
BikingIcon,
BusinessIcon,
CalendarIcon,
CameraIcon,
CellphoneIcon,
@@ -66,12 +68,16 @@ export function getIconByIconName(icon?: IconName): FC<IconProps> | null {
return AccessibilityIcon
case IconName.AccountCircle:
return AccountCircleIcon
case IconName.Airplane:
return AirplaneIcon
case IconName.ArrowRight:
return ArrowRightIcon
case IconName.Bar:
return BarIcon
case IconName.Biking:
return BikingIcon
case IconName.Business:
return BusinessIcon
case IconName.Calendar:
return CalendarIcon
case IconName.Camera:

View File

@@ -1,8 +1,10 @@
export { default as AccessibilityIcon } from "./Accessibility"
export { default as AccountCircleIcon } from "./AccountCircle"
export { default as AirplaneIcon } from "./Airplane"
export { default as ArrowRightIcon } from "./ArrowRight"
export { default as BarIcon } from "./Bar"
export { default as BikingIcon } from "./Biking"
export { default as BusinessIcon } from "./Business"
export { default as CalendarIcon } from "./Calendar"
export { default as CameraIcon } from "./Camera"
export { default as CellphoneIcon } from "./Cellphone"

View File

@@ -1,19 +1,20 @@
import { getIconByIconName } from "@/components/Icons/get-icon-by-icon-name"
import { getCategoryIconName } from "../utils"
import { getIconByPoiGroupAndCategory } from "../utils"
import { poiVariants } from "./variants"
import type { PoiMarkerProps } from "@/types/components/maps/poiMarker"
export default function PoiMarker({
category,
group,
categoryName,
skipBackground,
size = 16,
className = "",
}: PoiMarkerProps) {
const iconName = getCategoryIconName(category)
const iconName = getIconByPoiGroupAndCategory(group, categoryName)
const Icon = iconName ? getIconByIconName(iconName) : null
const classNames = poiVariants({ category, skipBackground, className })
const classNames = poiVariants({ group, skipBackground, className })
return Icon ? (
<span className={classNames}>

View File

@@ -7,46 +7,29 @@ This will be handled later. */
align-items: center;
padding: var(--Spacing-x-half);
border-radius: var(--Corner-radius-Rounded);
background-color: var(--Scandic-Beige-90);
}
.airport,
.amusementPark,
.busTerminal,
.fair,
.hospital,
.hotel,
.marketingCity {
background-color: var(--UI-Text-Placeholder);
}
.museum {
background: var(--Base-Interactive-Surface-Secondary-normal);
.attractions {
background-color: var(--Base-Interactive-Surface-Secondary-normal);
}
.nearbyCompanies,
.parkingGarage {
.business {
background-color: var(--Scandic-Yellow-50);
}
.restaurant {
background: var(--Scandic-Peach-50);
.location {
background-color: var(--UI-Text-Placeholder);
}
.shopping {
background: var(--Base-Interactive-Surface-Primary-normal);
.parking {
background-color: var(--UI-Text-Active);
}
.sports,
.theatre {
.publicTransport {
background-color: var(--Base-Interactive-Surface-Tertiary-normal);
}
.tourist {
background: var(--Scandic-Yellow-60);
}
.transportations {
background: var(--Base-Interactive-Surface-Tertiary-normal);
}
.zoo {
.shoppingDining {
background-color: var(--Base-Interactive-Surface-Primary-normal);
}
.icon.transparent {
background: transparent;
background-color: transparent;
padding: 0;
}

View File

@@ -2,26 +2,17 @@ import { cva } from "class-variance-authority"
import styles from "./poi.module.css"
import { PointOfInterestGroupEnum } from "@/types/hotel"
export const poiVariants = cva(styles.icon, {
variants: {
category: {
Airport: styles.airport,
"Amusement park": styles.amusementPark,
"Bus terminal": styles.busTerminal,
Fair: styles.fair,
Hospital: styles.hospital,
Hotel: styles.hotel,
"Marketing city": styles.marketingCity,
Museum: styles.museum,
"Nearby companies": styles.nearbyCompanies,
"Parking / Garage": styles.parkingGarage,
Restaurant: styles.restaurant,
Shopping: styles.shopping,
Sports: styles.sports,
Theatre: styles.theatre,
Tourist: styles.tourist,
Transportations: styles.transportations,
Zoo: styles.zoo,
group: {
[PointOfInterestGroupEnum.ATTRACTIONS]: styles.attractions,
[PointOfInterestGroupEnum.BUSINESS]: styles.business,
[PointOfInterestGroupEnum.LOCATION]: styles.location,
[PointOfInterestGroupEnum.PARKING]: styles.parking,
[PointOfInterestGroupEnum.PUBLIC_TRANSPORT]: styles.publicTransport,
[PointOfInterestGroupEnum.SHOPPING_DINING]: styles.shoppingDining,
},
skipBackground: {
true: styles.transparent,

View File

@@ -1,21 +1,30 @@
import { IconName } from "@/types/components/icon"
import type { PointOfInterestCategory } from "@/types/hotel"
import {
PointOfInterestCategoryNameEnum,
PointOfInterestGroupEnum,
} from "@/types/hotel"
/* 2024-09-18: At the moment, the icons for the different categories is unknown.
This will be handled later. */
export function getCategoryIconName(category?: PointOfInterestCategory | null) {
switch (category) {
case "Transportations":
return IconName.Train
case "Shopping":
export function getIconByPoiGroupAndCategory(
group: PointOfInterestGroupEnum,
category?: PointOfInterestCategoryNameEnum
) {
switch (group) {
case PointOfInterestGroupEnum.PUBLIC_TRANSPORT:
return category === PointOfInterestCategoryNameEnum.AIRPORT
? IconName.Airplane
: IconName.Train
case PointOfInterestGroupEnum.ATTRACTIONS:
return category === PointOfInterestCategoryNameEnum.MUSEUM
? IconName.Museum
: IconName.Camera
case PointOfInterestGroupEnum.BUSINESS:
return IconName.Business
case PointOfInterestGroupEnum.PARKING:
return IconName.Parking
case PointOfInterestGroupEnum.SHOPPING_DINING:
return IconName.Shopping
case "Museum":
return IconName.Museum
case "Tourist":
return IconName.Cultural
case "Restaurant":
return IconName.Restaurant
case PointOfInterestGroupEnum.LOCATION:
default:
return IconName.StarFilled
return IconName.Location
}
}

View File

@@ -2,6 +2,13 @@ import { z } from "zod"
import { toLang } from "@/server/utils"
import { getPoiGroupByCategoryName } from "./utils"
import {
PointOfInterestCategoryNameEnum,
PointOfInterestGroupEnum,
} from "@/types/hotel"
const ratingsSchema = z
.object({
tripAdvisor: z.object({
@@ -213,32 +220,15 @@ const rewardNightSchema = z.object({
}),
})
const poiCategories = z.enum([
"Airport",
"Amusement park",
"Bus terminal",
"Fair",
"Hospital",
"Hotel",
"Marketing city",
"Museum",
"Nearby companies",
"Parking / Garage",
"Restaurant",
"Shopping",
"Sports",
"Theatre",
"Tourist",
"Transportations",
"Zoo",
])
const poiGroups = z.nativeEnum(PointOfInterestGroupEnum)
const poiCategoryNames = z.nativeEnum(PointOfInterestCategoryNameEnum)
export const pointOfInterestSchema = z
.object({
name: z.string(),
distance: z.number(),
category: z.object({
name: poiCategories,
name: poiCategoryNames,
group: z.string(),
}),
location: locationSchema,
@@ -247,7 +237,8 @@ export const pointOfInterestSchema = z
.transform((poi) => ({
name: poi.name,
distance: poi.distance,
category: poi.category.name,
categoryName: poi.category.name,
group: getPoiGroupByCategoryName(poi.category.name),
coordinates: {
lat: poi.location.latitude,
lng: poi.location.longitude,

View File

@@ -1,5 +1,3 @@
import { IconName } from "@/types/components/icon"
import deepmerge from "deepmerge"
import { unstable_cache } from "next/cache"
@@ -15,23 +13,39 @@ import {
} from "./output"
import type { RequestOptionsWithOutBody } from "@/types/fetch"
import {
PointOfInterestCategoryNameEnum,
PointOfInterestGroupEnum,
} from "@/types/hotel"
import type { Lang } from "@/constants/languages"
import type { Endpoint } from "@/lib/api/endpoints"
export function getIconByPoiCategory(category: string) {
export function getPoiGroupByCategoryName(
category: PointOfInterestCategoryNameEnum
) {
switch (category) {
case "Transportations":
return IconName.Train
case "Shopping":
return IconName.Shopping
case "Museum":
return IconName.Museum
case "Tourist":
return IconName.Cultural
case "Restaurant":
return IconName.Restaurant
case PointOfInterestCategoryNameEnum.AIRPORT:
case PointOfInterestCategoryNameEnum.BUS_TERMINAL:
case PointOfInterestCategoryNameEnum.TRANSPORTATIONS:
return PointOfInterestGroupEnum.PUBLIC_TRANSPORT
case PointOfInterestCategoryNameEnum.AMUSEMENT_PARK:
case PointOfInterestCategoryNameEnum.MUSEUM:
case PointOfInterestCategoryNameEnum.SPORTS:
case PointOfInterestCategoryNameEnum.THEATRE:
case PointOfInterestCategoryNameEnum.TOURIST:
case PointOfInterestCategoryNameEnum.ZOO:
return PointOfInterestGroupEnum.ATTRACTIONS
case PointOfInterestCategoryNameEnum.NEARBY_COMPANIES:
case PointOfInterestCategoryNameEnum.FAIR:
return PointOfInterestGroupEnum.BUSINESS
case PointOfInterestCategoryNameEnum.PARKING_GARAGE:
return PointOfInterestGroupEnum.PARKING
case PointOfInterestCategoryNameEnum.SHOPPING:
case PointOfInterestCategoryNameEnum.RESTAURANT:
return PointOfInterestGroupEnum.SHOPPING_DINING
case PointOfInterestCategoryNameEnum.HOSPITAL:
default:
return null
return PointOfInterestGroupEnum.LOCATION
}
}

View File

@@ -9,9 +9,11 @@ export interface IconProps
export enum IconName {
Accessibility = "Accessibility",
AccountCircle = "AccountCircle",
Airplane = "Airplane",
ArrowRight = "ArrowRight",
Bar = "Bar",
Biking = "Biking",
Business = "Business",
Calendar = "Calendar",
Camera = "Camera",
Cellphone = "Cellphone",

View File

@@ -2,7 +2,14 @@ import { poiVariants } from "@/components/Maps/Markers/Poi/variants"
import type { VariantProps } from "class-variance-authority"
import {
PointOfInterestCategoryNameEnum,
PointOfInterestGroupEnum,
} from "@/types/hotel"
export interface PoiMarkerProps extends VariantProps<typeof poiVariants> {
group: PointOfInterestGroupEnum
categoryName?: PointOfInterestCategoryNameEnum
size?: number
className?: string
}

View File

@@ -20,4 +20,32 @@ export type HotelTripAdvisor =
export type RoomData = z.infer<typeof roomSchema>
export type PointOfInterest = z.output<typeof pointOfInterestSchema>
export type PointOfInterestCategory = PointOfInterest["category"]
export enum PointOfInterestCategoryNameEnum {
AIRPORT = "Airport",
AMUSEMENT_PARK = "Amusement park",
BUS_TERMINAL = "Bus terminal",
FAIR = "Fair",
HOSPITAL = "Hospital",
HOTEL = "Hotel",
MARKETING_CITY = "Marketing city",
MUSEUM = "Museum",
NEARBY_COMPANIES = "Nearby companies",
PARKING_GARAGE = "Parking / Garage",
RESTAURANT = "Restaurant",
SHOPPING = "Shopping",
SPORTS = "Sports",
THEATRE = "Theatre",
TOURIST = "Tourist",
TRANSPORTATIONS = "Transportations",
ZOO = "Zoo",
}
export enum PointOfInterestGroupEnum {
PUBLIC_TRANSPORT = "Public transport",
ATTRACTIONS = "Attractions",
BUSINESS = "Business",
LOCATION = "Location",
PARKING = "Parking",
SHOPPING_DINING = "Shopping & Dining",
}