diff --git a/apps/ui/components/SortingComponent.tsx b/apps/ui/components/SortingComponent.tsx index 296776aac..04aaa6235 100644 --- a/apps/ui/components/SortingComponent.tsx +++ b/apps/ui/components/SortingComponent.tsx @@ -1,54 +1,55 @@ import React from "react"; import { Sorting } from "@/components/form"; -import { useRouter } from "next/router"; import { useTranslation } from "next-i18next"; -import { useSearchValues } from "@/hooks/useSearchValues"; -import { type Url } from "next/dist/shared/lib/router/router"; +import { useSearchParams } from "next/navigation"; +import { useSearchModify } from "@/hooks/useSearchValues"; -export function SortingComponent() { - const searchValues = useSearchValues(); - const { t } = useTranslation(); - const router = useRouter(); +const SORTING_OPTIONS = [ + { + label: "search:sorting.label.name", + value: "name", + }, + { + label: "search:sorting.label.type", + value: "typeRank", + }, + { + label: "search:sorting.label.unit", + value: "unitName", + }, +] as const; - const sortingOptions = [ - { - label: t("search:sorting.label.name"), - value: "name", - }, - { - label: t("search:sorting.label.type"), - value: "typeRank", - }, - { - label: t("search:sorting.label.unit"), - value: "unitName", - }, - ]; +function validateSorting( + value: string | null +): (typeof SORTING_OPTIONS)[number]["value"] { + if (SORTING_OPTIONS?.some((option) => option.value === value)) { + return value as (typeof SORTING_OPTIONS)[number]["value"]; + } + return "name"; +} - const isOrderingAsc = searchValues.order !== "desc"; +export function SortingComponent() { + const searchValues = useSearchParams(); + const { handleRouteChange } = useSearchModify(); + const { t } = useTranslation(); - const value = - searchValues.sort != null && !Array.isArray(searchValues.sort) - ? searchValues.sort - : "name"; + const sortingOptions = SORTING_OPTIONS.map((option) => ({ + label: t(option.label), + value: option.value, + })); - const handleRouteChange = (url: Url) => { - router.replace(url, undefined, { shallow: true, scroll: false }); - }; + const isOrderingAsc = searchValues.get("order") !== "desc"; + const value = validateSorting(searchValues.get("sort")); const handleSort = (sort: string) => { - const params = { - ...searchValues, - sort, - }; - handleRouteChange({ query: params }); + const params = new URLSearchParams(searchValues); + params.set("sort", sort); + handleRouteChange(params); }; const handleOrderChange = (order: "asc" | "desc") => { - const params = { - ...searchValues, - order, - }; - handleRouteChange({ query: params }); + const params = new URLSearchParams(searchValues); + params.set("order", order); + handleRouteChange(params); }; return ( diff --git a/apps/ui/components/search/FilterTagList.tsx b/apps/ui/components/search/FilterTagList.tsx index a9a5f442f..5fd6939f4 100644 --- a/apps/ui/components/search/FilterTagList.tsx +++ b/apps/ui/components/search/FilterTagList.tsx @@ -1,8 +1,9 @@ import React from "react"; import { useTranslation } from "next-i18next"; import { FilterTags, StyledTag, ResetButton } from "common/src/tags"; -import { useSearchModify, useSearchValues } from "@/hooks/useSearchValues"; +import { useSearchModify } from "@/hooks/useSearchValues"; import { type TFunction } from "i18next"; +import { useSearchParams } from "next/navigation"; type FilterTagProps = { filters: readonly string[]; @@ -48,18 +49,25 @@ export function FilterTagList({ const { t } = useTranslation(); const { handleRemoveTag, handleResetTags } = useSearchModify(); - const formValues = useSearchValues(); + const searchValues = useSearchParams(); - const formValueKeys = Object.keys(formValues); - const filterOrder = filters; - const sortedValues = [...formValueKeys].sort( - (a, b) => filterOrder.indexOf(a) - filterOrder.indexOf(b) + const possibleKeys = searchValues.keys() ?? ([] as const); + + const formValueKeys: string[] = []; + for (const key of possibleKeys) { + if (!formValueKeys.includes(key)) { + formValueKeys.push(key); + } + } + + const keys = [...formValueKeys].sort( + (a, b) => filters.indexOf(a) - filters.indexOf(b) ); - const filteredTags = sortedValues + const filteredTags = keys .filter((key) => !hideList.includes(key)) .filter((key) => { - const value = formValues[key]; + const value = searchValues.get(key); if (value == null || value === "") { return false; } @@ -74,32 +82,29 @@ export function FilterTagList({ return ( {filteredTags.map((key) => { + const value = searchValues.getAll(key); const label = t(`searchForm:filters.${key}`, { label: key, - value: formValues[key], - count: Number(formValues[key]), + value, + count: value.length, }); - const value = formValues[key]; - // This should never happen, but for type completeness - if (value == null || value === "") { - return null; - } // Still have the old string encoded values (key=v1,v2,v3) for backwards compatibility // but support the better array version (key=v1&key=v2&key=v3) for new code const isMultiSelect = multiSelectFilters.includes(key); if (isMultiSelect) { - const values = Array.isArray(value) ? value : value.split(","); - return values.map((subValue) => ( + const isOldFormat = value.length === 1 && value[0].includes(","); + const values = isOldFormat ? value[0].split(",") : value; + return values.map((val) => ( handleRemoveTag([subValue], key)} - onDelete={() => handleRemoveTag([subValue], key)} - key={`${key}-${subValue}`} + id={`filter-tag__${key}-${val}`} + onClick={() => handleRemoveTag(key, val)} + onDelete={() => handleRemoveTag(key, val)} + key={`${key}-${val}`} aria-label={t(`searchForm:removeFilter`, { - value: translateTag(key, subValue), + value: translateTag(key, val), })} > - {translateTag(key, subValue)} + {translateTag(key, val)} )); } @@ -107,8 +112,8 @@ export function FilterTagList({ return ( handleRemoveTag([key])} - // Why is there no onClick here? + onDelete={() => handleRemoveTag(key)} + onClick={() => handleRemoveTag(key)} key={key} aria-label={t(`searchForm:removeFilter`, { value: label, diff --git a/apps/ui/components/search/SeasonalSearchForm.tsx b/apps/ui/components/search/SeasonalSearchForm.tsx index 2720b0382..7cb78da43 100644 --- a/apps/ui/components/search/SeasonalSearchForm.tsx +++ b/apps/ui/components/search/SeasonalSearchForm.tsx @@ -3,9 +3,8 @@ import { useTranslation } from "next-i18next"; import { TextInput, IconSearch } from "hds-react"; import { type SubmitHandler, useForm } from "react-hook-form"; import { participantCountOptions } from "@/modules/const"; -import { useSearchModify, useSearchValues } from "@/hooks/useSearchValues"; +import { useSearchModify } from "@/hooks/useSearchValues"; import { FilterTagList } from "./FilterTagList"; -import { ParsedUrlQuery } from "node:querystring"; import { ControlledSelect } from "common/src/components/form/ControlledSelect"; import { ControlledMultiSelect } from "./ControlledMultiSelect"; import { BottomContainer, Filters, StyledSubmitButton } from "./styled"; @@ -14,7 +13,9 @@ import { mapQueryParamToNumberArray, mapSingleParamToFormValue, } from "@/modules/search"; -import SingleLabelInputGroup from "../common/SingleLabelInputGroup"; +import SingleLabelInputGroup from "@/components/common/SingleLabelInputGroup"; +import { type URLSearchParams } from "node:url"; +import { useSearchParams } from "next/navigation"; const filterOrder = [ "applicationRound", @@ -36,16 +37,16 @@ type FormValues = { }; // TODO combine as much as possible with the one in single-search (move them to a common place) -function mapQueryToForm(query: ParsedUrlQuery): FormValues { +function mapQueryToForm(query: URLSearchParams): FormValues { return { - purposes: mapQueryParamToNumberArray(query.purposes), - unit: mapQueryParamToNumberArray(query.unit), + purposes: mapQueryParamToNumberArray(query.getAll("purposes")), + unit: mapQueryParamToNumberArray(query.getAll("unit")), reservationUnitTypes: mapQueryParamToNumberArray( - query.reservationUnitTypes + query.getAll("reservationUnitTypes") ), - minPersons: mapQueryParamToNumber(query.minPersons) ?? null, - maxPersons: mapQueryParamToNumber(query.maxPersons) ?? null, - textSearch: mapSingleParamToFormValue(query.textSearch) ?? "", + minPersons: mapQueryParamToNumber(query.getAll("minPersons")), + maxPersons: mapQueryParamToNumber(query.getAll("maxPersons")), + textSearch: mapSingleParamToFormValue(query.getAll("textSearch")) ?? "", }; } @@ -65,7 +66,7 @@ export function SeasonalSearchForm({ const { handleSearch } = useSearchModify(); - const searchValues = useSearchValues(); + const searchValues = useSearchParams(); const { control, register, handleSubmit } = useForm({ values: mapQueryToForm(searchValues), }); diff --git a/apps/ui/components/search/SingleSearchForm.tsx b/apps/ui/components/search/SingleSearchForm.tsx index 4f8a279d8..a222785b8 100644 --- a/apps/ui/components/search/SingleSearchForm.tsx +++ b/apps/ui/components/search/SingleSearchForm.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode } from "react"; +import React from "react"; import { useTranslation } from "next-i18next"; import { Checkbox, IconSearch, TextInput } from "hds-react"; import { type SubmitHandler, useForm, Controller } from "react-hook-form"; @@ -11,8 +11,7 @@ import { getDurationOptions, participantCountOptions } from "@/modules/const"; import { DateRangePicker } from "@/components/form"; import { FilterTagList } from "./FilterTagList"; import SingleLabelInputGroup from "@/components/common/SingleLabelInputGroup"; -import { useSearchModify, useSearchValues } from "@/hooks/useSearchValues"; -import { type ParsedUrlQuery } from "node:querystring"; +import { useSearchModify } from "@/hooks/useSearchValues"; import { ControlledMultiSelect } from "./ControlledMultiSelect"; import { ControlledSelect } from "common/src/components/form/ControlledSelect"; import { @@ -27,21 +26,13 @@ import { OptionalFilters, StyledSubmitButton, } from "./styled"; +import { useSearchParams, type ReadonlyURLSearchParams } from "next/navigation"; const StyledCheckBox = styled(Checkbox)` margin: 0 !important; grid-column: -2 / span 1; `; -const SingleLabelRangeWrapper = styled(SingleLabelInputGroup)<{ - label: string; - children: ReactNode; -}>` - & > div:not(:first-child) { - margin-top: var(--spacing-s); - } -`; - type FormValues = { // TODO there is some confusion on the types of these // they are actually an array of pks (number) but they are encoded as val1,val2,val3 string @@ -60,29 +51,28 @@ type FormValues = { textSearch: string; }; -function mapQueryToForm(query: ParsedUrlQuery): FormValues { - const dur = mapQueryParamToNumber(query.duration); +function mapQueryToForm(params: ReadonlyURLSearchParams): FormValues { + const dur = mapQueryParamToNumber(params.getAll("duration")); const duration = dur != null && dur > 0 ? dur : null; const showOnlyReservable = - mapSingleBooleanParamToFormValue(query.showOnlyReservable) ?? true; + mapSingleBooleanParamToFormValue(params.getAll("showOnlyReservable")) ?? + true; return { - purposes: mapQueryParamToNumberArray(query.purposes), - unit: mapQueryParamToNumberArray(query.unit), - equipments: mapQueryParamToNumberArray(query.equipments), + purposes: mapQueryParamToNumberArray(params.getAll("purposes")), + unit: mapQueryParamToNumberArray(params.getAll("unit")), + equipments: mapQueryParamToNumberArray(params.getAll("equipments")), reservationUnitTypes: mapQueryParamToNumberArray( - query.reservationUnitTypes + params.getAll("reservationUnitTypes") ), - // ? - timeBegin: mapSingleParamToFormValue(query.timeBegin) ?? null, - timeEnd: mapSingleParamToFormValue(query.timeEnd) ?? null, - startDate: mapSingleParamToFormValue(query.startDate) ?? null, - endDate: mapSingleParamToFormValue(query.endDate) ?? null, - // number params + timeBegin: mapSingleParamToFormValue(params.getAll("timeBegin")), + timeEnd: mapSingleParamToFormValue(params.getAll("timeEnd")), + startDate: mapSingleParamToFormValue(params.getAll("startDate")), + endDate: mapSingleParamToFormValue(params.getAll("endDate")), duration, - minPersons: mapQueryParamToNumber(query.minPersons) ?? null, - maxPersons: mapQueryParamToNumber(query.maxPersons) ?? null, + minPersons: mapQueryParamToNumber(params.getAll("minPersons")), + maxPersons: mapQueryParamToNumber(params.getAll("maxPersons")), showOnlyReservable, - textSearch: mapSingleParamToFormValue(query.textSearch) ?? "", + textSearch: mapSingleParamToFormValue(params.getAll("textSearch")) ?? "", }; } @@ -125,18 +115,17 @@ export function SingleSearchForm({ isLoading: boolean; }): JSX.Element | null { const { handleSearch } = useSearchModify(); - const { t } = useTranslation(); - const unitTypeOptions = reservationUnitTypeOptions; - const durationOptions = getDurationOptions(t); - - const searchValues = useSearchValues(); + const searchValues = useSearchParams(); // TODO the users of this should be using watch const formValues = mapQueryToForm(searchValues); - const { handleSubmit, setValue, getValues, control, register } = - useForm({ - values: formValues, - }); + const form = useForm({ + values: formValues, + }); + + const { handleSubmit, setValue, getValues, control, register } = form; + const unitTypeOptions = reservationUnitTypeOptions; + const durationOptions = getDurationOptions(t); const translateTag = (key: string, value: string): string | undefined => { // Handle possible number / string comparison @@ -195,7 +184,7 @@ export function SingleSearchForm({ options={equipmentsOptions} label={t("searchForm:equipmentsFilter")} /> - + - - + + - + { - router.replace(url, undefined, { shallow: true, scroll: false }); + const handleRouteChange = (query: URLSearchParams | ParsedUrlQueryInput) => { + if (query instanceof URLSearchParams) { + // [id] param is not included in the URLSearchParams object but required when routing + if (router.query.id) { + query.set("id", router.query.id as string); + } + router.replace({ query: query.toString() }, undefined, { + shallow: true, + scroll: false, + }); + } else { + router.replace({ query }, undefined, { shallow: true, scroll: false }); + } }; // TODO type this properly (not a Record) @@ -38,6 +34,7 @@ export function useSearchModify() { const v = Number(ref) > 0 ? Number(ref) : null; const nextRef = v != null ? v + 1 : 1; + // TODO can this be refactored to use the URLSearchParams object? const newValues = { ...criteria, sort: newSort, @@ -45,35 +42,27 @@ export function useSearchModify() { ...(force ? { ref: nextRef } : {}), }; - // NOTE without this next router can't handle [id] pages - const url: UrlObject = { - pathname: router.pathname, - query: { - ...router.query, - ...newValues, - }, + const query: ParsedUrlQueryInput = { + ...router.query, + ...newValues, }; - handleRouteChange(url); + handleRouteChange(query); }; /// @param hideList - list of keys to ignore when resetting the query const handleResetTags = (hideList: readonly string[]) => { - const keys = Object.keys(searchValues); - const newValues = keys.reduce((acc, key) => { - if (hideList.includes(key)) { - acc[key] = searchValues[key]; + const params = new URLSearchParams(); + for (const key of hideList) { + const values = searchValues.getAll(key); + for (const value of values) { + params.append(key, value); } - return acc; - }, {}); - // NOTE for some reason we don't have to fix [id] pages here - handleRouteChange({ query: newValues }); + } + + handleRouteChange(params); }; - // TODO is there a case where we remove the whole key: array? and not just single values - // also we can do a lot simpler variant of this, just remove the key from the query instead of constructing a new query - // TODO what are the use cases for array input? - // TODO what are the use cases for subItemKey? and can it be null? - const handleRemoveTag = (key: string[], subItemKey?: string) => { + const handleRemoveTag = (key: string, value?: string) => { // Forbidding resetting all filters (need to rework this so we always remove a single value) if (key.length === 0) { throw new Error("key must have at least one value"); @@ -82,29 +71,23 @@ export function useSearchModify() { // Oh this allows for a case of removing a single value? or no // yeah, the subItemKey is the actual query key we are finding from // the key: [] is the values we are removing - let newValues = {}; - if (subItemKey) { - const values = searchValues[subItemKey]; - if (values != null && typeof values === "string") { - const newValue = values.split(",").filter((n) => !key?.includes(n)); - newValues = { - ...searchValues, - [subItemKey]: newValue.join(","), - }; - } else if (values != null && Array.isArray(values)) { - const newValue = values.filter((n) => !key?.includes(n)); - newValues = { - ...searchValues, - [subItemKey]: newValue, - }; + const newValues = new URLSearchParams(searchValues); + if (value) { + const values = searchValues.getAll(key); + const isOldFormat = values.length === 1 && values[0].includes(","); + if (isOldFormat) { + const newVal = isOldFormat ? values[0].split(",") : values; + const newValue = newVal.filter((n) => n !== value); + newValues.set(key, newValue.join(",")); + } else { + newValues.delete(key, value); } - } else if (key) { - const { [`${key}`]: _, ...rest } = searchValues; - newValues = rest; + } else { + newValues.delete(key); } - handleRouteChange({ query: newValues }); + handleRouteChange(newValues); }; - return { handleSearch, handleRemoveTag, handleResetTags }; + return { handleSearch, handleRemoveTag, handleResetTags, handleRouteChange }; } diff --git a/apps/ui/modules/search.ts b/apps/ui/modules/search.ts index a1f0fcddc..cf34a1371 100644 --- a/apps/ui/modules/search.ts +++ b/apps/ui/modules/search.ts @@ -3,6 +3,7 @@ import { type LocalizationLanguages, getLocalizationLang, + toNumber, } from "common/src/helpers"; import { type QueryReservationUnitsArgs, @@ -20,11 +21,11 @@ import { getTranslationSafe, toApiDate, } from "common/src/common/util"; -import { type ParsedUrlQuery } from "node:querystring"; import { fromUIDate } from "./util"; import { startOfDay } from "date-fns"; import { SEARCH_PAGING_LIMIT } from "./const"; import { type ApolloClient } from "@apollo/client"; +import { type ReadonlyURLSearchParams } from "next/navigation"; function transformOrderByName(desc: boolean, language: LocalizationLanguages) { if (language === "fi") { @@ -71,7 +72,7 @@ function transformOrderByTypeRank( } function transformOrderBy( - orderBy: string, + orderBy: string | null, desc: boolean, language: LocalizationLanguages ): ReservationUnitOrderingChoices | null { @@ -125,80 +126,96 @@ function ignoreMaybeArray(value: T | T[]): T { return Array.isArray(value) ? value[0] : value; } -export function processVariables( - values: ParsedUrlQuery, - language: string, - reservationKind: ReservationKind -): QueryReservationUnitsArgs { - const sortCriteria = values.sort; - const desc = values.order === "desc"; - const orderBy = sortCriteria - ? transformSortString(ignoreMaybeArray(sortCriteria), language, desc) - : null; - const startDate = fromUIDate(ignoreMaybeArray(values.startDate ?? "")); - const endDate = fromUIDate(ignoreMaybeArray(values.endDate ?? "")); +type ProcessVariablesParams = + | { + values: ReadonlyURLSearchParams; + language: string; + kind: ReservationKind.Direct; + } + | { + values: ReadonlyURLSearchParams; + language: string; + kind: ReservationKind.Season; + applicationRound: number; + }; +export function processVariables({ + values, + language, + kind, + ...rest +}: ProcessVariablesParams): QueryReservationUnitsArgs { + const sortCriteria = values.getAll("sort"); + const desc = values.getAll("order").includes("desc"); + const orderBy = transformSortString( + ignoreMaybeArray(sortCriteria), + language, + desc + ); + const today = startOfDay(new Date()); + const startDate = fromUIDate(ignoreMaybeArray(values.getAll("startDate"))); + const reservableDateStart = + startDate && startDate >= today ? toApiDate(startDate) : null; + const endDate = fromUIDate(ignoreMaybeArray(values.getAll("endDate"))); + const reservableDateEnd = + endDate && endDate >= today ? toApiDate(endDate) : null; - const dur = - values.duration != null ? Number(ignoreMaybeArray(values.duration)) : null; + const dur = Number(ignoreMaybeArray(values.getAll("duration"))); const duration = dur != null && dur > 0 ? dur : null; - const isSeasonal = reservationKind === ReservationKind.Season; + const isSeasonal = kind === ReservationKind.Season; + const textSearch = ignoreMaybeArray(values.getAll("textSearch")); + const minPersons = toNumber(ignoreMaybeArray(values.getAll("minPersons"))); + const maxPersons = toNumber(ignoreMaybeArray(values.getAll("maxPersons"))); + const purposes = paramToIntegers(values.getAll("purposes")); + const unit = paramToIntegers(values.getAll("unit")); + const reservationUnitTypes = paramToIntegers( + values.getAll("reservationUnitTypes") + ); + const equipments = paramToIntegers(values.getAll("equipments")); + const showOnlyReservable = + ignoreMaybeArray(values.getAll("showOnlyReservable")) !== "false"; + const applicationRound = + "applicationRound" in rest && isSeasonal ? rest.applicationRound : null; + const timeEnd = ignoreMaybeArray(values.getAll("timeEnd")); + const timeBegin = ignoreMaybeArray(values.getAll("timeBegin")); return { - ...(values.textSearch != null - ? { - textSearch: ignoreMaybeArray(values.textSearch), - } - : {}), - ...(values.minPersons != null - ? { - minPersons: parseInt(ignoreMaybeArray(values.minPersons), 10), - } - : {}), - ...(values.maxPersons != null - ? { - maxPersons: parseInt(ignoreMaybeArray(values.maxPersons), 10), - } - : {}), - ...(values.purposes != null + ...(textSearch !== "" ? { - purposes: paramToIntegers(values.purposes), + textSearch, } : {}), - ...(values.unit != null + ...(minPersons != null && minPersons >= 0 ? { - unit: paramToIntegers(values.unit), + minPersons, } : {}), - ...(values.reservationUnitTypes != null + ...(maxPersons != null && maxPersons >= 0 ? { - reservationUnitType: paramToIntegers(values.reservationUnitTypes), + maxPersons, } : {}), - ...(values.equipments != null + purposes, + unit, + reservationUnitType: reservationUnitTypes, + equipments, + ...(startDate != null ? { - equipments: paramToIntegers(values.equipments), + reservableDateStart, } : {}), - ...(values.startDate != null + ...(endDate != null ? { - reservableDateStart: - startDate && startDate >= today ? toApiDate(startDate) : null, + reservableDateEnd, } : {}), - ...(values.endDate != null + ...(timeBegin != null && timeBegin !== "" ? { - reservableDateEnd: - endDate && endDate >= today ? toApiDate(endDate) : null, + reservableTimeStart: timeBegin, } : {}), - ...(values.timeBegin != null && values.timeBegin !== "" + ...(timeEnd != null && timeEnd !== "" ? { - reservableTimeStart: ignoreMaybeArray(values.timeBegin), - } - : {}), - ...(values.timeEnd != null && values.timeEnd !== "" - ? { - reservableTimeEnd: ignoreMaybeArray(values.timeEnd), + reservableTimeEnd: timeEnd, } : {}), ...(duration != null @@ -206,19 +223,19 @@ export function processVariables( reservableMinimumDurationMinutes: duration.toString(), } : {}), - ...(!isSeasonal && ignoreMaybeArray(values.showOnlyReservable) !== "false" + ...(!isSeasonal && showOnlyReservable ? { showOnlyReservable: true, } : {}), - ...(values.id != null && isSeasonal - ? { applicationRound: paramToIntegers(values.id) } + ...(isSeasonal && applicationRound != null && applicationRound > 0 + ? { applicationRound: [applicationRound] } : {}), first: SEARCH_PAGING_LIMIT, orderBy, isDraft: false, isVisible: true, - reservationKind, + reservationKind: kind, }; } @@ -252,16 +269,9 @@ export function mapQueryParamToNumber( if (param == null) return null; if (param === "") return null; if (Array.isArray(param)) { - const v = Number(param[0]); - if (Number.isNaN(v)) { - return null; - } - return v; - } - if (Number.isNaN(Number(param))) { - return null; + return toNumber(param[0]); } - return Number(param); + return toNumber(param); } export function mapQueryParamToNumberArray( diff --git a/apps/ui/pages/recurring/[id]/index.tsx b/apps/ui/pages/recurring/[id]/index.tsx index f16da28b3..da78fce81 100644 --- a/apps/ui/pages/recurring/[id]/index.tsx +++ b/apps/ui/pages/recurring/[id]/index.tsx @@ -26,10 +26,10 @@ import { mapQueryParamToNumber, processVariables, } from "@/modules/search"; -import { useSearchValues } from "@/hooks/useSearchValues"; import { useSearchQuery } from "@/hooks/useSearchQuery"; import { SortingComponent } from "@/components/SortingComponent"; import { useRouter } from "next/router"; +import { useSearchParams } from "next/navigation"; type Props = Awaited>["props"]; @@ -71,7 +71,7 @@ function SeasonalSearch({ }: Props): JSX.Element { const { t, i18n } = useTranslation(); const router = useRouter(); - const searchValues = useSearchValues(); + const searchValues = useSearchParams(); const applicationRoundPk = mapQueryParamToNumber(router.query.id); const selectedApplicationRound = applicationRounds.find( @@ -86,11 +86,12 @@ function SeasonalSearch({ // Hide other application rounds' reservation units } = useReservationUnitsList(selectedApplicationRound); - const variables = processVariables( - searchValues, - i18n.language, - ReservationKind.Season - ); + const variables = processVariables({ + values: searchValues, + language: i18n.language, + kind: ReservationKind.Season, + applicationRound: applicationRoundPk ?? 0, + }); const query = useSearchQuery(variables); const { data, isLoading, error, fetchMore, previousData } = query; diff --git a/apps/ui/pages/search/index.tsx b/apps/ui/pages/search/index.tsx index deac99465..222510188 100644 --- a/apps/ui/pages/search/index.tsx +++ b/apps/ui/pages/search/index.tsx @@ -14,10 +14,10 @@ import ReservationUnitCard from "@/components/search/SingleSearchReservationUnit import { getCommonServerSideProps } from "@/modules/serverUtils"; import { createApolloClient } from "@/modules/apolloClient"; import { getSearchOptions, processVariables } from "@/modules/search"; -import { useSearchValues } from "@/hooks/useSearchValues"; import { useSearchQuery } from "@/hooks/useSearchQuery"; import { SortingComponent } from "@/components/SortingComponent"; import { Flex } from "common/styles/util"; +import { useSearchParams } from "next/navigation"; export async function getServerSideProps(ctx: GetServerSidePropsContext) { const { locale } = ctx; @@ -44,13 +44,13 @@ function SearchSingle({ }: Props): JSX.Element { const { t, i18n } = useTranslation(); - const searchValues = useSearchValues(); + const searchValues = useSearchParams(); - const vars = processVariables( - searchValues, - i18n.language, - ReservationKind.Direct - ); + const vars = processVariables({ + values: searchValues, + language: i18n.language, + kind: ReservationKind.Direct, + }); const query = useSearchQuery(vars); const { data, isLoading, error, fetchMore, previousData } = query; diff --git a/packages/common/src/helpers.ts b/packages/common/src/helpers.ts index a9f1140d2..a7e0fef6d 100644 --- a/packages/common/src/helpers.ts +++ b/packages/common/src/helpers.ts @@ -69,18 +69,18 @@ export const fromMondayFirst = (day: 0 | 1 | 2 | 3 | 4 | 5 | 6) => export type LocalizationLanguages = "fi" | "sv" | "en"; -export const getLocalizationLang = (code: string): LocalizationLanguages => { - if (code.startsWith("fi")) { +export function getLocalizationLang(code?: string): LocalizationLanguages { + if (code?.startsWith("fi")) { return "fi"; } - if (code.startsWith("sv")) { + if (code?.startsWith("sv")) { return "sv"; } - if (code.startsWith("en")) { + if (code?.startsWith("en")) { return "en"; } return "fi"; -}; +} export const isBrowser = typeof window !== "undefined";