diff --git a/.gitignore b/.gitignore index 74dc14a..943c0a0 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,7 @@ yarn.lock # downloaded data /data -/scripts/download-hotels-as-json.*.js +/scripts/download-places-as-json.*.js # misc .DS_Store diff --git a/README.md b/README.md index 1b844d7..1a66fa6 100644 --- a/README.md +++ b/README.md @@ -119,16 +119,16 @@ Mivel a node.js szkript behúzza az `src` file-ban található JS modulokat amik Ezt legegyszerűbben egy command line-ból kiadott [Rollup parancsal](https://rollupjs.org/command-line-interface/) tudjuk megtenni (az `npx` parancsot használva nem kell a Rollupot fel installálni sem a projektbe sem a gépünkre globálisan) ```bash -npx rollup scripts/download-hotels-as-json.js --file scripts/download-hotels-as-json.bundle.js --format esm +npx rollup scripts/download-places-as-json.js --file scripts/download-places-as-json.bundle.js --format esm ``` -> ⚠️ A létrejött `scripts/download-hotels-as-json.bundle.js` nem fog bekerülni gitbe. +> ⚠️ A létrejött `scripts/download-places-as-json.bundle.js` nem fog bekerülni gitbe. ### 2) Futasuk a bundle-t Miután létrehoztuk futassuk le ```bash -node scripts/download-hotels-as-json.bundle.js +node scripts/download-places-as-json.bundle.js ``` > ⚠️ A szkript futásához minimum Node.js 18-as verzió fog kelleni ugyanis a behúzott szkriptben `fetch` API-t kell futattnunk. Ha nem akarsz globálisan frissíteni hanszálj [NVM](https://github.com/nvm-sh/nvm)-t. diff --git a/eslint.config.js b/eslint.config.js index 77cdd20..31e8a59 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,8 +1,10 @@ import { defineConfig } from "eslint/config"; import js from "@eslint/js"; +import globals from "globals"; import react from "eslint-plugin-react"; export default defineConfig([ + js.configs.recommended, { files: ["**/*.js"], plugins: { @@ -23,6 +25,10 @@ export default defineConfig([ modules: true, }, }, + globals: { + ...globals.browser, + ...globals.node, + }, }, }, ]); diff --git a/package-lock.json b/package-lock.json index c436bde..ff78c94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,8 +14,8 @@ "leaflet": "^1.9.4", "leaflet.markercluster": "^1.5.3", "mkdirp": "^3.0.1", - "react": "^19.1.0", - "react-dom": "^19.1.0", + "react": "~19.2.4", + "react-dom": "~19.2.4", "react-gtm-module": "^2.0.11", "react-helmet-async": "^2.0.5", "react-i18next": "^15.5.3", @@ -30,9 +30,11 @@ "devDependencies": { "@eslint/js": "^9.30.0", "@testing-library/react": "^16.3.0", + "@vitejs/plugin-basic-ssl": "^2.1.4", "@vitejs/plugin-react": "^4.5.2", "eslint": "^9.30.0", "eslint-plugin-react": "^7.37.5", + "globals": "^17.3.0", "jsdom": "^26.1.0", "prettier": "^2.8.4", "vite": "^6.3.5", @@ -346,6 +348,16 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/types": { "version": "7.27.7", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.7.tgz", @@ -1004,13 +1016,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -1024,19 +1029,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/@eslint/js": { "version": "9.30.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.30.0.tgz", @@ -1713,6 +1705,19 @@ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "license": "ISC" }, + "node_modules/@vitejs/plugin-basic-ssl": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.1.4.tgz", + "integrity": "sha512-HXciTXN/sDBYWgeAD4V4s0DN0g72x5mlxQhHxtYu3Tt8BLa6MzcJZUyDVFCdtjNs3bfENVHVzOsmooTVuNgAAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0" + } + }, "node_modules/@vitejs/plugin-react": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.6.0.tgz", @@ -1927,14 +1932,11 @@ } }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } + "license": "Python-2.0" }, "node_modules/aria-query": { "version": "5.3.0", @@ -3368,6 +3370,30 @@ "js-yaml": "^3.13.1" } }, + "node_modules/front-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/front-matter/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3505,13 +3531,16 @@ } }, "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz", + "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globalthis": { @@ -4476,14 +4505,13 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -4741,13 +4769,6 @@ "markdown-it": "bin/markdown-it.js" } }, - "node_modules/markdown-it/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, "node_modules/markdown-it/node_modules/entities": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", @@ -5866,24 +5887,24 @@ } }, "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", "dependencies": { - "scheduler": "^0.26.0" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.1.0" + "react": "^19.2.4" } }, "node_modules/react-fast-compare": { @@ -6305,9 +6326,9 @@ } }, "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, "node_modules/semver": { @@ -7385,6 +7406,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index a2a2f30..c8e0a25 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,8 @@ "leaflet": "^1.9.4", "leaflet.markercluster": "^1.5.3", "mkdirp": "^3.0.1", - "react": "^19.1.0", - "react-dom": "^19.1.0", + "react": "~19.2.4", + "react-dom": "~19.2.4", "react-gtm-module": "^2.0.11", "react-helmet-async": "^2.0.5", "react-i18next": "^15.5.3", @@ -38,9 +38,11 @@ "devDependencies": { "@eslint/js": "^9.30.0", "@testing-library/react": "^16.3.0", + "@vitejs/plugin-basic-ssl": "^2.1.4", "@vitejs/plugin-react": "^4.5.2", "eslint": "^9.30.0", "eslint-plugin-react": "^7.37.5", + "globals": "^17.3.0", "jsdom": "^26.1.0", "prettier": "^2.8.4", "vite": "^6.3.5", diff --git a/scripts/download-hotels-as-json.js b/scripts/download-places-as-json.js similarity index 75% rename from scripts/download-hotels-as-json.js rename to scripts/download-places-as-json.js index 3e19092..80e4919 100644 --- a/scripts/download-hotels-as-json.js +++ b/scripts/download-places-as-json.js @@ -1,10 +1,10 @@ -import loadHotelDataFromCsv from "../src/utils/load-hotel-data-from-csv.js"; +import loadPlaceDataFromCsv from "../src/utils/load-place-data-from-csv.js"; import fs from "fs"; import { mkdirp } from "mkdirp"; import path from "path"; -function downloadHotelsAsJSON() { - loadHotelDataFromCsv() +function downloadPlacesAsJSON() { + loadPlaceDataFromCsv() .then(async (data) => { const filePath = path.join(__dirname, "..", "data", "nerhotel.json"); mkdirp(path.dirname(filePath)).then((dir) => { @@ -17,4 +17,4 @@ function downloadHotelsAsJSON() { }); } -downloadHotelsAsJSON(); +downloadPlacesAsJSON(); diff --git a/src/App.jsx b/src/App.jsx index 072c7b0..bd94182 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,68 +1,25 @@ -import { useEffect, useState, useReducer } from "react"; import { BrowserRouter, Route, Routes } from "react-router"; import { Helmet, HelmetProvider } from "react-helmet-async"; +import { useTranslation } from "react-i18next"; import ErrorBoundary from "./components/ErrorBoundary"; -import AnalyticsWrapper from "./components/analytics/AnalyticsWrapper"; +import AnalyticsProvider from "./components/analytics/AnalyticsProvider"; -import { MapContext, HotelContext } from "./context"; -import reducer, { initialState } from "./reducer"; -import { useTranslation } from "react-i18next"; -import { config } from "./config"; +import { PlacesProvider } from "./context/places-provider"; -import loadHotelDataFromCsv from "./utils/load-hotel-data-from-csv"; -import LegacyHashRouteRedirect from "./components/routing/LegacyHashRouteRedirect"; +import { ScrollToTop } from "./components/navigation/ScrollToTop"; -import HotelView from "./views/HotelView"; -import MapView from "./views/MapView"; -import ContentPageView from "./views/ContentPageView"; -import PersonView from "./views/PersonView"; -import ErrorView from "./views/ErrorView"; -import PressReleasesView from "./views/PressReleasesView"; +import PlacePage from "./pages/PlacePage"; +import PlacesPage from "./pages/PlacesPage"; +import ContentPage from "./pages/ContentPage"; +import PersonPage from "./pages/PersonPage"; +import ErrorPage from "./pages/ErrorPage"; +import PressReleasesPage from "./pages/PressReleasesPage"; +import { SearchPage } from "./pages/SearchPage"; +import { LanguageLayout } from "./pages/LanguageLayout"; function App() { - const [state, dispatch] = useReducer(reducer, initialState); - const mapData = { ...state, dispatch }; - const { i18n, t } = useTranslation(); - const [hotels, setHotels] = useState([]); - - useEffect(() => { - const queryString = window.location.href.split("?")[1]; - const urlParamsObj = new URLSearchParams(queryString); - - let preferredLang; - if (urlParamsObj.has(config.locales.paramName)) { - preferredLang = urlParamsObj.get(config.locales.paramName); - } else { - preferredLang = localStorage.getItem(config.locales.paramName); - } - - if (preferredLang && config.locales.available.includes(preferredLang) && preferredLang !== i18n.options.lng) { - i18n.changeLanguage(preferredLang); - localStorage.setItem(config.locales.paramName, preferredLang); - } - }, [i18n]); - - useEffect(() => { - let isSubscribed = true; - - loadHotelDataFromCsv() - .then((data) => { - if (isSubscribed) { - setHotels(data); - } - }) - .catch((e) => { - console.error(e); - }) - .finally(() => { - dispatch({ type: "SetDataLoaded" }); - }); - - return () => { - isSubscribed = false; - }; - }, []); + const { t } = useTranslation(); return ( @@ -72,26 +29,26 @@ function App() { {t("general.tagline")} - {t("general.siteName")} - - - - - - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - - - - + + + + + + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + + ); diff --git a/src/assets/list-icon.svg b/src/assets/list-icon.svg new file mode 100644 index 0000000..3a315b4 --- /dev/null +++ b/src/assets/list-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/hotel-icon.svg b/src/assets/place-icon.svg similarity index 100% rename from src/assets/hotel-icon.svg rename to src/assets/place-icon.svg diff --git a/src/components/ErrorScreen.jsx b/src/components/ErrorScreen.jsx index 87ded76..afdd786 100644 --- a/src/components/ErrorScreen.jsx +++ b/src/components/ErrorScreen.jsx @@ -13,16 +13,17 @@ function ErrorScreen(props) {

