diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json
index 0df797a3..26e4203e 100644
--- a/frontend/.eslintrc.json
+++ b/frontend/.eslintrc.json
@@ -4,8 +4,7 @@
"plugin:react/recommended",
"plugin:prettier/recommended",
"airbnb",
- "prettier",
- "wikimedia/client/es6"],
+ "prettier"],
"plugins": ["react", "prettier", "eslint-plugin-react", "eslint-plugin-react-hooks"],
"parser": "@babel/eslint-parser",
"parserOptions": {
@@ -28,17 +27,29 @@
"no-console": "warn",
"func-names": "error",
"no-process-exit": "error",
+ "no-restricted-syntax": [
+ "error",
+ {
+ "selector": "ForInStatement",
+ "message": "for..in iterates over the prototype chain; use Object.{keys,values,entries} instead."
+ },
+ {
+ "selector": "LabeledStatement",
+ "message": "Labels are a form of GOTO; use functions or early returns instead."
+ },
+ {
+ "selector": "WithStatement",
+ "message": "`with` is disallowed in strict mode because it makes scope ambiguous."
+ }
+ ],
"object-shorthand": "error",
"template-curly-spacing": "off",
"computed-property-spacing": "off",
"arrow-parens": "off",
"class-methods-use-this": "error",
"import/prefer-default-export": "error",
- "react/require-default-props": "error",
+ "react/require-default-props": ["error", { "functions": "defaultArguments" }],
"comma-dangle": "off",
- "es-x/no-rest-spread-properties": "off",
- "es-x/no-trailing-function-commas": "off",
- "es-x/no-global-this": "off",
"indent": ["error", 4],
"implicit-arrow-linebreak": "off",
"react/function-component-definition": [
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index bc66b9a1..5e22d659 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -13,6 +13,7 @@
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.6",
"@mui/material": "^5.14.6",
+ "@react-leaflet/core": "^2.1.0",
"axios": "^1.7.6",
"browser-image-compression": "^2.0.2",
"i18next": "^23.15.1",
@@ -43,7 +44,6 @@
"eslint": "^8.30.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^8.10.0",
- "eslint-config-wikimedia": "^0.28.2",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-prettier": "^4.2.1",
@@ -2273,38 +2273,6 @@
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
"license": "MIT"
},
- "node_modules/@es-joy/jsdoccomment": {
- "version": "0.43.1",
- "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.43.1.tgz",
- "integrity": "sha512-I238eDtOolvCuvtxrnqtlBaw0BwdQuYqK7eA6XIonicMdOOOb75mqdIzkGDUbS04+1Di007rgm9snFRNeVrOog==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/eslint": "^8.56.5",
- "@types/estree": "^1.0.5",
- "@typescript-eslint/types": "^7.2.0",
- "comment-parser": "1.4.1",
- "esquery": "^1.5.0",
- "jsdoc-type-pratt-parser": "~4.0.0"
- },
- "engines": {
- "node": ">=16"
- }
- },
- "node_modules/@es-joy/jsdoccomment/node_modules/@typescript-eslint/types": {
- "version": "7.18.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz",
- "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || >=20.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
"node_modules/@eslint-community/eslint-utils": {
"version": "4.9.1",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
@@ -3385,13 +3353,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@mdn/browser-compat-data": {
- "version": "5.7.6",
- "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.7.6.tgz",
- "integrity": "sha512-7xdrMX0Wk7grrTZQwAoy1GkvPMFoizStUoL+VmtUkAxegbCCec+3FKwOM6yc/uGU5+BEczQHXAlWiqvM8JeENg==",
- "dev": true,
- "license": "CC0-1.0"
- },
"node_modules/@mui/core-downloads-tracker": {
"version": "5.18.0",
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.18.0.tgz",
@@ -4151,17 +4112,6 @@
"@types/node": "*"
}
},
- "node_modules/@types/eslint": {
- "version": "8.56.12",
- "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz",
- "integrity": "sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/estree": "*",
- "@types/json-schema": "*"
- }
- },
"node_modules/@types/estree": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
@@ -4322,13 +4272,6 @@
"undici-types": "~8.3.0"
}
},
- "node_modules/@types/normalize-package-data": {
- "version": "2.4.4",
- "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
- "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@types/parse-json": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
@@ -4485,212 +4428,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@typescript-eslint/project-service": {
- "version": "8.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.62.0.tgz",
- "integrity": "sha512-wexnCqiTg7BOGtbLDftYpRWlmLq4xfoMd7BKFR6Y75sZS3QmRKLdN3yWLhmIYgqMmP/OXWpj3H8odkb5nGURCQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.62.0",
- "@typescript-eslint/types": "^8.62.0",
- "debug": "^4.4.3"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
- "node_modules/@typescript-eslint/scope-manager": {
- "version": "8.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.62.0.tgz",
- "integrity": "sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.62.0",
- "@typescript-eslint/visitor-keys": "8.62.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.0.tgz",
- "integrity": "sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
- "node_modules/@typescript-eslint/types": {
- "version": "8.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.0.tgz",
- "integrity": "sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.0.tgz",
- "integrity": "sha512-+hVbNxtW64pIcZWDPGbyaKF7vp2IBTVY5ma1blwwksrjdsbdqqEKvJWMGbBofei4F6Dovx1M0RJgoFeNu2279A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/project-service": "8.62.0",
- "@typescript-eslint/tsconfig-utils": "8.62.0",
- "@typescript-eslint/types": "8.62.0",
- "@typescript-eslint/visitor-keys": "8.62.0",
- "debug": "^4.4.3",
- "minimatch": "^10.2.2",
- "semver": "^7.7.3",
- "tinyglobby": "^0.2.15",
- "ts-api-utils": "^2.5.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
- "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "18 || 20 || >=22"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
- "version": "5.0.6",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
- "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^4.0.2"
- },
- "engines": {
- "node": "18 || 20 || >=22"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
- "version": "10.2.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
- "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "brace-expansion": "^5.0.5"
- },
- "engines": {
- "node": "18 || 20 || >=22"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
- "version": "7.8.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
- "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@typescript-eslint/utils": {
- "version": "8.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.62.0.tgz",
- "integrity": "sha512-82r66fi9zYwZ+mTq3vKgwjbZ1PVk/DJzrXFLpG6RnBbdvH8TEGVHIs9H4d2drhkOzf0syZuD/OZvvlu6GDbP4g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.9.1",
- "@typescript-eslint/scope-manager": "8.62.0",
- "@typescript-eslint/types": "8.62.0",
- "@typescript-eslint/typescript-estree": "8.62.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
- "node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.62.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.0.tgz",
- "integrity": "sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.62.0",
- "eslint-visitor-keys": "^5.0.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
- "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^20.19.0 || ^22.13.0 || >=24"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
"node_modules/@ungap/structured-clone": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.2.tgz",
@@ -5422,16 +5159,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
- "node_modules/are-docs-informative": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz",
- "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14"
- }
- },
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -5631,16 +5358,6 @@
"node": ">=12.0.0"
}
},
- "node_modules/ast-metadata-inferer": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/ast-metadata-inferer/-/ast-metadata-inferer-0.8.1.tgz",
- "integrity": "sha512-ht3Dm6Zr7SXv6t1Ra6gFo0+kLDglHGrEbYihTkcycrbHw7WCcuhBzPlJYHEsIpycaUwzsJHje+vUcxXUX4ztTA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@mdn/browser-compat-data": "^5.6.19"
- }
- },
"node_modules/ast-types-flow": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
@@ -5991,13 +5708,6 @@
"multicast-dns": "^7.2.5"
}
},
- "node_modules/boolbase": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
- "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/brace-expansion": {
"version": "1.1.15",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
@@ -6065,13 +5775,6 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
- "node_modules/browserslist-config-wikimedia": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/browserslist-config-wikimedia/-/browserslist-config-wikimedia-0.7.0.tgz",
- "integrity": "sha512-CTa0lv78dXKEgrYsOLCkqO+9UUS3CV9MWEOYHcymgEvx4mYxB80sCoKRCR7wW2SOMNxjaP9hohrZripjnKuRTA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/bser": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
@@ -6089,19 +5792,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/builtin-modules": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
- "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/bundle-name": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
@@ -6334,29 +6024,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/clean-regexp": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz",
- "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "escape-string-regexp": "^1.0.5"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/clean-regexp/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.8.0"
- }
- },
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -6500,16 +6167,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/comment-parser": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz",
- "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 12.0.0"
- }
- },
"node_modules/commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
@@ -7065,16 +6722,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/diff-sequences": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz",
- "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
- }
- },
"node_modules/dns-packet": {
"version": "5.6.1",
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz",
@@ -7529,35 +7176,6 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/eslint-compat-utils": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz",
- "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "semver": "^7.5.4"
- },
- "engines": {
- "node": ">=12"
- },
- "peerDependencies": {
- "eslint": ">=6.0.0"
- }
- },
- "node_modules/eslint-compat-utils/node_modules/semver": {
- "version": "7.8.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
- "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/eslint-config-airbnb": {
"version": "19.0.4",
"resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz",
@@ -7613,32 +7231,6 @@
"eslint": ">=7.0.0"
}
},
- "node_modules/eslint-config-wikimedia": {
- "version": "0.28.2",
- "resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.28.2.tgz",
- "integrity": "sha512-5+rdnT7wH1gpKAO6tHYThg78eMhZMruJzvqku3Y5iaEY/A7kSKLFpA/vOj/snys9fKjDHC9BXmArQh+agkOoJQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "browserslist-config-wikimedia": "^0.7.0",
- "eslint": "^8.57.0",
- "eslint-plugin-compat": "^4.2.0",
- "eslint-plugin-es-x": "^7.6.0",
- "eslint-plugin-jest": "^28.5.0",
- "eslint-plugin-jsdoc": "48.2.5",
- "eslint-plugin-json-es": "^1.6.0",
- "eslint-plugin-mediawiki": "^0.7.0",
- "eslint-plugin-mocha": "^10.4.3",
- "eslint-plugin-n": "^17.7.0",
- "eslint-plugin-no-jquery": "^3.0.1",
- "eslint-plugin-qunit": "^8.1.1",
- "eslint-plugin-security": "^1.7.1",
- "eslint-plugin-unicorn": "^53.0.0",
- "eslint-plugin-vue": "^9.26.0",
- "eslint-plugin-wdio": "^8.24.12",
- "eslint-plugin-yml": "^1.14.0"
- }
- },
"node_modules/eslint-import-resolver-node": {
"version": "0.3.10",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz",
@@ -7713,116 +7305,10 @@
"ms": "^2.1.1"
}
},
- "node_modules/eslint-plugin-compat": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-4.2.0.tgz",
- "integrity": "sha512-RDKSYD0maWy5r7zb5cWQS+uSPc26mgOzdORJ8hxILmWM7S/Ncwky7BcAtXVY5iRbKjBdHsWU8Yg7hfoZjtkv7w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@mdn/browser-compat-data": "^5.3.13",
- "ast-metadata-inferer": "^0.8.0",
- "browserslist": "^4.21.10",
- "caniuse-lite": "^1.0.30001524",
- "find-up": "^5.0.0",
- "lodash.memoize": "^4.1.2",
- "semver": "^7.5.4"
- },
- "engines": {
- "node": ">=14.x"
- },
- "peerDependencies": {
- "eslint": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
- }
- },
- "node_modules/eslint-plugin-compat/node_modules/find-up": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
- "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "locate-path": "^6.0.0",
- "path-exists": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/eslint-plugin-compat/node_modules/locate-path": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
- "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-locate": "^5.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/eslint-plugin-compat/node_modules/p-locate": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
- "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-limit": "^3.0.2"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/eslint-plugin-compat/node_modules/semver": {
- "version": "7.8.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
- "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/eslint-plugin-es-x": {
- "version": "7.8.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz",
- "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==",
- "dev": true,
- "funding": [
- "https://github.com/sponsors/ota-meshi",
- "https://opencollective.com/eslint"
- ],
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.1.2",
- "@eslint-community/regexpp": "^4.11.0",
- "eslint-compat-utils": "^0.5.1"
- },
- "engines": {
- "node": "^14.18.0 || >=16.0.0"
- },
- "peerDependencies": {
- "eslint": ">=8"
- }
- },
- "node_modules/eslint-plugin-import": {
- "version": "2.32.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
- "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
+ "node_modules/eslint-plugin-import": {
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
+ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7876,96 +7362,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/eslint-plugin-jest": {
- "version": "28.14.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.14.0.tgz",
- "integrity": "sha512-P9s/qXSMTpRTerE2FQ0qJet2gKbcGyFTPAJipoKxmWqR6uuFqIqk8FuEfg5yBieOezVrEfAMZrEwJ6yEp+1MFQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0"
- },
- "engines": {
- "node": "^16.10.0 || ^18.12.0 || >=20.0.0"
- },
- "peerDependencies": {
- "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0 || ^8.0.0",
- "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0",
- "jest": "*"
- },
- "peerDependenciesMeta": {
- "@typescript-eslint/eslint-plugin": {
- "optional": true
- },
- "jest": {
- "optional": true
- }
- }
- },
- "node_modules/eslint-plugin-jsdoc": {
- "version": "48.2.5",
- "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.2.5.tgz",
- "integrity": "sha512-ZeTfKV474W1N9niWfawpwsXGu+ZoMXu4417eBROX31d7ZuOk8zyG66SO77DpJ2+A9Wa2scw/jRqBPnnQo7VbcQ==",
- "dev": true,
- "license": "BSD-3-Clause",
- "dependencies": {
- "@es-joy/jsdoccomment": "~0.43.0",
- "are-docs-informative": "^0.0.2",
- "comment-parser": "1.4.1",
- "debug": "^4.3.4",
- "escape-string-regexp": "^4.0.0",
- "esquery": "^1.5.0",
- "is-builtin-module": "^3.2.1",
- "semver": "^7.6.1",
- "spdx-expression-parse": "^4.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "peerDependencies": {
- "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0"
- }
- },
- "node_modules/eslint-plugin-jsdoc/node_modules/semver": {
- "version": "7.8.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
- "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/eslint-plugin-json-es": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-json-es/-/eslint-plugin-json-es-1.6.0.tgz",
- "integrity": "sha512-xVn6hufGQH1Aa+yqOhQ43Cq28GuitTcMpQh+uaUh27U2qnVLBrvkN+2xQSnv6zpdLEPS35JCNhq4kvhR+PQCgw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "eslint-visitor-keys": "^3.3.0",
- "espree": "^9.3.1"
- },
- "peerDependencies": {
- "eslint": ">= 7"
- }
- },
- "node_modules/eslint-plugin-json-es/node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
"node_modules/eslint-plugin-jsx-a11y": {
"version": "6.10.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz",
@@ -8006,100 +7402,6 @@
"node": ">= 0.4"
}
},
- "node_modules/eslint-plugin-mediawiki": {
- "version": "0.7.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-mediawiki/-/eslint-plugin-mediawiki-0.7.1.tgz",
- "integrity": "sha512-C/OfySlkkjHJrZl1ieHlL8AyebgrppyX49DUYEVXk7n0qu7dD/vuOnzeQz28H2MkUVj3GnoFSUxqbczG6IUwwg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "upath": "^2.0.1"
- },
- "peerDependencies": {
- "eslint": ">=5.0.0"
- }
- },
- "node_modules/eslint-plugin-mocha": {
- "version": "10.5.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-10.5.0.tgz",
- "integrity": "sha512-F2ALmQVPT1GoP27O1JTZGrV9Pqg8k79OeIuvw63UxMtQKREZtmkK1NFgkZQ2TW7L2JSSFKHFPTtHu5z8R9QNRw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "eslint-utils": "^3.0.0",
- "globals": "^13.24.0",
- "rambda": "^7.4.0"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "eslint": ">=7.0.0"
- }
- },
- "node_modules/eslint-plugin-n": {
- "version": "17.24.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.24.0.tgz",
- "integrity": "sha512-/gC7/KAYmfNnPNOb3eu8vw+TdVnV0zhdQwexsw6FLXbhzroVj20vRn2qL8lDWDGnAQ2J8DhdfvXxX9EoxvERvw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.5.0",
- "enhanced-resolve": "^5.17.1",
- "eslint-plugin-es-x": "^7.8.0",
- "get-tsconfig": "^4.8.1",
- "globals": "^15.11.0",
- "globrex": "^0.1.2",
- "ignore": "^5.3.2",
- "semver": "^7.6.3",
- "ts-declaration-location": "^1.0.6"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- },
- "peerDependencies": {
- "eslint": ">=8.23.0"
- }
- },
- "node_modules/eslint-plugin-n/node_modules/globals": {
- "version": "15.15.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
- "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/eslint-plugin-n/node_modules/semver": {
- "version": "7.8.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
- "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/eslint-plugin-no-jquery": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-no-jquery/-/eslint-plugin-no-jquery-3.1.1.tgz",
- "integrity": "sha512-LTLO3jH/Tjr1pmxCEqtV6qmt+OChv8La4fwgG470JRpgxyFF4NOzoC9CRy92GIWD3Yjl0qLEgPmD2FLQWcNEjg==",
- "dev": true,
- "license": "MIT",
- "peerDependencies": {
- "eslint": ">=8.0.0"
- }
- },
"node_modules/eslint-plugin-prettier": {
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.5.tgz",
@@ -8122,23 +7424,6 @@
}
}
},
- "node_modules/eslint-plugin-qunit": {
- "version": "8.2.6",
- "resolved": "https://registry.npmjs.org/eslint-plugin-qunit/-/eslint-plugin-qunit-8.2.6.tgz",
- "integrity": "sha512-S1jC/DIW9J8VtNX4uG1vlf5FZVrfQFlcuiYmvTHR2IICUhubHqpWA5o+qS1tujh+81Gs39omKV2D4OXfbSJE5g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.4.0",
- "requireindex": "^1.2.0"
- },
- "engines": {
- "node": "^16.0.0 || ^18.0.0 || >=20.0.0"
- },
- "peerDependencies": {
- "eslint": ">=8.38.0"
- }
- },
"node_modules/eslint-plugin-react": {
"version": "7.37.5",
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
@@ -8222,230 +7507,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/eslint-plugin-security": {
- "version": "1.7.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.7.1.tgz",
- "integrity": "sha512-sMStceig8AFglhhT2LqlU5r+/fn9OwsA72O5bBuQVTssPCdQAOQzL+oMn/ZcpeUY6KcNfLJArgcrsSULNjYYdQ==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "safe-regex": "^2.1.1"
- }
- },
- "node_modules/eslint-plugin-unicorn": {
- "version": "53.0.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-53.0.0.tgz",
- "integrity": "sha512-kuTcNo9IwwUCfyHGwQFOK/HjJAYzbODHN3wP0PgqbW+jbXqpNWxNVpVhj2tO9SixBwuAdmal8rVcWKBxwFnGuw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/helper-validator-identifier": "^7.24.5",
- "@eslint-community/eslint-utils": "^4.4.0",
- "@eslint/eslintrc": "^3.0.2",
- "ci-info": "^4.0.0",
- "clean-regexp": "^1.0.0",
- "core-js-compat": "^3.37.0",
- "esquery": "^1.5.0",
- "indent-string": "^4.0.0",
- "is-builtin-module": "^3.2.1",
- "jsesc": "^3.0.2",
- "pluralize": "^8.0.0",
- "read-pkg-up": "^7.0.1",
- "regexp-tree": "^0.1.27",
- "regjsparser": "^0.10.0",
- "semver": "^7.6.1",
- "strip-indent": "^3.0.0"
- },
- "engines": {
- "node": ">=18.18"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1"
- },
- "peerDependencies": {
- "eslint": ">=8.56.0"
- }
- },
- "node_modules/eslint-plugin-unicorn/node_modules/@eslint/eslintrc": {
- "version": "3.3.5",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz",
- "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ajv": "^6.14.0",
- "debug": "^4.3.2",
- "espree": "^10.0.1",
- "globals": "^14.0.0",
- "ignore": "^5.2.0",
- "import-fresh": "^3.2.1",
- "js-yaml": "^4.1.1",
- "minimatch": "^3.1.5",
- "strip-json-comments": "^3.1.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint-plugin-unicorn/node_modules/eslint-visitor-keys": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
- "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint-plugin-unicorn/node_modules/espree": {
- "version": "10.4.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
- "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "acorn": "^8.15.0",
- "acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^4.2.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/eslint-plugin-unicorn/node_modules/globals": {
- "version": "14.0.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
- "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/eslint-plugin-unicorn/node_modules/semver": {
- "version": "7.8.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
- "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/eslint-plugin-vue": {
- "version": "9.33.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.33.0.tgz",
- "integrity": "sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.4.0",
- "globals": "^13.24.0",
- "natural-compare": "^1.4.0",
- "nth-check": "^2.1.1",
- "postcss-selector-parser": "^6.0.15",
- "semver": "^7.6.3",
- "vue-eslint-parser": "^9.4.3",
- "xml-name-validator": "^4.0.0"
- },
- "engines": {
- "node": "^14.17.0 || >=16.0.0"
- },
- "peerDependencies": {
- "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0"
- }
- },
- "node_modules/eslint-plugin-vue/node_modules/semver": {
- "version": "7.8.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
- "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/eslint-plugin-wdio": {
- "version": "8.37.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-wdio/-/eslint-plugin-wdio-8.37.0.tgz",
- "integrity": "sha512-X217zXxSqj1IPWu3bxN7D/xEUmNk7Jg5lBf2JwYH3mCogaqL2tnHZnwt0EQ5D9oEejfEl2+4zqHSzhXq1X7F2A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^16.13 || >=18"
- }
- },
- "node_modules/eslint-plugin-yml": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-yml/-/eslint-plugin-yml-1.19.1.tgz",
- "integrity": "sha512-bYkOxyEiXh9WxUhVYPELdSHxGG5pOjCSeJOVkfdIyj6tuiHDxrES2WAW1dBxn3iaZQey57XflwLtCYRcNPOiOg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "^4.3.2",
- "diff-sequences": "^27.5.1",
- "escape-string-regexp": "4.0.0",
- "eslint-compat-utils": "^0.6.0",
- "natural-compare": "^1.4.0",
- "yaml-eslint-parser": "^1.2.1"
- },
- "engines": {
- "node": "^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/ota-meshi"
- },
- "peerDependencies": {
- "eslint": ">=6.0.0"
- }
- },
- "node_modules/eslint-plugin-yml/node_modules/eslint-compat-utils": {
- "version": "0.6.5",
- "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.6.5.tgz",
- "integrity": "sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "semver": "^7.5.4"
- },
- "engines": {
- "node": ">=12"
- },
- "peerDependencies": {
- "eslint": ">=6.0.0"
- }
- },
- "node_modules/eslint-plugin-yml/node_modules/semver": {
- "version": "7.8.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
- "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@@ -8470,25 +7531,6 @@
"node": ">=4.0"
}
},
- "node_modules/eslint-utils": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
- "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "eslint-visitor-keys": "^2.0.0"
- },
- "engines": {
- "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/mysticatea"
- },
- "peerDependencies": {
- "eslint": ">=5"
- }
- },
"node_modules/eslint-visitor-keys": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
@@ -8883,24 +7925,6 @@
"bser": "2.1.1"
}
},
- "node_modules/fdir": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
- "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
- }
- },
"node_modules/file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -9295,19 +8319,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/get-tsconfig": {
- "version": "4.14.0",
- "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz",
- "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "resolve-pkg-maps": "^1.0.0"
- },
- "funding": {
- "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
- }
- },
"node_modules/glob": {
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
@@ -9419,13 +8430,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/globrex": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
- "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/goober": {
"version": "2.1.19",
"resolved": "https://registry.npmjs.org/goober/-/goober-2.1.19.tgz",
@@ -9574,13 +8578,6 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"license": "MIT"
},
- "node_modules/hosted-git-info": {
- "version": "2.8.9",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
- "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/hpack.js": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
@@ -10063,22 +9060,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-builtin-module": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
- "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "builtin-modules": "^3.3.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/is-callable": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
@@ -11645,16 +10626,6 @@
"js-yaml": "bin/js-yaml.js"
}
},
- "node_modules/jsdoc-type-pratt-parser": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz",
- "integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12.0.0"
- }
- },
"node_modules/jsdom": {
"version": "26.1.0",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz",
@@ -11949,13 +10920,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/lodash.memoize": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
- "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -12483,29 +11447,6 @@
"node": ">=18"
}
},
- "node_modules/normalize-package-data": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
- "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "hosted-git-info": "^2.1.4",
- "resolve": "^1.10.0",
- "semver": "2 || 3 || 4 || 5",
- "validate-npm-package-license": "^3.0.1"
- }
- },
- "node_modules/normalize-package-data/node_modules/semver": {
- "version": "5.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
- "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver"
- }
- },
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -12526,20 +11467,7 @@
"path-key": "^3.0.0"
},
"engines": {
- "node": ">=8"
- }
- },
- "node_modules/nth-check": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
- "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "boolbase": "^1.0.0"
- },
- "funding": {
- "url": "https://github.com/fb55/nth-check?sponsor=1"
+ "node": ">=8"
}
},
"node_modules/nwsapi": {
@@ -13051,16 +11979,6 @@
"node": ">=16.0.0"
}
},
- "node_modules/pluralize": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
- "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/possible-typed-array-names": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
@@ -13191,20 +12109,6 @@
"postcss": "^8.1.0"
}
},
- "node_modules/postcss-selector-parser": {
- "version": "6.1.4",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz",
- "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cssesc": "^3.0.0",
- "util-deprecate": "^1.0.2"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
@@ -13430,13 +12334,6 @@
],
"license": "MIT"
},
- "node_modules/rambda": {
- "version": "7.5.0",
- "resolved": "https://registry.npmjs.org/rambda/-/rambda-7.5.0.tgz",
- "integrity": "sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -13625,60 +12522,6 @@
"react-dom": ">=16.6.0"
}
},
- "node_modules/read-pkg": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
- "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/normalize-package-data": "^2.4.0",
- "normalize-package-data": "^2.5.0",
- "parse-json": "^5.0.0",
- "type-fest": "^0.6.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/read-pkg-up": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
- "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "find-up": "^4.1.0",
- "read-pkg": "^5.2.0",
- "type-fest": "^0.8.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/read-pkg-up/node_modules/type-fest": {
- "version": "0.8.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
- "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
- "dev": true,
- "license": "(MIT OR CC0-1.0)",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/read-pkg/node_modules/type-fest": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
- "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
- "dev": true,
- "license": "(MIT OR CC0-1.0)",
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
@@ -13797,16 +12640,6 @@
"node": ">=4"
}
},
- "node_modules/regexp-tree": {
- "version": "0.1.27",
- "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz",
- "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "regexp-tree": "bin/regexp-tree"
- }
- },
"node_modules/regexp.prototype.flags": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
@@ -13866,28 +12699,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/regjsparser": {
- "version": "0.10.0",
- "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.10.0.tgz",
- "integrity": "sha512-qx+xQGZVsy55CH0a1hiVwHmqjLryfh7wQyF5HO07XJ9f7dQMY/gPQHhlyDkIzJKC+x2fUCpCcUODUUUFrm7SHA==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "jsesc": "~0.5.0"
- },
- "bin": {
- "regjsparser": "bin/parser"
- }
- },
- "node_modules/regjsparser/node_modules/jsesc": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
- "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==",
- "dev": true,
- "bin": {
- "jsesc": "bin/jsesc"
- }
- },
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -13908,16 +12719,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/requireindex": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz",
- "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.5"
- }
- },
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
@@ -13969,16 +12770,6 @@
"node": ">=8"
}
},
- "node_modules/resolve-pkg-maps": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
- "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
- }
- },
"node_modules/retry": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
@@ -14141,16 +12932,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/safe-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz",
- "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "regexp-tree": "~0.1.1"
- }
- },
"node_modules/safe-regex-test": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
@@ -14639,53 +13420,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/spdx-correct": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
- "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "spdx-expression-parse": "^3.0.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "node_modules/spdx-correct/node_modules/spdx-expression-parse": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
- "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "spdx-exceptions": "^2.1.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "node_modules/spdx-exceptions": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
- "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
- "dev": true,
- "license": "CC-BY-3.0"
- },
- "node_modules/spdx-expression-parse": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz",
- "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "spdx-exceptions": "^2.1.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "node_modules/spdx-license-ids": {
- "version": "3.0.23",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz",
- "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==",
- "dev": true,
- "license": "CC0-1.0"
- },
"node_modules/spdy": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz",
@@ -15430,23 +14164,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/tinyglobby": {
- "version": "0.2.17",
- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
- "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fdir": "^6.5.0",
- "picomatch": "^4.0.4"
- },
- "engines": {
- "node": ">=12.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/SuperchupuDev"
- }
- },
"node_modules/tldts": {
"version": "6.1.86",
"resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
@@ -15540,42 +14257,6 @@
"tslib": "2"
}
},
- "node_modules/ts-api-utils": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
- "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18.12"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4"
- }
- },
- "node_modules/ts-declaration-location": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/ts-declaration-location/-/ts-declaration-location-1.0.7.tgz",
- "integrity": "sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==",
- "dev": true,
- "funding": [
- {
- "type": "ko-fi",
- "url": "https://ko-fi.com/rebeccastevens"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/ts-declaration-location"
- }
- ],
- "license": "BSD-3-Clause",
- "dependencies": {
- "picomatch": "^4.0.2"
- },
- "peerDependencies": {
- "typescript": ">=4.0.0"
- }
- },
"node_modules/tsconfig-paths": {
"version": "3.15.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
@@ -15771,8 +14452,8 @@
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
- "devOptional": true,
"license": "Apache-2.0",
+ "optional": true,
"peer": true,
"bin": {
"tsc": "bin/tsc",
@@ -15926,17 +14607,6 @@
"@unrs/resolver-binding-win32-x64-msvc": "1.12.2"
}
},
- "node_modules/upath": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz",
- "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4",
- "yarn": "*"
- }
- },
"node_modules/update-browserslist-db": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
@@ -16077,28 +14747,6 @@
"node": ">=10.12.0"
}
},
- "node_modules/validate-npm-package-license": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
- "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "spdx-correct": "^3.0.0",
- "spdx-expression-parse": "^3.0.0"
- }
- },
- "node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
- "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "spdx-exceptions": "^2.1.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@@ -16118,74 +14766,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/vue-eslint-parser": {
- "version": "9.4.3",
- "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz",
- "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "^4.3.4",
- "eslint-scope": "^7.1.1",
- "eslint-visitor-keys": "^3.3.0",
- "espree": "^9.3.1",
- "esquery": "^1.4.0",
- "lodash": "^4.17.21",
- "semver": "^7.3.6"
- },
- "engines": {
- "node": "^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/mysticatea"
- },
- "peerDependencies": {
- "eslint": ">=6.0.0"
- }
- },
- "node_modules/vue-eslint-parser/node_modules/eslint-scope": {
- "version": "7.2.2",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
- "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "esrecurse": "^4.3.0",
- "estraverse": "^5.2.0"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/vue-eslint-parser/node_modules/semver": {
- "version": "7.8.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
- "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/w3c-xmlserializer": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
@@ -17027,16 +15607,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/xml-name-validator": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
- "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/xmlchars": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
@@ -17070,52 +15640,6 @@
"node": ">= 6"
}
},
- "node_modules/yaml-eslint-parser": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/yaml-eslint-parser/-/yaml-eslint-parser-1.3.2.tgz",
- "integrity": "sha512-odxVsHAkZYYglR30aPYRY4nUGJnoJ2y1ww2HDvZALo0BDETv9kWbi16J52eHs+PWRNmF4ub6nZqfVOeesOvntg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "eslint-visitor-keys": "^3.0.0",
- "yaml": "^2.0.0"
- },
- "engines": {
- "node": "^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/ota-meshi"
- }
- },
- "node_modules/yaml-eslint-parser/node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/yaml-eslint-parser/node_modules/yaml": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
- "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "yaml": "bin.mjs"
- },
- "engines": {
- "node": ">= 14.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/eemeli"
- }
- },
"node_modules/yargs": {
"version": "17.7.3",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index b9a7bce5..f5d15adc 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -15,10 +15,10 @@
"watch": "webpack --progress -d --config webpack.config.js --watch",
"test": "jest --config jest.config.js",
"coverage": "jest --coverage --config jest.config.js",
- "lint-fix": "eslint src/**/*.jsx tests/**/*.jsx --fix",
- "lint": "eslint src/**/*.jsx tests/**/*.jsx",
- "prettier-fix": "prettier --write \"src/**/*.jsx\" \"tests/**/*.jsx\"",
- "prettier": "prettier --check \"src/**/*.jsx\" \"tests/**/*.jsx\""
+ "lint-fix": "eslint \"src/**/*.{js,jsx}\" \"tests/**/*.{js,jsx}\" --fix",
+ "lint": "eslint \"src/**/*.{js,jsx}\" \"tests/**/*.{js,jsx}\"",
+ "prettier-fix": "prettier --write \"src/**/*.{js,jsx}\" \"tests/**/*.{js,jsx}\"",
+ "prettier": "prettier --check \"src/**/*.{js,jsx}\" \"tests/**/*.{js,jsx}\""
},
"files": [
"dist"
@@ -42,7 +42,6 @@
"eslint": "^8.30.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^8.10.0",
- "eslint-config-wikimedia": "^0.28.2",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-prettier": "^4.2.1",
@@ -64,6 +63,7 @@
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.6",
"@mui/material": "^5.14.6",
+ "@react-leaflet/core": "^2.1.0",
"axios": "^1.7.6",
"browser-image-compression": "^2.0.2",
"i18next": "^23.15.1",
diff --git a/frontend/src/components/Categories/CategoriesContext.jsx b/frontend/src/components/Categories/CategoriesContext.jsx
index c188869a..624bbce4 100644
--- a/frontend/src/components/Categories/CategoriesContext.jsx
+++ b/frontend/src/components/Categories/CategoriesContext.jsx
@@ -1,5 +1,6 @@
import React, { useState, useContext, createContext, useMemo, useEffect, useCallback } from 'react';
-import { httpService } from '../../services/http/httpService';
+import PropTypes from 'prop-types';
+import httpService from '../../services/http/httpService';
/**
* React Context for managing categories state across the application.
@@ -67,6 +68,10 @@ export const CategoriesProvider = ({ children }) => {
return {children};
};
+CategoriesProvider.propTypes = {
+ children: PropTypes.node.isRequired,
+};
+
/**
* Custom hook to access categories context.
* Must be used within a CategoriesProvider component.
diff --git a/frontend/src/components/FiltersForm/FiltersForm.jsx b/frontend/src/components/FiltersForm/FiltersForm.jsx
index 603135d8..3b9ab257 100644
--- a/frontend/src/components/FiltersForm/FiltersForm.jsx
+++ b/frontend/src/components/FiltersForm/FiltersForm.jsx
@@ -239,7 +239,7 @@ const LoadingSkeleton = () => (
>
);
-export const FiltersForm = () => {
+const FiltersForm = () => {
const { t } = useTranslation();
const {
categories: selectedFilters,
@@ -456,3 +456,5 @@ export const FiltersForm = () => {
);
};
+
+export default FiltersForm;
diff --git a/frontend/src/components/FiltersForm/FiltersTooltip.jsx b/frontend/src/components/FiltersForm/FiltersTooltip.jsx
index dd5b34bf..1081115e 100644
--- a/frontend/src/components/FiltersForm/FiltersTooltip.jsx
+++ b/frontend/src/components/FiltersForm/FiltersTooltip.jsx
@@ -27,37 +27,35 @@ const IconWrapper = styled.span`
* @param {string} props.text - The help text to display in the tooltip
* @returns {React.ReactElement} Info icon with attached MUI tooltip
*/
-const FiltersTooltip = ({ text }) => {
- return (
- (
+
-
-
-
-
- );
-};
+ },
+ }}
+ >
+
+
+
+
+);
FiltersTooltip.propTypes = {
text: PropTypes.string.isRequired,
diff --git a/frontend/src/components/Map/Map.jsx b/frontend/src/components/Map/Map.jsx
index 045f7e19..ba009829 100644
--- a/frontend/src/components/Map/Map.jsx
+++ b/frontend/src/components/Map/Map.jsx
@@ -1,13 +1,10 @@
import ReactDOM from 'react-dom/client';
-import React, { useEffect } from 'react';
-import { httpService } from '../../services/http/httpService';
-import { FiltersForm } from '../FiltersForm/FiltersForm';
-import { MapComponent } from './MapComponent';
-import { useMapStore } from './store/map.store';
-import { CategoriesProvider } from '../Categories/CategoriesContext';
+import React from 'react';
import { createPortal } from 'react-dom';
-import { AppToaster } from '../common/AppToaster';
-import useDebounce from '../../utils/hooks/useDebounce';
+import FiltersForm from '../FiltersForm/FiltersForm';
+import MapComponent from './MapComponent';
+import { CategoriesProvider } from '../Categories/CategoriesContext';
+import AppToaster from '../common/AppToaster';
/**
* Wrapper component that renders the map and filters form into their respective DOM placeholders.
@@ -41,10 +38,12 @@ const MapWrap = () => {
*
* @returns {void}
*/
-export const MapContainer = () => {
+const MapContainer = () => {
const appContainer = document.createElement('div');
document.body.appendChild(appContainer);
const root = ReactDOM.createRoot(appContainer);
root.render();
};
+
+export default MapContainer;
diff --git a/frontend/src/components/Map/MapComponent.jsx b/frontend/src/components/Map/MapComponent.jsx
index 137efac6..b43eacc9 100644
--- a/frontend/src/components/Map/MapComponent.jsx
+++ b/frontend/src/components/Map/MapComponent.jsx
@@ -1,19 +1,19 @@
import React, { useState } from 'react';
import { MapContainer, TileLayer } from 'react-leaflet';
import Control from 'react-leaflet-custom-control';
-import { LocationControl } from './components/LocationControl';
-import { SuggestNewPointButton } from './components/SuggestNewPointButton';
-import { LocationPermissionBanner } from './components/LocationPermissionBanner';
-import { mapConfig } from './map.config';
-import { CustomZoomControl } from './components/ZoomControl';
+import LocationControl from './components/LocationControl';
+import SuggestNewPointButton from './components/SuggestNewPointButton';
+import LocationPermissionBanner from './components/LocationPermissionBanner';
+import mapConfig from './map.config';
+import CustomZoomControl from './components/ZoomControl';
import MapAutocomplete from './components/MapAutocomplete';
import ListViewButton from './components/ListView';
import AccessibilityTable from './components/AccessibilityTable';
import SaveMapConfiguration from './components/SaveMapConfiguration';
-import { Markers } from './components/Markers';
-import { MapLoadingOverlay } from './components/MapLoadingOverlay';
+import Markers from './components/Markers';
+import MapLoadingOverlay from './components/MapLoadingOverlay';
import { LocationProvider, useLocation } from './context/LocationContext';
-import { GoToLocation } from './components/GoToLocation';
+import GoToLocation from './components/GoToLocation';
import MapOverlays from '../../plugins/MapOverlays';
/**
@@ -83,10 +83,10 @@ const MapComponentInner = () => {
*
* @returns {React.ReactElement} MapContainer with markers and controls, or AccessibilityTable when list view is active
*/
-export const MapComponent = () => {
- return (
-
-
-
- );
-};
+const MapComponent = () => (
+
+
+
+);
+
+export default MapComponent;
diff --git a/frontend/src/components/Map/components/AccessibilityTable.jsx b/frontend/src/components/Map/components/AccessibilityTable.jsx
index a375169d..3961669f 100644
--- a/frontend/src/components/Map/components/AccessibilityTable.jsx
+++ b/frontend/src/components/Map/components/AccessibilityTable.jsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useMemo, useState } from 'react';
+import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
@@ -10,7 +10,7 @@ import Paper from '@mui/material/Paper';
import Arrow from '@mui/icons-material/ArrowLeftRounded';
import { IconButton } from '@mui/material';
import PropTypes from 'prop-types';
-import { httpService } from '../../../services/http/httpService';
+import httpService from '../../../services/http/httpService';
import FieldRenderer from '../../MarkerPopup/FieldRenderer';
import { useCategories } from '../../Categories/CategoriesContext';
@@ -44,61 +44,38 @@ const AccessibilityTable = ({ userPosition, setIsAccessibilityTableOpen }) => {
}, [categories, userPosition]);
useEffect(() => {
+ if (!data) {
+ return;
+ }
try {
- const uniqueHeadersSet = new Set();
- if (!data) {
- return;
- }
- uniqueHeadersSet.add(t('title'));
- for (const place of data) {
- for (const item of place.data) {
+ const uniqueHeadersSet = new Set([t('title')]);
+ data.forEach(place => {
+ place.data.forEach(item => {
uniqueHeadersSet.add(item[0]);
- }
- }
- const uniqueNumberedKeys = {};
- for (const [index, key] of Array.from(uniqueHeadersSet).entries()) {
- uniqueNumberedKeys[key] = index;
- }
- const orderedKeysArray = Object.keys(uniqueNumberedKeys).sort(
- (a, b) => uniqueNumberedKeys[a] - uniqueNumberedKeys[b],
- );
+ });
+ });
+ const orderedKeysArray = Array.from(uniqueHeadersSet);
setHeaders(orderedKeysArray);
- const rowsLocal = [];
-
const getArr = (placeItem, key) => {
const item = placeItem.find(it => it[0] === key);
- if (!item) {
- return ['', '—'];
- }
- return item;
+ return item || ['', '—'];
};
- for (const it of data) {
- const row = [];
- const place = it.data;
- row.push(it.title);
+ const rowsLocal = data.map(it => {
+ const row = [it.title];
// Skip first element (title) and iterate over remaining keys
- for (const key of orderedKeysArray.slice(1)) {
- const values = getArr(place, key);
- if (values === undefined) {
- continue;
- }
- const value = values[1];
- if (Array.isArray(value)) {
- const str = value.join(', ');
- row.push(str);
- continue;
- }
- row.push(value);
- }
- rowsLocal.push(row);
- }
+ orderedKeysArray.slice(1).forEach(key => {
+ const [, value] = getArr(it.data, key);
+ row.push(Array.isArray(value) ? value.join(', ') : value);
+ });
+ return row;
+ });
setRows(rowsLocal);
} catch (error) {
console.log('AccessibilityTable: ', error);
}
- }, [data]);
+ }, [data, t]);
return (
<>
@@ -125,13 +102,13 @@ const AccessibilityTable = ({ userPosition, setIsAccessibilityTableOpen }) => {
- {rows.map((row, index) => (
+ {rows.map(row => (
- {row.map((cell, index) => (
-
+ {row.map((cell, cellIndex) => (
+
{cell.type ? : cell}
))}
diff --git a/frontend/src/components/Map/components/GoToLocation.jsx b/frontend/src/components/Map/components/GoToLocation.jsx
index 57441ef5..e3edbc0d 100644
--- a/frontend/src/components/Map/components/GoToLocation.jsx
+++ b/frontend/src/components/Map/components/GoToLocation.jsx
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { useMap } from 'react-leaflet';
-import { httpService } from '../../../services/http/httpService';
-import { useMapStore } from '../store/map.store';
+import httpService from '../../../services/http/httpService';
+import useMapStore from '../store/map.store';
/**
* Component that handles navigating to a specific location by ID.
@@ -10,7 +10,7 @@ import { useMapStore } from '../store/map.store';
*
* @returns {null} This component renders nothing
*/
-export const GoToLocation = () => {
+const GoToLocation = () => {
const map = useMap();
const [hasNavigated, setHasNavigated] = useState(false);
const setSelectedLocationId = useMapStore(state => state.setSelectedLocationId);
@@ -50,3 +50,5 @@ export const GoToLocation = () => {
return null;
};
+
+export default GoToLocation;
diff --git a/frontend/src/components/Map/components/ListView.jsx b/frontend/src/components/Map/components/ListView.jsx
index 799ff2ae..bbfb6e57 100644
--- a/frontend/src/components/Map/components/ListView.jsx
+++ b/frontend/src/components/Map/components/ListView.jsx
@@ -1,4 +1,5 @@
import React from 'react';
+import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Button, Tooltip } from '@mui/material';
import ViewListIcon from '@mui/icons-material/ViewList';
@@ -65,4 +66,8 @@ const Wrapper = styled.div`
z-index: 9999999;
`;
+ListView.propTypes = {
+ onClick: PropTypes.func.isRequired,
+};
+
export default ListView;
diff --git a/frontend/src/components/Map/components/LocationControl.jsx b/frontend/src/components/Map/components/LocationControl.jsx
index 88b45bb8..62e6f50d 100644
--- a/frontend/src/components/Map/components/LocationControl.jsx
+++ b/frontend/src/components/Map/components/LocationControl.jsx
@@ -86,4 +86,4 @@ const LocationControl = () => {
);
};
-export { LocationControl };
+export default LocationControl;
diff --git a/frontend/src/components/Map/components/LocationPermissionBanner.jsx b/frontend/src/components/Map/components/LocationPermissionBanner.jsx
index 29a6238f..9d27f01c 100644
--- a/frontend/src/components/Map/components/LocationPermissionBanner.jsx
+++ b/frontend/src/components/Map/components/LocationPermissionBanner.jsx
@@ -12,7 +12,7 @@ const BANNER_DISMISSED_KEY = 'goodmap_location_banner_dismissed';
*
* @returns {React.ReactElement|null} Banner component or null if not applicable
*/
-export const LocationPermissionBanner = () => {
+const LocationPermissionBanner = () => {
const { t } = useTranslation();
const { permissionState, requestGeolocation } = useLocation();
const [dismissed, setDismissed] = useState(true);
@@ -52,6 +52,8 @@ export const LocationPermissionBanner = () => {
);
};
+export default LocationPermissionBanner;
+
const BannerContainer = styled.div`
position: absolute;
/* Positioned above the zoom controls and other map buttons */
diff --git a/frontend/src/components/Map/components/MapLoadingOverlay.jsx b/frontend/src/components/Map/components/MapLoadingOverlay.jsx
index fe7c21be..d882f4eb 100644
--- a/frontend/src/components/Map/components/MapLoadingOverlay.jsx
+++ b/frontend/src/components/Map/components/MapLoadingOverlay.jsx
@@ -56,7 +56,7 @@ const LoadingText = styled.p`
* @param {string} [props.text] - Optional loading text to display
* @returns {React.ReactElement} Loading overlay with spinner or custom GIF
*/
-export const MapLoadingOverlay = ({ isLoading, text = null }) => {
+const MapLoadingOverlay = ({ isLoading, text = null }) => {
const customGif = globalThis.MAP_LOADING_GIF;
return (
@@ -71,3 +71,5 @@ MapLoadingOverlay.propTypes = {
isLoading: PropTypes.bool.isRequired,
text: PropTypes.string,
};
+
+export default MapLoadingOverlay;
diff --git a/frontend/src/components/Map/components/Markers.jsx b/frontend/src/components/Map/components/Markers.jsx
index 82a83024..038df6e5 100644
--- a/frontend/src/components/Map/components/Markers.jsx
+++ b/frontend/src/components/Map/components/Markers.jsx
@@ -2,10 +2,10 @@ import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { useMap } from 'react-leaflet';
import MarkerClusterGroup from 'react-leaflet-cluster';
-import { httpService } from '../../../services/http/httpService';
-import { MarkerPopup } from '../../MarkerPopup/MarkerPopup';
+import httpService from '../../../services/http/httpService';
+import MarkerPopup from '../../MarkerPopup/MarkerPopup';
import { useCategories } from '../../Categories/CategoriesContext';
-import { ClusterMarker } from '../../MarkerPopup/ClusterMarker';
+import ClusterMarker from '../../MarkerPopup/ClusterMarker';
/**
* Converts location data into marker components.
@@ -43,7 +43,7 @@ const getMarkers = locations => {
* @param {Function} [props.onLoadingChange] - Callback fired when loading state changes
* @returns {React.ReactElement|Array} MarkerClusterGroup containing location markers, or empty array while loading
*/
-export const Markers = ({ onLoadingChange = null }) => {
+const Markers = ({ onLoadingChange = null }) => {
const { categories, isInitialized } = useCategories();
const [markers, setMarkers] = useState([]);
const [areMarkersLoaded, setAreMarkersLoaded] = useState(false);
@@ -74,7 +74,7 @@ export const Markers = ({ onLoadingChange = null }) => {
// Only use client-side clustering when server-side clustering is disabled
const markerCluster = useServerSideClustering ? (
- <>{markersToAdd}>
+ markersToAdd
) : (
{
Markers.propTypes = {
onLoadingChange: PropTypes.func,
};
+
+export default Markers;
diff --git a/frontend/src/components/Map/components/SaveMapConfiguration.jsx b/frontend/src/components/Map/components/SaveMapConfiguration.jsx
index 454f907d..803ea64a 100644
--- a/frontend/src/components/Map/components/SaveMapConfiguration.jsx
+++ b/frontend/src/components/Map/components/SaveMapConfiguration.jsx
@@ -1,6 +1,6 @@
import { useEffect } from 'react';
import { useMap } from 'react-leaflet';
-import { useMapStore } from '../store/map.store';
+import useMapStore from '../store/map.store';
/**
* Component that saves the current map configuration (zoom level) to the map store.
@@ -15,7 +15,6 @@ const SaveMapConfiguration = () => {
useEffect(() => {
const updateMapState = () => {
- const bounds = map.getBounds();
const zoom = map.getZoom();
useMapStore.getState().setMapConfiguration({
@@ -24,7 +23,7 @@ const SaveMapConfiguration = () => {
// northWestBoundLng: bounds.getNorthWest().lng,
// southEastBoundLat: bounds.getSouthEast().lat,
// southEastBoundLng: bounds.getSouthEast().lng,
- zoom: zoom,
+ zoom,
});
};
diff --git a/frontend/src/components/Map/components/SuggestNewPointButton.jsx b/frontend/src/components/Map/components/SuggestNewPointButton.jsx
index 80c32037..05a4f166 100644
--- a/frontend/src/components/Map/components/SuggestNewPointButton.jsx
+++ b/frontend/src/components/Map/components/SuggestNewPointButton.jsx
@@ -5,7 +5,7 @@ import AddIcon from '@mui/icons-material/Add';
import { useTranslation } from 'react-i18next';
import { buttonStyle, getLocationAwareStyles } from '../../../styles/buttonStyle';
import { useLocation } from '../context/LocationContext';
-import { SuggestNewPointDialog } from './SuggestNewPointDialog';
+import SuggestNewPointDialog from './SuggestNewPointDialog';
/**
* Button that opens the "suggest a new point" form. Opening requires the user's
@@ -14,7 +14,7 @@ import { SuggestNewPointDialog } from './SuggestNewPointDialog';
*
* @returns {React.ReactElement} Button and its suggestion dialog
*/
-export const SuggestNewPointButton = () => {
+const SuggestNewPointButton = () => {
const { t } = useTranslation();
const { locationGranted, requestLocationWithFeedback } = useLocation();
const [showNewPointBox, setShowNewPointSuggestionBox] = useState(false);
@@ -53,3 +53,5 @@ export const SuggestNewPointButton = () => {
>
);
};
+
+export default SuggestNewPointButton;
diff --git a/frontend/src/components/Map/components/SuggestNewPointDialog.jsx b/frontend/src/components/Map/components/SuggestNewPointDialog.jsx
index 2824121a..47a7e41f 100644
--- a/frontend/src/components/Map/components/SuggestNewPointDialog.jsx
+++ b/frontend/src/components/Map/components/SuggestNewPointDialog.jsx
@@ -24,11 +24,11 @@ import AddAPhotoIcon from '@mui/icons-material/AddAPhoto';
import RefreshIcon from '@mui/icons-material/Refresh';
import axios from 'axios';
import { useTranslation } from 'react-i18next';
-import { getCsrfToken } from '../../../utils/csrf';
+import imageCompression from 'browser-image-compression';
+import getCsrfToken from '../../../utils/csrf';
import { useLocation } from '../context/LocationContext';
import { useCategories } from '../../Categories/CategoriesContext';
-import { toast } from '../../../utils/toast';
-import imageCompression from 'browser-image-compression';
+import toast from '../../../utils/toast';
// Map a category's options to a { key: translation } object.
// Options come as [[key, translation], ...] or [key, ...].
@@ -36,7 +36,8 @@ const mapCategoryOptions = categoryOptions => {
const optionMap = {};
categoryOptions.forEach(opt => {
if (Array.isArray(opt)) {
- optionMap[opt[0]] = opt[1];
+ const [key, value] = opt;
+ optionMap[key] = value;
} else {
optionMap[opt] = opt;
}
@@ -113,7 +114,7 @@ const useScrollToTop = trigger => {
* @param {{open: boolean, onClose: () => void}} props
* @returns {React.ReactElement} Dialog with the new point suggestion form
*/
-export const SuggestNewPointDialog = ({ open, onClose }) => {
+const SuggestNewPointDialog = ({ open, onClose }) => {
const { t } = useTranslation();
const { userPosition, requestLocationWithFeedback } = useLocation();
const { categoriesData } = useCategories();
@@ -280,24 +281,19 @@ export const SuggestNewPointDialog = ({ open, onClose }) => {
};
// Helper to get translated field label
- const getFieldLabel = fieldName => {
- // Check static translations first, then category translations, then fallback to raw name
- return (
- staticFieldTranslations[fieldName] ||
- categoryTranslations.fieldNames[fieldName] ||
- fieldName
- );
- };
+ // Check static translations first, then category translations, then fallback to raw name
+ const getFieldLabel = fieldName =>
+ staticFieldTranslations[fieldName] ||
+ categoryTranslations.fieldNames[fieldName] ||
+ fieldName;
// Helper to get translated option label
- const getOptionLabel = (fieldName, optionKey) => {
- return categoryTranslations.options[fieldName]?.[optionKey] || optionKey;
- };
+ const getOptionLabel = (fieldName, optionKey) =>
+ categoryTranslations.options[fieldName]?.[optionKey] || optionKey;
// Helper to get translated selected values for display
- const getSelectedDisplay = (fieldName, selectedValues) => {
- return selectedValues.map(val => getOptionLabel(fieldName, val)).join(', ');
- };
+ const getSelectedDisplay = (fieldName, selectedValues) =>
+ selectedValues.map(val => getOptionLabel(fieldName, val)).join(', ');
// Render form field based on field type and whether it's a category
const renderFormField = (fieldName, fieldType) => {
@@ -328,7 +324,8 @@ export const SuggestNewPointDialog = ({ open, onClose }) => {
);
- } else if (isCategory) {
+ }
+ if (isCategory) {
// Single select for category fields
return (
@@ -347,20 +344,19 @@ export const SuggestNewPointDialog = ({ open, onClose }) => {
);
- } else {
- // Text field for non-category fields
- return (
-
- );
}
+ // Text field for non-category fields
+ return (
+
+ );
};
return (
@@ -445,3 +441,5 @@ SuggestNewPointDialog.propTypes = {
open: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
};
+
+export default SuggestNewPointDialog;
diff --git a/frontend/src/components/Map/components/ZoomControl.jsx b/frontend/src/components/Map/components/ZoomControl.jsx
index 07c5036f..598d9a15 100644
--- a/frontend/src/components/Map/components/ZoomControl.jsx
+++ b/frontend/src/components/Map/components/ZoomControl.jsx
@@ -11,7 +11,7 @@ import { zoomInButtonStyle, zoomOutButtonStyle } from '../../../styles/buttonSty
*
* @returns {React.ReactElement} Control component with zoom in/out buttons
*/
-export const CustomZoomControl = () => {
+const CustomZoomControl = () => {
const map = useMap();
const handleZoomIn = () => {
@@ -45,3 +45,5 @@ export const CustomZoomControl = () => {
);
};
+
+export default CustomZoomControl;
diff --git a/frontend/src/components/Map/map.config.js b/frontend/src/components/Map/map.config.js
index aa381cda..25a88e66 100644
--- a/frontend/src/components/Map/map.config.js
+++ b/frontend/src/components/Map/map.config.js
@@ -1,5 +1,7 @@
-export const mapConfig = {
+const mapConfig = {
initialMapCoordinates: [51.917, 19.013],
initialMapZoom: 7,
maxMapZoom: 19,
};
+
+export default mapConfig;
diff --git a/frontend/src/components/Map/store/map.store.js b/frontend/src/components/Map/store/map.store.js
index 8fe11f42..fa6e4b94 100644
--- a/frontend/src/components/Map/store/map.store.js
+++ b/frontend/src/components/Map/store/map.store.js
@@ -1,8 +1,10 @@
-import {create} from 'zustand';
+import { create } from 'zustand';
-export const useMapStore = create((set) => ({
+const useMapStore = create(set => ({
mapConfiguration: null,
- setMapConfiguration: (mapConfiguration) => set({mapConfiguration}),
+ setMapConfiguration: mapConfiguration => set({ mapConfiguration }),
selectedLocationId: null,
- setSelectedLocationId: (selectedLocationId) => set({selectedLocationId}),
+ setSelectedLocationId: selectedLocationId => set({ selectedLocationId }),
}));
+
+export default useMapStore;
diff --git a/frontend/src/components/MarkerPopup/ClusterMarker.jsx b/frontend/src/components/MarkerPopup/ClusterMarker.jsx
index bab9a8e2..d247c69a 100644
--- a/frontend/src/components/MarkerPopup/ClusterMarker.jsx
+++ b/frontend/src/components/MarkerPopup/ClusterMarker.jsx
@@ -16,7 +16,7 @@ import styled from 'styled-components';
* @param {number} props.cluster.cluster_count - Number of markers in this cluster
* @returns {React.ReactElement} Marker component with custom cluster icon
*/
-export const ClusterMarker = ({ cluster }) => {
+const ClusterMarker = ({ cluster }) => {
const map = useMap();
const handleClusterClick = () => {
const targetZoom = Math.min(map.getZoom() + 5, map.getMaxZoom());
@@ -24,14 +24,16 @@ export const ClusterMarker = ({ cluster }) => {
};
// Create a DivIcon with the rendered React component (memoized to prevent recreation)
- const clusterIcon = useMemo(() => {
- return new DivIcon({
- html: ReactDOMServer.renderToString(),
- className: 'custom-cluster-icon',
- iconSize: [30, 30],
- iconAnchor: [15, 15],
- });
- }, [cluster.cluster_count]);
+ const clusterIcon = useMemo(
+ () =>
+ new DivIcon({
+ html: ReactDOMServer.renderToString(),
+ className: 'custom-cluster-icon',
+ iconSize: [30, 30],
+ iconAnchor: [15, 15],
+ }),
+ [cluster.cluster_count],
+ );
return (
{
* @param {number} props.cluster.cluster_count - Number of markers in this cluster
* @returns {React.ReactElement} Styled circular icon with cluster count
*/
-const ClusterMarkerIcon = ({ cluster }) => {
- return (
-
- {cluster.cluster_count}
-
- );
-};
+const ClusterMarkerIcon = ({ cluster }) => (
+
+ {cluster.cluster_count}
+
+);
/**
* Styled container for the cluster marker icon.
@@ -97,3 +97,5 @@ ClusterMarker.propTypes = {
cluster_count: PropTypes.number.isRequired,
}).isRequired,
};
+
+export default ClusterMarker;
diff --git a/frontend/src/components/MarkerPopup/DesktopPopup.jsx b/frontend/src/components/MarkerPopup/DesktopPopup.jsx
index c4690924..d065815d 100644
--- a/frontend/src/components/MarkerPopup/DesktopPopup.jsx
+++ b/frontend/src/components/MarkerPopup/DesktopPopup.jsx
@@ -1,5 +1,7 @@
-import React, { useEffect, useRef } from 'react';
+import React, { useEffect } from 'react';
+import PropTypes from 'prop-types';
import { Popup } from 'react-leaflet';
+import { useLeafletContext } from '@react-leaflet/core';
import styled from 'styled-components';
/**
@@ -11,24 +13,28 @@ const StyledPopup = styled(Popup)`
/**
* Desktop popup component for displaying location details.
- * Automatically opens the popup on mount using a ref to access the underlying Leaflet marker.
- * This is a workaround since react-leaflet's Popup doesn't support lazy loading or .open() function.
+ * Automatically opens the popup on mount via the parent Marker, which react-leaflet exposes as
+ * the context's overlay container. This is a workaround since react-leaflet's Popup doesn't
+ * support lazy loading or an .open() function.
*
* @param {Object} props - Component props
* @param {React.ReactNode} props.children - Content to display inside the popup
* @returns {React.ReactElement} Styled Popup component that auto-opens on mount
*/
-export const DesktopPopup = ({ children }) => {
- const popupRef = useRef(null);
+const DesktopPopup = ({ children }) => {
+ const context = useLeafletContext();
useEffect(() => {
- if (popupRef.current) {
- const marker = popupRef.current._source;
- if (marker) {
- marker.openPopup();
- }
- }
- }, []);
+ // Popup binds itself to the overlay container in its own effect, and child effects run
+ // before parent ones, so the popup is already bound by the time this runs.
+ context.overlayContainer?.openPopup();
+ }, [context]);
- return {children};
+ return {children};
};
+
+DesktopPopup.propTypes = {
+ children: PropTypes.node.isRequired,
+};
+
+export default DesktopPopup;
diff --git a/frontend/src/components/MarkerPopup/LocationDetails.jsx b/frontend/src/components/MarkerPopup/LocationDetails.jsx
index e6c87d46..f04d063e 100644
--- a/frontend/src/components/MarkerPopup/LocationDetails.jsx
+++ b/frontend/src/components/MarkerPopup/LocationDetails.jsx
@@ -5,13 +5,12 @@ import ReportProblemOutlinedIcon from '@mui/icons-material/ReportProblemOutlined
import ShareIcon from '@mui/icons-material/Share';
import { useTranslation } from 'react-i18next';
import { isMobile } from 'react-device-detect';
+import React, { useState } from 'react';
import { buttonStyleSmall } from '../../styles/buttonStyle';
import getContentAsString from './fieldContent';
import FieldRenderer from './FieldRenderer';
-import { ReportProblemForm } from './ReportProblemForm';
-import { toast } from '../../utils/toast';
-
-import React, { useState } from 'react';
+import ReportProblemForm from './ReportProblemForm';
+import toast from '../../utils/toast';
const PopupContainer = styled.div`
padding: 8px 4px;
@@ -209,7 +208,7 @@ const LocationDetails = ({ place }) => {
{CTACategories.length > 0 && (
- {CTACategories.map(([_category, value]) => (
+ {CTACategories.map(([, value]) => (
))}
@@ -222,6 +221,8 @@ LocationDetails.propTypes = {
place: PropTypes.shape({
title: PropTypes.string.isRequired,
subtitle: PropTypes.string,
+ // [category, value] tuples where value's shape varies by field type - can't be typed more precisely.
+ // eslint-disable-next-line react/forbid-prop-types
data: PropTypes.arrayOf(PropTypes.array).isRequired,
}).isRequired,
};
@@ -300,13 +301,13 @@ ShareLocationButton.propTypes = {
* @param {string} props.place.metadata.uuid - Unique identifier for the location
* @returns {React.ReactElement} Fragment containing location details, navigation button, and report form
*/
-export const LocationDetailsBox = ({ place }) => {
+const LocationDetailsBox = ({ place }) => {
const { t } = useTranslation();
const [showForm, setShowForm] = useState(false);
const toggleForm = () => setShowForm(!showForm);
return (
-
+ <>
{
{showForm && }
-
+ >
);
};
@@ -345,6 +346,8 @@ LocationDetailsBox.propTypes = {
place: PropTypes.shape({
title: PropTypes.string.isRequired,
subtitle: PropTypes.string,
+ // [category, value] tuples where value's shape varies by field type - can't be typed more precisely.
+ // eslint-disable-next-line react/forbid-prop-types
data: PropTypes.arrayOf(PropTypes.array).isRequired,
position: PropTypes.arrayOf(PropTypes.number).isRequired,
metadata: PropTypes.shape({
@@ -352,3 +355,5 @@ LocationDetailsBox.propTypes = {
}).isRequired,
}).isRequired,
};
+
+export default LocationDetailsBox;
diff --git a/frontend/src/components/MarkerPopup/MarkerPopup.jsx b/frontend/src/components/MarkerPopup/MarkerPopup.jsx
index 5c53e582..6bec59fb 100644
--- a/frontend/src/components/MarkerPopup/MarkerPopup.jsx
+++ b/frontend/src/components/MarkerPopup/MarkerPopup.jsx
@@ -4,12 +4,12 @@ import { Marker } from 'react-leaflet';
import { isMobile } from 'react-device-detect';
import { Icon } from 'leaflet';
import { useTranslation } from 'react-i18next';
-import { httpService } from '../../services/http/httpService';
-import { useMapStore } from '../Map/store/map.store';
+import httpService from '../../services/http/httpService';
+import useMapStore from '../Map/store/map.store';
-import { LocationDetailsBox } from './LocationDetails';
-import { MobilePopup } from './MobilePopup';
-import { DesktopPopup } from './DesktopPopup';
+import LocationDetailsBox from './LocationDetails';
+import MobilePopup from './MobilePopup';
+import DesktopPopup from './DesktopPopup';
import iconAsterisk from '../../res/img/marker-icon-asterisk.png';
/**
@@ -63,6 +63,12 @@ const LocationDetailsBoxWrapper = ({ theplace }) => {
return {renderContent()};
};
+LocationDetailsBoxWrapper.propTypes = {
+ theplace: PropTypes.shape({
+ uuid: PropTypes.string.isRequired,
+ }).isRequired,
+};
+
/**
* Custom Leaflet icon for markers with remarks/special annotations.
* Displays an asterisk icon to visually distinguish remarked locations from standard markers.
@@ -84,7 +90,7 @@ const asteriskIcon = new Icon({
* @param {boolean} [props.place.has_remark] - Whether this location has a remark (uses asterisk icon if true)
* @returns {React.ReactElement} Leaflet Marker component with click-to-show-details functionality
*/
-export const MarkerPopup = ({ place }) => {
+const MarkerPopup = ({ place }) => {
const selectedLocationId = useMapStore(state => state.selectedLocationId);
const setSelectedLocationId = useMapStore(state => state.setSelectedLocationId);
const [isClicked, setIsClicked] = useState(false);
@@ -123,6 +129,7 @@ export const MarkerPopup = ({ place }) => {
}
return (
+ // eslint-disable-next-line react/jsx-props-no-spreading -- icon is only conditionally set
{isClicked && }
@@ -136,3 +143,5 @@ MarkerPopup.propTypes = {
uuid: PropTypes.string.isRequired,
}).isRequired,
};
+
+export default MarkerPopup;
diff --git a/frontend/src/components/MarkerPopup/MobilePopup.jsx b/frontend/src/components/MarkerPopup/MobilePopup.jsx
index 29746309..e666d624 100644
--- a/frontend/src/components/MarkerPopup/MobilePopup.jsx
+++ b/frontend/src/components/MarkerPopup/MobilePopup.jsx
@@ -1,8 +1,22 @@
import React, { useEffect, useState } from 'react';
+import PropTypes from 'prop-types';
import { Dialog, DialogContent, DialogTitle, IconButton, Slide } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import { useLeafletContext } from '@react-leaflet/core';
+/**
+ * Slide transition component for the mobile popup dialog.
+ * Animates the dialog sliding up from the bottom of the screen.
+ *
+ * @param {Object} props - Transition props passed by Material-UI
+ * @param {React.Ref} ref - Forwarded ref for the transition component
+ * @returns {React.ReactElement} Slide transition component
+ */
+const SlideTransition = React.forwardRef((props, ref) => (
+ // eslint-disable-next-line react/jsx-props-no-spreading -- forwards arbitrary MUI transition props
+
+));
+
/**
* Mobile-optimized popup component for displaying location details.
* Renders as a bottom sheet dialog that slides up from the bottom of the screen.
@@ -13,7 +27,7 @@ import { useLeafletContext } from '@react-leaflet/core';
* @param {React.ReactNode} props.children - Content to display inside the popup dialog
* @returns {React.ReactElement} Material-UI Dialog styled as a bottom sheet
*/
-export const MobilePopup = ({ children }) => {
+const MobilePopup = ({ children }) => {
const [isOpen, setIsOpen] = useState(true);
const context = useLeafletContext();
@@ -29,7 +43,7 @@ export const MobilePopup = ({ children }) => {
};
if (overlayContainer) {
- centerMap(overlayContainer._latlng);
+ centerMap(overlayContainer.getLatLng());
overlayContainer.on('click', place => {
centerMap(place.latlng);
@@ -81,14 +95,8 @@ export const MobilePopup = ({ children }) => {
);
};
-/**
- * Slide transition component for the mobile popup dialog.
- * Animates the dialog sliding up from the bottom of the screen.
- *
- * @param {Object} props - Transition props passed by Material-UI
- * @param {React.Ref} ref - Forwarded ref for the transition component
- * @returns {React.ReactElement} Slide transition component
- */
-const SlideTransition = React.forwardRef(function Transition(props, ref) {
- return ;
-});
+MobilePopup.propTypes = {
+ children: PropTypes.node.isRequired,
+};
+
+export default MobilePopup;
diff --git a/frontend/src/components/MarkerPopup/ReportProblemForm.jsx b/frontend/src/components/MarkerPopup/ReportProblemForm.jsx
index 923b7955..9a2405b0 100644
--- a/frontend/src/components/MarkerPopup/ReportProblemForm.jsx
+++ b/frontend/src/components/MarkerPopup/ReportProblemForm.jsx
@@ -3,7 +3,7 @@ import React, { useState } from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
-import { getCsrfToken } from '../../utils/csrf';
+import getCsrfToken from '../../utils/csrf';
/**
* Styled form component with flexbox column layout.
@@ -145,7 +145,7 @@ const getIssueTypeOptions = t => {
];
};
-export const ReportProblemForm = ({ placeId }) => {
+const ReportProblemForm = ({ placeId }) => {
const { t } = useTranslation();
const [problem, setProblem] = useState('');
const [problemType, setProblemType] = useState('');
@@ -213,3 +213,5 @@ export const ReportProblemForm = ({ placeId }) => {
ReportProblemForm.propTypes = {
placeId: PropTypes.string.isRequired,
};
+
+export default ReportProblemForm;
diff --git a/frontend/src/components/common/AppToaster.jsx b/frontend/src/components/common/AppToaster.jsx
index 9e1395a1..6a9ac7a9 100644
--- a/frontend/src/components/common/AppToaster.jsx
+++ b/frontend/src/components/common/AppToaster.jsx
@@ -3,7 +3,7 @@ import { createPortal } from 'react-dom';
import toast, { ToastBar, Toaster } from 'react-hot-toast';
import { IconButton } from '@mui/material';
import Close from '@mui/icons-material/Close';
-import { useMaxToasts } from '../../utils/hooks/useMaxToasts';
+import useMaxToasts from '../../utils/hooks/useMaxToasts';
/**
* Global toast notification component for displaying user feedback messages.
@@ -12,7 +12,7 @@ import { useMaxToasts } from '../../utils/hooks/useMaxToasts';
*
* @returns {React.ReactElement} Toaster component configured for the application
*/
-export const AppToaster = () => {
+const AppToaster = () => {
useMaxToasts();
// #main-row's stacking context would trap the toast below the MUI Dialog.
@@ -56,3 +56,5 @@ export const AppToaster = () => {
document.getElementById('overlay-root'),
);
};
+
+export default AppToaster;
diff --git a/frontend/src/components/common/Autocomplete.jsx b/frontend/src/components/common/Autocomplete.jsx
index 5204ea9a..115d6fb7 100644
--- a/frontend/src/components/common/Autocomplete.jsx
+++ b/frontend/src/components/common/Autocomplete.jsx
@@ -20,13 +20,14 @@ const AutoComplete = ({ onClick }) => {
const { t } = useTranslation();
const timeoutRef = useRef(null);
- useEffect(() => {
- return () => {
+ useEffect(
+ () => () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
- };
- }, []);
+ },
+ [],
+ );
const handleInputChange = e => {
const term = e.target.value;
diff --git a/frontend/src/index.js b/frontend/src/index.js
index 6752c0f6..40d00372 100644
--- a/frontend/src/index.js
+++ b/frontend/src/index.js
@@ -1,6 +1,6 @@
-import { MapContainer } from './components/Map/Map';
+import MapContainer from './components/Map/Map';
import './i18n';
-import { loadPlugins } from './plugins/pluginLoader';
+import loadPlugins from './plugins/pluginLoader';
(async () => {
await loadPlugins();
diff --git a/frontend/src/plugins/pluginLoader.js b/frontend/src/plugins/pluginLoader.js
index 92d26b21..09331265 100644
--- a/frontend/src/plugins/pluginLoader.js
+++ b/frontend/src/plugins/pluginLoader.js
@@ -12,7 +12,7 @@ async function loadRemoteScript(url) {
});
}
-export async function loadPlugins() {
+async function loadPlugins() {
const manifest = globalThis.PLUGIN_MANIFEST;
if (!Array.isArray(manifest) || manifest.length === 0) return;
@@ -23,10 +23,14 @@ export async function loadPlugins() {
// per-capability module from it.
const initialized = new Set();
+ // Sequential by design: each container must finish init() against the shared scope before
+ // the next one loads, so the awaits below cannot be parallelized with Promise.all.
+ /* eslint-disable no-await-in-loop */
for (const { pluginName, url, module: moduleName, config, capability } of manifest) {
try {
if (!initialized.has(pluginName)) {
await loadRemoteScript(url);
+ // eslint-disable-next-line camelcase
await window[pluginName].init(__webpack_share_scopes__.default);
initialized.add(pluginName);
}
@@ -36,4 +40,7 @@ export async function loadPlugins() {
console.warn(`Failed to load plugin "${pluginName}" (${capability}):`, e);
}
}
+ /* eslint-enable no-await-in-loop */
}
+
+export default loadPlugins;
diff --git a/frontend/src/services/hooks/useAutocomplete.js b/frontend/src/services/hooks/useAutocomplete.js
index 90bdd496..4bee243a 100644
--- a/frontend/src/services/hooks/useAutocomplete.js
+++ b/frontend/src/services/hooks/useAutocomplete.js
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
-import { httpService } from '../http/httpService';
+import httpService from '../http/httpService';
/**
* Custom hook for address autocomplete functionality.
diff --git a/frontend/src/services/http/endpoints.js b/frontend/src/services/http/endpoints.js
index 7e1e2d89..b8d0bef1 100644
--- a/frontend/src/services/http/endpoints.js
+++ b/frontend/src/services/http/endpoints.js
@@ -27,4 +27,3 @@ export const LOCATIONS_CLUSTERED = '/api/locations-clustered';
* Converts addresses/place names to geographic coordinates.
*/
export const SEARCH_ADDRESS = 'https://nominatim.openstreetmap.org/search';
-
diff --git a/frontend/src/services/http/httpService.js b/frontend/src/services/http/httpService.js
index 620fb6d7..6a12e810 100644
--- a/frontend/src/services/http/httpService.js
+++ b/frontend/src/services/http/httpService.js
@@ -5,7 +5,7 @@ import {
SEARCH_ADDRESS,
LOCATIONS_CLUSTERED,
} from './endpoints';
-import { useMapStore } from '../../components/Map/store/map.store';
+import useMapStore from '../../components/Map/store/map.store';
// UUID allowlist used to validate location ids before they are placed in a
// request URL. An inline regex (not uuid.validate()) is used deliberately:
@@ -63,7 +63,7 @@ function filtersToQuery(filters) {
* HTTP service object containing all API interaction methods.
* Provides methods for fetching categories, locations, languages, and address search.
*/
-export const httpService = {
+const httpService = {
/**
* Fetches complete categories data including subcategories in a single request.
* Uses the /api/categories-full endpoint to avoid waterfall requests.
@@ -208,3 +208,5 @@ export const httpService = {
return fetch(`${SEARCH_ADDRESS}?${queryString}`).then(response => response.json());
},
};
+
+export default httpService;
diff --git a/frontend/src/utils/csrf.js b/frontend/src/utils/csrf.js
index a28959fd..a09251ba 100644
--- a/frontend/src/utils/csrf.js
+++ b/frontend/src/utils/csrf.js
@@ -26,7 +26,7 @@
* headers: { 'X-CSRFToken': csrfToken }
* });
*/
-export const getCsrfToken = () => {
+const getCsrfToken = () => {
const metaTag = document.querySelector('meta[name="csrf-token"]');
const token = metaTag?.getAttribute('content');
@@ -38,3 +38,5 @@ export const getCsrfToken = () => {
return token;
};
+
+export default getCsrfToken;
diff --git a/frontend/src/utils/hooks/useMaxToasts.js b/frontend/src/utils/hooks/useMaxToasts.js
index 346afeec..c565a634 100644
--- a/frontend/src/utils/hooks/useMaxToasts.js
+++ b/frontend/src/utils/hooks/useMaxToasts.js
@@ -1,5 +1,5 @@
-import React, { useEffect } from "react";
-import toast, {useToasterStore } from "react-hot-toast";
+import { useEffect } from 'react';
+import toast, { useToasterStore } from 'react-hot-toast';
/**
* Custom hook that limits the maximum number of visible toasts.
@@ -8,15 +8,15 @@ import toast, {useToasterStore } from "react-hot-toast";
* @param {number} max - Maximum number of visible toasts (defaults to 1)
* @returns {void}
*/
-export function useMaxToasts(max = 1) {
+function useMaxToasts(max = 1) {
const { toasts } = useToasterStore();
-
+
useEffect(() => {
- const excessToasts = toasts
- .filter((t) => t.visible)
- .filter((_, i) => i >= max);
+ const excessToasts = toasts.filter(t => t.visible).filter((_, i) => i >= max);
for (const t of excessToasts) {
toast.dismiss(t.id);
}
}, [toasts, max]);
-}
\ No newline at end of file
+}
+
+export default useMaxToasts;
diff --git a/frontend/src/utils/toast.js b/frontend/src/utils/toast.js
index 4fa99162..f981d913 100644
--- a/frontend/src/utils/toast.js
+++ b/frontend/src/utils/toast.js
@@ -8,8 +8,10 @@ import { toast as toastify } from 'react-hot-toast';
* @property {Function} error - Displays an error toast notification
* @property {Function} info - Displays an info/loading toast notification
*/
-export const toast = {
+const toast = {
success: toastify.success,
error: toastify.error,
info: toastify.loading,
};
+
+export default toast;
diff --git a/frontend/tests/CategoriesContext.test.jsx b/frontend/tests/CategoriesContext.test.jsx
index 1023b993..d8780fab 100644
--- a/frontend/tests/CategoriesContext.test.jsx
+++ b/frontend/tests/CategoriesContext.test.jsx
@@ -1,17 +1,21 @@
import React from 'react';
import { render, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
-import { FiltersForm } from '../src/components/FiltersForm/FiltersForm';
-import { SuggestNewPointButton } from '../src/components/Map/components/SuggestNewPointButton';
+import FiltersForm from '../src/components/FiltersForm/FiltersForm';
+import SuggestNewPointButton from '../src/components/Map/components/SuggestNewPointButton';
import { CategoriesProvider } from '../src/components/Categories/CategoriesContext';
import { LocationProvider } from '../src/components/Map/context/LocationContext';
-import { httpService } from '../src/services/http/httpService';
+import httpService from '../src/services/http/httpService';
jest.mock('axios');
jest.mock('../src/services/http/httpService', () => ({
- httpService: { getCategoriesData: jest.fn(), getLocations: jest.fn() },
+ __esModule: true,
+ default: { getCategoriesData: jest.fn(), getLocations: jest.fn() },
+}));
+jest.mock('../src/utils/toast', () => ({
+ __esModule: true,
+ default: { success: jest.fn(), error: jest.fn() },
}));
-jest.mock('../src/utils/toast', () => ({ toast: { success: jest.fn(), error: jest.fn() } }));
jest.mock('browser-image-compression');
// Both the filter panel and the suggestion dialog need the category definitions.
diff --git a/frontend/tests/FiltersForm.test.jsx b/frontend/tests/FiltersForm.test.jsx
index 6eae3bbf..1a896c41 100644
--- a/frontend/tests/FiltersForm.test.jsx
+++ b/frontend/tests/FiltersForm.test.jsx
@@ -1,9 +1,9 @@
import React from 'react';
import '@testing-library/jest-dom';
import { fireEvent, render, waitFor, within } from '@testing-library/react';
-import { FiltersForm } from '../src/components/FiltersForm/FiltersForm';
+import FiltersForm from '../src/components/FiltersForm/FiltersForm';
import { CategoriesProvider } from '../src/components/Categories/CategoriesContext';
-import { httpService } from '../src/services/http/httpService';
+import httpService from '../src/services/http/httpService';
jest.mock('../src/services/http/httpService');
diff --git a/frontend/tests/Map/MapComponent.test.jsx b/frontend/tests/Map/MapComponent.test.jsx
index c7497e8d..2f307e68 100644
--- a/frontend/tests/Map/MapComponent.test.jsx
+++ b/frontend/tests/Map/MapComponent.test.jsx
@@ -1,10 +1,10 @@
import React from 'react';
import { render, waitFor, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
-import { MapComponent } from '../../src/components/Map/MapComponent';
-import { FiltersForm } from '../../src/components/FiltersForm/FiltersForm';
+import MapComponent from '../../src/components/Map/MapComponent';
+import FiltersForm from '../../src/components/FiltersForm/FiltersForm';
import { CategoriesProvider } from '../../src/components/Categories/CategoriesContext';
-import { httpService } from '../../src/services/http/httpService';
+import httpService from '../../src/services/http/httpService';
jest.mock('../../src/services/http/httpService');
@@ -46,7 +46,6 @@ describe('MapComponent', () => {
});
});
- // eslint-disable-next-line es-x/no-async-functions -- needed to await waitFor
it('renders without crashing', async () => {
render(
@@ -77,7 +76,6 @@ describe('MapComponent with FiltersForm', () => {
globalThis.FEATURE_FLAGS = {};
});
- // eslint-disable-next-line es-x/no-async-functions -- needed to await waitFor
it('fetches locations once, already filtered by default-checked options', async () => {
httpService.getLocations.mockClear();
httpService.getCategoriesData.mockResolvedValueOnce({
diff --git a/frontend/tests/Map/components/AccessibilityTable.test.jsx b/frontend/tests/Map/components/AccessibilityTable.test.jsx
index 738cc41a..25930438 100644
--- a/frontend/tests/Map/components/AccessibilityTable.test.jsx
+++ b/frontend/tests/Map/components/AccessibilityTable.test.jsx
@@ -2,7 +2,7 @@ import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { render, screen, act, waitFor } from '@testing-library/react';
import AccessibilityTable from '../../../src/components/Map/components/AccessibilityTable';
-import { httpService } from '../../../src/services/http/httpService';
+import httpService from '../../../src/services/http/httpService';
import { CategoriesProvider } from '../../../src/components/Categories/CategoriesContext';
diff --git a/frontend/tests/Map/components/Markers.test.jsx b/frontend/tests/Map/components/Markers.test.jsx
index 3bac069b..44edffb8 100644
--- a/frontend/tests/Map/components/Markers.test.jsx
+++ b/frontend/tests/Map/components/Markers.test.jsx
@@ -2,12 +2,13 @@ import React from 'react';
import '@testing-library/jest-dom';
import { render, waitFor } from '@testing-library/react';
import { MapContainer } from 'react-leaflet';
-import { Markers } from '../../../src/components/Map/components/Markers';
+import Markers from '../../../src/components/Map/components/Markers';
import { CategoriesProvider } from '../../../src/components/Categories/CategoriesContext';
-import { httpService } from '../../../src/services/http/httpService';
+import httpService from '../../../src/services/http/httpService';
jest.mock('../../../src/services/http/httpService', () => ({
- httpService: {
+ __esModule: true,
+ default: {
getCategoriesData: jest.fn(),
getLocations: jest.fn(),
},
diff --git a/frontend/tests/Map/components/SuggestNewPoint.test.jsx b/frontend/tests/Map/components/SuggestNewPoint.test.jsx
index 2f2ea080..789aad1f 100644
--- a/frontend/tests/Map/components/SuggestNewPoint.test.jsx
+++ b/frontend/tests/Map/components/SuggestNewPoint.test.jsx
@@ -2,7 +2,8 @@ import { render, fireEvent, waitFor, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import React from 'react';
import axios from 'axios';
-import { SuggestNewPointButton } from '../../../src/components/Map/components/SuggestNewPointButton';
+import imageCompression from 'browser-image-compression';
+import SuggestNewPointButton from '../../../src/components/Map/components/SuggestNewPointButton';
import { LocationProvider } from '../../../src/components/Map/context/LocationContext';
import { CategoriesProvider } from '../../../src/components/Categories/CategoriesContext';
import {
@@ -18,26 +19,26 @@ import {
fillTextField,
} from '../../utils/dialogHelpers';
import { ERROR_MESSAGES, FILE_SIZES, SIMPLE_SCHEMA, FULL_SCHEMA } from '../../utils/testConstants';
-import { httpService } from '../../../src/services/http/httpService';
-import { toast } from '../../../src/utils/toast';
-import imageCompression from 'browser-image-compression';
+import httpService from '../../../src/services/http/httpService';
+import toast from '../../../src/utils/toast';
-const renderWithProvider = component => {
- return render(
+const renderWithProvider = component =>
+ render(
{component}
,
);
-};
jest.mock('axios');
jest.mock('../../../src/services/http/httpService', () => ({
- httpService: {
+ __esModule: true,
+ default: {
getCategoriesData: jest.fn(),
},
}));
jest.mock('../../../src/utils/toast', () => ({
- toast: { success: jest.fn(), error: jest.fn() },
+ __esModule: true,
+ default: { success: jest.fn(), error: jest.fn() },
}));
jest.mock('browser-image-compression');
diff --git a/frontend/tests/MarkerPopup/CTA.test.jsx b/frontend/tests/MarkerPopup/CTA.test.jsx
index 36fefc8e..53d4d192 100644
--- a/frontend/tests/MarkerPopup/CTA.test.jsx
+++ b/frontend/tests/MarkerPopup/CTA.test.jsx
@@ -1,7 +1,7 @@
import React from 'react';
import '@testing-library/jest-dom';
import { render, fireEvent } from '@testing-library/react';
-import { LocationDetailsBox } from '../../src/components/MarkerPopup/LocationDetails';
+import LocationDetailsBox from '../../src/components/MarkerPopup/LocationDetails';
const correctMarkerData = {
title: 'Most Grunwaldzki',
diff --git a/frontend/tests/MarkerPopup/ClusterMarker.test.jsx b/frontend/tests/MarkerPopup/ClusterMarker.test.jsx
index 0aca93ae..04e13384 100644
--- a/frontend/tests/MarkerPopup/ClusterMarker.test.jsx
+++ b/frontend/tests/MarkerPopup/ClusterMarker.test.jsx
@@ -2,7 +2,7 @@ import React from 'react';
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import { MapContainer } from 'react-leaflet';
-import { ClusterMarker } from '../../src/components/MarkerPopup/ClusterMarker';
+import ClusterMarker from '../../src/components/MarkerPopup/ClusterMarker';
const correctClusterData = {
position: [51.1095, 17.0525],
diff --git a/frontend/tests/MarkerPopup/LocationDetailsBox.test.jsx b/frontend/tests/MarkerPopup/LocationDetailsBox.test.jsx
index b62f0b1f..4032747b 100644
--- a/frontend/tests/MarkerPopup/LocationDetailsBox.test.jsx
+++ b/frontend/tests/MarkerPopup/LocationDetailsBox.test.jsx
@@ -1,8 +1,8 @@
import React from 'react';
import '@testing-library/jest-dom';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
-import { LocationDetailsBox } from '../../src/components/MarkerPopup/LocationDetails';
-import { toast } from '../../src/utils/toast';
+import LocationDetailsBox from '../../src/components/MarkerPopup/LocationDetails';
+import toast from '../../src/utils/toast';
const correctMarkerData = {
title: 'Most Grunwaldzki',
@@ -106,7 +106,8 @@ describe('should render marker popup correctly', () => {
});
jest.mock('../../src/utils/toast', () => ({
- toast: {
+ __esModule: true,
+ default: {
success: jest.fn(),
error: jest.fn(),
info: jest.fn(),
diff --git a/frontend/tests/MarkerPopup/MarkerClusterGroupIntegration.test.jsx b/frontend/tests/MarkerPopup/MarkerClusterGroupIntegration.test.jsx
index eadee6d8..26b446fd 100644
--- a/frontend/tests/MarkerPopup/MarkerClusterGroupIntegration.test.jsx
+++ b/frontend/tests/MarkerPopup/MarkerClusterGroupIntegration.test.jsx
@@ -3,7 +3,7 @@ import '@testing-library/jest-dom';
import { render } from '@testing-library/react';
import { MapContainer, TileLayer } from 'react-leaflet';
import MarkerClusterGroup from 'react-leaflet-cluster';
-import { MarkerPopup } from '../../src/components/MarkerPopup/MarkerPopup';
+import MarkerPopup from '../../src/components/MarkerPopup/MarkerPopup';
/**
* Integration test for MarkerPopup with MarkerClusterGroup
diff --git a/frontend/tests/MarkerPopup/MarkerPopup.test.jsx b/frontend/tests/MarkerPopup/MarkerPopup.test.jsx
index 02f82fd6..9689d6ca 100644
--- a/frontend/tests/MarkerPopup/MarkerPopup.test.jsx
+++ b/frontend/tests/MarkerPopup/MarkerPopup.test.jsx
@@ -2,8 +2,8 @@ import React from 'react';
import '@testing-library/jest-dom';
import { render, screen, fireEvent, act, waitFor } from '@testing-library/react';
import { MapContainer } from 'react-leaflet';
-import { MarkerPopup } from '../../src/components/MarkerPopup/MarkerPopup';
-import { httpService } from '../../src/services/http/httpService';
+import MarkerPopup from '../../src/components/MarkerPopup/MarkerPopup';
+import httpService from '../../src/services/http/httpService';
jest.mock('../../src/services/http/httpService');
diff --git a/frontend/tests/MarkerPopup/ReportProblemForm.test.jsx b/frontend/tests/MarkerPopup/ReportProblemForm.test.jsx
index c1a5b3c7..72f44012 100644
--- a/frontend/tests/MarkerPopup/ReportProblemForm.test.jsx
+++ b/frontend/tests/MarkerPopup/ReportProblemForm.test.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react';
-import { ReportProblemForm } from '../../src/components/MarkerPopup/ReportProblemForm';
+import ReportProblemForm from '../../src/components/MarkerPopup/ReportProblemForm';
jest.mock('axios');
const axios = require('axios');