diff --git a/pwa/package-lock.json b/pwa/package-lock.json index 66a84a4c..b5b625ed 100644 --- a/pwa/package-lock.json +++ b/pwa/package-lock.json @@ -41,6 +41,7 @@ "@nl-design-system-unstable/zwolle-design-tokens": "^1.0.0-alpha.100", "@parcel/watcher": "^2.3.0", "@tabler/icons-react": "2.21.0", + "@types/qs": "^6.9.9", "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", "@utrecht/component-library-react": "^1.0.0-alpha.394", @@ -56,6 +57,7 @@ "i18next": "^21.6.16", "jwt-decode": "^3.1.2", "lodash": "^4.17.21", + "qs": "^6.11.2", "react": "^18.2.0", "react-collapsible": "^2.10.0", "react-dom": "^18.2.0", @@ -3998,6 +4000,11 @@ "version": "15.7.5", "license": "MIT" }, + "node_modules/@types/qs": { + "version": "6.9.9", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.9.tgz", + "integrity": "sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg==" + }, "node_modules/@types/reach__router": { "version": "1.3.11", "license": "MIT", @@ -5381,6 +5388,20 @@ "version": "2.0.0", "license": "MIT" }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/boolbase": { "version": "1.0.0", "license": "ISC" @@ -7903,6 +7924,20 @@ "version": "2.0.0", "license": "MIT" }, + "node_modules/express/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/ext": { "version": "1.7.0", "license": "ISC", @@ -13528,8 +13563,9 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "license": "BSD-3-Clause", + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", "dependencies": { "side-channel": "^1.0.4" }, diff --git a/pwa/package.json b/pwa/package.json index ea587d63..6d6751db 100644 --- a/pwa/package.json +++ b/pwa/package.json @@ -4,7 +4,9 @@ "private": true, "description": "Product Website Template", "author": "Conduction", - "keywords": ["gatsby"], + "keywords": [ + "gatsby" + ], "scripts": { "develop": "gatsby develop", "start": "gatsby develop", @@ -55,6 +57,7 @@ "@nl-design-system-unstable/zwolle-design-tokens": "^1.0.0-alpha.100", "@parcel/watcher": "^2.3.0", "@tabler/icons-react": "2.21.0", + "@types/qs": "^6.9.9", "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", "@utrecht/component-library-react": "^1.0.0-alpha.394", @@ -70,6 +73,7 @@ "i18next": "^21.6.16", "jwt-decode": "^3.1.2", "lodash": "^4.17.21", + "qs": "^6.11.2", "react": "^18.2.0", "react-collapsible": "^2.10.0", "react-dom": "^18.2.0", diff --git a/pwa/src/services/filtersToQueryParams.ts b/pwa/src/services/filtersToQueryParams.ts index 72df8ae5..c87a1407 100644 --- a/pwa/src/services/filtersToQueryParams.ts +++ b/pwa/src/services/filtersToQueryParams.ts @@ -5,7 +5,7 @@ export const filtersToQueryParams = (filters: any): string => { delete filters[key]; }); - let params = ""; + let params: string = ""; for (const [key, value] of Object.entries(filters)) { if (!value) continue; @@ -28,4 +28,53 @@ export const filtersToQueryParams = (filters: any): string => { return params; }; +export const filtersToUrlQueryParams = (filters: any): string => { + Object.keys(filters) + .filter((key) => filterKeysToRemove.includes(key)) + .forEach((key) => { + delete filters[key]; + }); + + let params: string = ""; + + var first_iteration = true; + for (const [key, value] of Object.entries(filters)) { + if (!value) continue; + + if (first_iteration) { + if (typeof value === "string") { + params += `?${key}=${value.replace(/\s+/g, "_")}`; + } + + if (Array.isArray(value)) { + let arrayParams = ""; + + value.forEach((value) => { + arrayParams += `?${key}[]=${value.replace(/\s+/g, "_")}`; + }); + + params += arrayParams; + } + + first_iteration = false; + } else { + if (typeof value === "string") { + params += `&${key}=${value.replace(/\s+/g, "_")}`; + } + + if (Array.isArray(value)) { + let arrayParams = ""; + + value.forEach((value) => { + arrayParams += `&${key}[]=${value.replace(/\s+/g, "_")}`; + }); + + params += arrayParams; + } + } + } + + return params; +}; + const filterKeysToRemove: string[] = []; diff --git a/pwa/src/templates/templateParts/filters/FiltersTemplate.tsx b/pwa/src/templates/templateParts/filters/FiltersTemplate.tsx index 0cba6fd1..34dcc340 100644 --- a/pwa/src/templates/templateParts/filters/FiltersTemplate.tsx +++ b/pwa/src/templates/templateParts/filters/FiltersTemplate.tsx @@ -1,23 +1,30 @@ import * as React from "react"; import * as styles from "./FiltersTemplate.module.css"; import ResultsDisplaySwitch from "../../../components/resultsDisplaySwitch/ResultsDisplaySwitch"; +import _ from "lodash"; +import qs from "qs"; import { useForm } from "react-hook-form"; import { InputText, SelectSingle } from "@conduction/components"; -import { useFiltersContext } from "../../../context/filters"; +import { IFiltersContext, defaultFiltersContext, useFiltersContext } from "../../../context/filters"; import { Button } from "@utrecht/component-library-react/dist/css-module"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faMagnifyingGlass, faSpinner } from "@fortawesome/free-solid-svg-icons"; import { generateYearsArray } from "../../../data/years"; import { TEMP_PUBLICATION_TYPES } from "../../../data/PublicationType"; import { useTranslation } from "react-i18next"; +import { filtersToUrlQueryParams } from "../../../services/filtersToQueryParams"; +import { navigate } from "gatsby"; +import { useGatsbyContext } from "../../../context/gatsby"; interface FiltersTemplateProps { isLoading: boolean; } export const FiltersTemplate: React.FC = ({ isLoading }) => { - const { filters, setFilters } = useFiltersContext(); const { t } = useTranslation(); + const { filters, setFilters } = useFiltersContext(); + const { gatsbyContext } = useGatsbyContext(); + const [queryParams, setQueryParams] = React.useState(defaultFiltersContext); const filterTimeout = React.useRef(null); const { @@ -25,6 +32,7 @@ export const FiltersTemplate: React.FC = ({ isLoading }) = register, handleSubmit, watch, + setValue, formState: { errors }, } = useForm(); @@ -33,9 +41,30 @@ export const FiltersTemplate: React.FC = ({ isLoading }) = const today = new Date(); const currentYear = today.getFullYear(); + const url = gatsbyContext.location.search; + const [, params] = url.split("?"); + const parsedParams = qs.parse(params); + + const handleSetFormValues = (params: any): void => { + const basicFields: string[] = ["_search", "category"]; + basicFields.forEach((field) => setValue(field, params[field])); + + setValue( + "year", + generateYearsArray(currentYear - 1995).find((year: any) => { + return year.after === params.Publicatiedatum?.after && year.before === params.Publicatiedatum?.before; + }), + ); + + setValue( + "category", + TEMP_PUBLICATION_TYPES.find((option) => option.value === params.Categorie?.replace(/_/g, " ")), + ); + }; + const onSubmit = (data: any) => { setFilters({ - _search: data.title, + _search: data._search, "Publicatiedatum[after]": data.year?.after, "Publicatiedatum[before]": data.year?.before, Categorie: data.category?.value, @@ -48,24 +77,45 @@ export const FiltersTemplate: React.FC = ({ isLoading }) = filterTimeout.current = setTimeout(() => onSubmit(watcher), 500); }, [watcher]); + React.useEffect(() => { + if (_.isEmpty(parsedParams)) return; + + handleSetFormValues(parsedParams); + }, []); + + React.useEffect(() => { + //Prevents loop that puts user at top of page after scroll + if (_.isEqual(filters, queryParams)) return; + + setQueryParams(filters); + navigate(`/${filtersToUrlQueryParams(filters)}`); + }, [filters]); + return (
+ { + return ( + year.after === filters["Publicatiedatum[after]"] && year.before === filters["Publicatiedatum[before]"] + ); + })} {...{ register, errors, control }} ariaLabel={t("Select year")} /> +