{t("error.heading")}

{t("error.text")}

-

- {!props.standAlone ? ( - - ) : ( - - )} -

- +
+

+ {!props.standAlone ? ( + + ) : ( + + )} +

+
diff --git a/src/components/FilterControl.jsx b/src/components/FilterControl.jsx deleted file mode 100644 index 1103144..0000000 --- a/src/components/FilterControl.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import { FaShoppingBasket, FaSpa, FaWineBottle } from "react-icons/fa"; -import { GiCampingTent } from "react-icons/gi"; -import { MdCasino, MdLocalBar, MdLocalCafe, MdOutlineRestaurant, MdSelectAll } from "react-icons/md"; -import { TbHotelService } from "react-icons/tb"; -import styles from "./FilterControl.module.css"; -import { controlButton, button } from "../css/map-list-opener.module.css"; -import { FaFilter } from "react-icons/fa6"; -import { useState, useCallback } from "react"; - -function FilterControl({ language, filterType, setFilterType }) { - const [filterOpen, setFilterOpen] = useState(false); - const size = 16; - const options = [ - { type: "mind", category: "all", category_de: "Alles", icon: }, - { type: "bár", category: "bar", category_de: "Bar", icon: }, - { type: "borászat", category: "winery", category_de: "Weingut", icon: }, - { type: "étterem", category: "restaurant", category_de: "Restaurant", icon: }, - { type: "kávézó", category: "café", category_de: "Cafe", icon: }, - { type: "spa&sport", category: "spa&sport", category_de: "Spa und Sport", icon: }, - { type: "kaszinó", category: "casino", category_de: "Kasino", icon: }, - { type: "kemping", category: "camping", category_de: "Camping", icon: }, - { type: "kiskereskedelem", category: "retail", category_de: "Einzelhandel", icon: }, - { type: "szálloda", category: "hotel", category_de: "Hotel", icon: }, - ]; - const toggleFilterOpen = useCallback(() => { - setFilterOpen((prevState) => !prevState); - }, []); - - function getTranslation(language, option) { - switch (language) { - case "hu": - return option.type; - case "en": - return option.category; - case "de": - return option.category_de; - default: - return option.type; - } - } - - return ( - <> - -
-
- {options.map((option, i) => ( -
{ - setFilterType(option.type); - setFilterOpen(false); - }} - > - - {getTranslation(language, option)} -
- ))} -
-
- - ); -} - -export default FilterControl; diff --git a/src/components/LangSwitch.jsx b/src/components/LangSwitch.jsx index 44c013b..9cf0b05 100644 --- a/src/components/LangSwitch.jsx +++ b/src/components/LangSwitch.jsx @@ -1,11 +1,15 @@ +import { useTranslation } from "react-i18next"; + import styles from "./LangSwitch.module.css"; function LangSwitch(props) { + const { t } = useTranslation(); + return (