diff --git a/.pa11yci b/.pa11yci index e08b782f7..f7802eace 100644 --- a/.pa11yci +++ b/.pa11yci @@ -20,6 +20,7 @@ "https://staging.fellesdatakatalog.digdir.no/informationmodels/7e6ef541-86bd-398d-8cd3-fad2838a1726", "https://staging.fellesdatakatalog.digdir.no/concepts/5d358e0e-52f8-420c-b5af-23703e1ecd6c", "https://staging.fellesdatakatalog.digdir.no/dataservices/a0350cbc-19c8-3c13-a196-2ae8f5aadf2e", - "https://staging.fellesdatakatalog.digdir.no/datasets/2d1d37c9-6388-3ba7-9a7d-1ae5627a79a8" + "https://staging.fellesdatakatalog.digdir.no/datasets/2d1d37c9-6388-3ba7-9a7d-1ae5627a79a8", + "https://staging.fellesdatakatalog.digdir.no/requests" ] } diff --git a/package-lock.json b/package-lock.json index 76dad3c00..ec0f5b60d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,7 +65,7 @@ "react-router-breadcrumbs-hoc": "^4.1.0", "react-router-dom": "^5.3.4", "react-scroll": "^1.8.9", - "react-select": "^5.7.4", + "react-select": "^5.7.5", "reactstrap": "^9.1.5", "redux": "^4.2.1", "redux-api-middleware": "^3.2.1", @@ -138,7 +138,7 @@ "eslint-plugin-react": "^7.31.11", "eslint-plugin-react-hooks": "^4.6.0", "file-loader": "^6.2.0", - "fork-ts-checker-webpack-plugin": "^7.2.14", + "fork-ts-checker-webpack-plugin": "^8.0.0", "html-webpack-plugin": "^5.5.0", "husky": "^8.0.3", "identity-obj-proxy": "^3.0.0", @@ -161,7 +161,7 @@ "ts-node": "^10.9.1", "typescript": "^4.9.4", "url-loader": "^4.1.1", - "webpack": "^5.87.0", + "webpack": "^5.88.2", "webpack-bundle-analyzer": "^4.8.0", "webpack-cli": "^5.0.1", "webpack-dev-server": "^4.15.1", @@ -175,9 +175,9 @@ } }, "node_modules/@adobe/css-tools": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", - "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.1.tgz", + "integrity": "sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==", "dev": true }, "node_modules/@ampproject/remapping": { @@ -488,11 +488,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.15.tgz", - "integrity": "sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dependencies": { - "@babel/types": "^7.22.15", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -631,12 +631,12 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -853,9 +853,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.16", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.16.tgz", - "integrity": "sha512-+gPfKv8UWeKKeJTUxe59+OobVcrYHETCsORl61EmSkmgymguYk/X5bp7GuUIXaFsc6y++v8ZxPsLSSuujqDphA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -2201,18 +2201,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.20.tgz", - "integrity": "sha512-eU260mPZbU7mZ0N+X10pxXhQFMGTeLb9eFS0mxehS8HZp9o1uSnFeWQuG1UPrlxgA7QoUzFhOnilHDp0AXCyHw==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dependencies": { "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.22.15", + "@babel/generator": "^7.23.0", "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.22.5", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.16", - "@babel/types": "^7.22.19", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -2221,12 +2221,12 @@ } }, "node_modules/@babel/types": { - "version": "7.22.19", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.19.tgz", - "integrity": "sha512-P7LAw/LbojPzkgp5oznjE6tQEIWbp4PkkfrZDINTro9zgBRtI324/EYsiSI7lhPbpIQ+DCeR2NNmMWANGGfZsg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dependencies": { "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.19", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -3261,18 +3261,27 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.0.4.tgz", - "integrity": "sha512-FPFLbg2b06MIw1dqk2SOEMAMX3xlrreGjcui5OTxfBDtaKTmh0kioOVjT8gcfl58juawL/yF+S+gnq8aUYQx/Q==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz", + "integrity": "sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==", + "dependencies": { + "@floating-ui/utils": "^0.1.3" + } }, "node_modules/@floating-ui/dom": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.0.12.tgz", - "integrity": "sha512-HeG/wHoa2laUHlDX3xkzqlUqliAfa+zqV04LaKIwNCmCNaW2p0fQi4/Kd0LB4GdFoJ2UllLFq5gWnXAd67lg7w==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", + "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==", "dependencies": { - "@floating-ui/core": "^1.0.4" + "@floating-ui/core": "^1.4.2", + "@floating-ui/utils": "^0.1.3" } }, + "node_modules/@floating-ui/utils": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz", + "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==" + }, "node_modules/@fluentui/date-time-utilities": { "version": "8.5.5", "resolved": "https://registry.npmjs.org/@fluentui/date-time-utilities/-/date-time-utilities-8.5.5.tgz", @@ -13433,9 +13442,9 @@ } }, "node_modules/fork-ts-checker-webpack-plugin": { - "version": "7.2.14", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.14.tgz", - "integrity": "sha512-Tg2feh/n8k486KX0EbXVUfJj3j0xnnbKYTJw0fnIb2QdV0+lblOYZSal5ed9hARoWVwKeOC7sYE2EakSRLo5ZA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-8.0.0.tgz", + "integrity": "sha512-mX3qW3idpueT2klaQXBzrIM/pHw+T0B/V9KHEvNrqijTq9NFnMZU6oreVxDYcf33P8a5cW+67PjodNHthGnNVg==", "dev": true, "dependencies": { "@babel/code-frame": "^7.16.7", @@ -13457,13 +13466,7 @@ }, "peerDependencies": { "typescript": ">3.6.0", - "vue-template-compiler": "*", "webpack": "^5.11.0" - }, - "peerDependenciesMeta": { - "vue-template-compiler": { - "optional": true - } } }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/ansi-styles": { @@ -20177,6 +20180,23 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -21381,9 +21401,9 @@ } }, "node_modules/postcss": { - "version": "8.4.20", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.20.tgz", - "integrity": "sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ { "type": "opencollective", @@ -21392,10 +21412,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -21480,17 +21504,6 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, - "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -22332,9 +22345,9 @@ } }, "node_modules/react-select": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.7.4.tgz", - "integrity": "sha512-NhuE56X+p9QDFh4BgeygHFIvJJszO1i1KSkg/JPcIJrbovyRtI+GuOEa4XzFCEpZRAEoEI8u/cAHK+jG/PgUzQ==", + "version": "5.7.7", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.7.7.tgz", + "integrity": "sha512-HhashZZJDRlfF/AKj0a0Lnfs3sRdw/46VJIRd8IbB9/Ovr74+ZIwkAdSBjSPXsFMG+u72c5xShqwLSKIJllzqw==", "dependencies": { "@babel/runtime": "^7.12.0", "@emotion/cache": "^11.4.0", @@ -25803,9 +25816,9 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webpack": { - "version": "5.87.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.87.0.tgz", - "integrity": "sha512-GOu1tNbQ7p1bDEoFRs2YPcfyGs8xq52yyPBZ3m2VGnXGtV9MxjrkABHm4V9Ia280OefsSLzvbVoXcfLxjKY/Iw==", + "version": "5.88.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", + "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -26248,27 +26261,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/webpack-merge": { "version": "5.8.0", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", diff --git a/package.json b/package.json index 7e2b2e8d6..43ff1066c 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "react-router-breadcrumbs-hoc": "^4.1.0", "react-router-dom": "^5.3.4", "react-scroll": "^1.8.9", - "react-select": "^5.7.4", + "react-select": "^5.7.5", "reactstrap": "^9.1.5", "redux": "^4.2.1", "redux-api-middleware": "^3.2.1", @@ -179,7 +179,7 @@ "eslint-plugin-react": "^7.31.11", "eslint-plugin-react-hooks": "^4.6.0", "file-loader": "^6.2.0", - "fork-ts-checker-webpack-plugin": "^7.2.14", + "fork-ts-checker-webpack-plugin": "^8.0.0", "html-webpack-plugin": "^5.5.0", "husky": "^8.0.3", "identity-obj-proxy": "^3.0.0", @@ -202,7 +202,7 @@ "ts-node": "^10.9.1", "typescript": "^4.9.4", "url-loader": "^4.1.1", - "webpack": "^5.87.0", + "webpack": "^5.88.2", "webpack-bundle-analyzer": "^4.8.0", "webpack-cli": "^5.0.1", "webpack-dev-server": "^4.15.1", diff --git a/src/api/community-api/search.ts b/src/api/community-api/search.ts index b85e5555d..ea2fb8b9e 100644 --- a/src/api/community-api/search.ts +++ b/src/api/community-api/search.ts @@ -40,6 +40,39 @@ export const extractTopicsFromSearch = ( return uniqueTopics; }; +const buildCommunityRequestsQueryParams = ( + queryTerm: string | undefined, + page: string | undefined, + sortOption: string | undefined +) => { + const params = new URLSearchParams(); + + if (queryTerm) params.append('term', queryTerm); + if (page) params.append('page', page); + if (sortOption) params.append('sortBy', sortOption); + + return params.toString(); +}; +export const searchCommunityRequests = ( + queryTerm: string | undefined, + page: string | undefined, + sortOption: string | undefined +) => + axios + .get( + `${FDK_COMMUNITY_BASE_URI}/api/search?categories[]=6&sortDirection=desc&in=titles&matchWords=all&showAs=topics&${buildCommunityRequestsQueryParams( + queryTerm, + page, + sortOption + )}` + ) + .then(({ data }) => data); + +export const getRequestCategory = () => + axios + .get(`${FDK_COMMUNITY_BASE_URI}/api/category/6`) + .then(({ data }) => data); + export const pruneNodebbTemplateTags = (raw_text: string) => raw_text.replace( /(?:\|\s)(?:\[{2})(.*?)(?:\]{2}:)(.*?)(?:\s\|)/g, diff --git a/src/app/app-nav-bar/app-nav-bar.tsx b/src/app/app-nav-bar/app-nav-bar.tsx index 8e7e00b00..61a4df07c 100644 --- a/src/app/app-nav-bar/app-nav-bar.tsx +++ b/src/app/app-nav-bar/app-nav-bar.tsx @@ -13,7 +13,6 @@ import { PATHNAME_REPORTS, PATHNAME_ABOUT, PATHNAME_ABOUT_REGISTRATION, - PATHNAME_ABOUT_NAP, PATHNAME_AI, PATHNAME_HOME_NAP, PATHNAME_ORGANIZATIONS, @@ -21,7 +20,9 @@ import { PATHNAME_SPARQL, PATHNAME_ABOUT_DATASETS, PATHNAME_GUIDANCE, - PATHNAME_NEWS_ARCHIVE + PATHNAME_NEWS_ARCHIVE, + PATHNAME_REQUESTS, + PATHNAME_ABOUT_NAP } from '../../constants/constants'; import { themeFDK, themeNAP } from '../theme'; @@ -70,7 +71,7 @@ const fdkItems = () => [ @@ -104,7 +105,7 @@ const fdkItems = () => [ @@ -121,6 +122,9 @@ const fdkItems = () => [ {localization.menu.sparql} + + {localization.menu.requests} + , diff --git a/src/app/app.jsx b/src/app/app.jsx index d4028008f..ed537b466 100644 --- a/src/app/app.jsx +++ b/src/app/app.jsx @@ -46,7 +46,17 @@ import { PATHNAME_ABOUT_CONCEPTS, PATHNAME_ABOUT_INFORMATIONMODELS, PATHNAME_AI, - PATHNAME_TRANSPORT + PATHNAME_REQUESTS, + PATHNAME_TRANSPORT_GENERAL, + PATHNAME_TRANSPORT_GENERAL_INFO_ABOUT_THE_PORTAL, + PATHNAME_TRANSPORT_GENERAL_ITS, + PATHNAME_TRANSPORT_GENERAL_ROLES, + PATHNAME_TRANSPORT_USERS_NEWS, + PATHNAME_TRANSPORT_USERS_DATA_IN_NAP, + PATHNAME_TRANSPORT_USERS_WHAT, + PATHNAME_TRANSPORT_USERS_WHERE, + PATHNAME_TRANSPORT_PROVIDERS_ADD, + PATHNAME_TRANSPORT_PROVIDERS_COMPLIANCE } from '../constants/constants'; import ScrollToTop from '../components/scroll-to-top'; import { getConfig } from '../config'; @@ -59,6 +69,7 @@ import OrganizationsRouter from '../pages/organizations'; import InformationPage from '../pages/cms-information-page'; import TransportPage from '../pages/cms-transport-page'; import { AiProjectPage } from '../pages/ai-project-page'; +import RequestsPage from '../pages/requests'; import { parseSearchParams } from '../lib/location-history-helper'; import routes from '../routes'; @@ -113,7 +124,17 @@ export function App({ language, onChangeLanguage }) { [PATHNAME_ABOUT_CONCEPTS]: InformationPage, [PATHNAME_ABOUT_INFORMATIONMODELS]: InformationPage, [PATHNAME_AI]: AiProjectPage, - [PATHNAME_TRANSPORT]: TransportPage + [PATHNAME_REQUESTS]: RequestsPage, + [PATHNAME_TRANSPORT_GENERAL]: TransportPage, + [PATHNAME_TRANSPORT_GENERAL_INFO_ABOUT_THE_PORTAL]: TransportPage, + [PATHNAME_TRANSPORT_GENERAL_ITS]: TransportPage, + [PATHNAME_TRANSPORT_GENERAL_ROLES]: TransportPage, + [PATHNAME_TRANSPORT_USERS_NEWS]: TransportPage, + [PATHNAME_TRANSPORT_USERS_DATA_IN_NAP]: TransportPage, + [PATHNAME_TRANSPORT_USERS_WHAT]: TransportPage, + [PATHNAME_TRANSPORT_USERS_WHERE]: TransportPage, + [PATHNAME_TRANSPORT_PROVIDERS_ADD]: TransportPage, + [PATHNAME_TRANSPORT_PROVIDERS_COMPLIANCE]: TransportPage }; return ( diff --git a/src/app/breadcrumbs/breadcrumbs.component.jsx b/src/app/breadcrumbs/breadcrumbs.component.jsx index 765835d58..3ddf11898 100644 --- a/src/app/breadcrumbs/breadcrumbs.component.jsx +++ b/src/app/breadcrumbs/breadcrumbs.component.jsx @@ -24,7 +24,18 @@ import { PATHNAME_ABOUT_DATA_SERVICES, PATHNAME_ABOUT_CONCEPTS, PATHNAME_ABOUT_INFORMATIONMODELS, - PATHNAME_AI + PATHNAME_AI, + PATHNAME_TRANSPORT_GENERAL, + PATHNAME_TRANSPORT_GENERAL_INFO_ABOUT_THE_PORTAL, + PATHNAME_TRANSPORT_GENERAL_ITS, + PATHNAME_TRANSPORT_GENERAL_ROLES, + PATHNAME_TRANSPORT_USERS_NEWS, + PATHNAME_TRANSPORT_USERS_WHERE, + PATHNAME_TRANSPORT_USERS_WHAT, + PATHNAME_TRANSPORT_USERS_DATA_IN_NAP, + PATHNAME_TRANSPORT_PROVIDERS_ADD, + PATHNAME_TRANSPORT_PROVIDERS_COMPLIANCE, + PATHNAME_REQUESTS } from '../../constants/constants'; import DatasetBreadcrumb from './dataset-breadcrumb'; import DataServiceBreadcrumb from './data-service-breadcrumb'; @@ -135,6 +146,62 @@ const routes = [ { path: PATHNAME_AI, breadcrumb: () => + }, + { + path: PATHNAME_TRANSPORT_GENERAL, + breadcrumb: () => + }, + { + path: PATHNAME_TRANSPORT_GENERAL_ROLES, + breadcrumb: () => ( + + ) + }, + { + path: PATHNAME_TRANSPORT_GENERAL_ITS, + breadcrumb: () => ( + + ) + }, + { + path: PATHNAME_TRANSPORT_GENERAL_INFO_ABOUT_THE_PORTAL, + breadcrumb: () => ( + + ) + }, + { + path: PATHNAME_TRANSPORT_USERS_NEWS, + breadcrumb: () => + }, + { + path: PATHNAME_TRANSPORT_USERS_WHERE, + breadcrumb: () => ( + + ) + }, + { + path: PATHNAME_TRANSPORT_USERS_WHAT, + breadcrumb: () => ( + + ) + }, + { + path: PATHNAME_TRANSPORT_USERS_DATA_IN_NAP, + breadcrumb: () => + }, + { + path: PATHNAME_TRANSPORT_PROVIDERS_ADD, + breadcrumb: () => + }, + { + path: PATHNAME_TRANSPORT_PROVIDERS_COMPLIANCE, + breadcrumb: () => ( + + ) + }, + { + path: PATHNAME_REQUESTS, + breadcrumb: () => } ]; diff --git a/src/components/banner/index.tsx b/src/components/banner/index.tsx new file mode 100644 index 000000000..9e06b1bd7 --- /dev/null +++ b/src/components/banner/index.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import type { FC } from 'react'; +import SC from './styled'; + +import EllipseSVG from './svg/ellipse-1.svg'; +import RectangleSVG from './svg/rectangle-1.svg'; + +interface Props { + title: string; +} + +const Banner: FC = ({ title }) => ( + + + + + +

{title}

+
+ + + +
+); + +export default Banner; diff --git a/src/components/banner/styled.ts b/src/components/banner/styled.ts new file mode 100644 index 000000000..8bf2e2a14 --- /dev/null +++ b/src/components/banner/styled.ts @@ -0,0 +1,64 @@ +import styled from 'styled-components'; + +const Container = styled.div` + position: relative; + display: flex; + width: 100%; + align-items: flex-start; + color: #2d3741; + background-color: #ffffff; + overflow-wrap: break-word; + height: 160px; +`; + +const TitleContainer = styled.div` + display: flex; + width: 100%; + overflow-wrap: break-word; + flex-grow: 1; + align-self: center; + justify-content: center; + font-size: 30px; +`; + +const SvgEllipse = styled.div` + position: absolute; + padding-top: 1.6rem; + + @media screen and (max-width: 600px) { + .svgEllipse { + display: none; + } + } + + @media (max-width: 1620px) { + .svgEllipse { + position: relative; + } +`; + +const SvgRectangle = styled.div` + position: absolute; + right: 100px; + padding-bottom: 1.6rem; + + @media screen and (max-width: 940px) { + .svgRectangle { + display: none; + } + } + + @media screen and (max-width: 940px) { + .svgRectangle { + display: none; + position: relative; + } + } +`; + +export default { + SvgRectangle, + SvgEllipse, + Container, + TitleContainer +}; diff --git a/src/components/banner/svg/ellipse-1.svg b/src/components/banner/svg/ellipse-1.svg new file mode 100644 index 000000000..df665fb1c --- /dev/null +++ b/src/components/banner/svg/ellipse-1.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/banner/svg/rectangle-1.svg b/src/components/banner/svg/rectangle-1.svg new file mode 100644 index 000000000..ca21d9366 --- /dev/null +++ b/src/components/banner/svg/rectangle-1.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/filter-box/filter-box.component.tsx b/src/components/filter-box/filter-box.component.tsx index 13efe3e1e..c33930c78 100644 --- a/src/components/filter-box/filter-box.component.tsx +++ b/src/components/filter-box/filter-box.component.tsx @@ -10,7 +10,7 @@ import localization from '../../lib/localization'; import { FilterOption } from '../filter-option/filter-option.component'; import './filter-box.scss'; -import { FilterSearchOption } from '../../types'; +import { SelectOption } from '../../types'; interface Props { htmlKey?: number; @@ -197,7 +197,7 @@ export class FilterBox extends React.Component { label: label || localization.facet.formatType.UNKNOWN }; }) - .sort((a: FilterSearchOption, b: FilterSearchOption) => + .sort((a: SelectOption, b: SelectOption) => a.label.localeCompare(b.label) ); diff --git a/src/components/search-field/MagnifyingGlass.svg b/src/components/search-field/MagnifyingGlass.svg new file mode 100644 index 000000000..bae747e91 --- /dev/null +++ b/src/components/search-field/MagnifyingGlass.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/src/components/search-field/search-field.tsx b/src/components/search-field/search-field.tsx new file mode 100644 index 000000000..5bc4d00fa --- /dev/null +++ b/src/components/search-field/search-field.tsx @@ -0,0 +1,64 @@ +import React, { FC, ChangeEvent, useState, KeyboardEvent } from 'react'; +import { Input, SearchField as StyledSearchField, SvgWrapper } from './styled'; +import MagnifyingGlassSVG from './MagnifyingGlass.svg'; + +type IconPosType = 'left' | 'right'; + +interface SearchFieldProps { + ariaLabel: string; + placeholder?: string; + error?: boolean; + startIcon?: JSX.Element; + endIcon?: JSX.Element; + onSearchSubmit?: (inputValue: string) => void | any; + iconPos?: IconPosType; +} + +const SearchField: FC = ({ + ariaLabel, + startIcon, + endIcon = , + placeholder = 'Input placeholder ...', + error = false, + onSearchSubmit +}) => { + const [inputValue, setInputValue] = useState(''); + const conditionalPlaceholder = error ? 'Invalid input' : placeholder; + + const onInput = (changeEvent: ChangeEvent) => { + setInputValue(changeEvent.target.value); + + if (onSearchSubmit && changeEvent.target.value === '') { + onSearchSubmit(''); + } + }; + + const onSubmit = (event: KeyboardEvent | 'clicked') => { + if (onSearchSubmit) { + if (event === 'clicked' || event?.key === 'Enter') { + onSearchSubmit(inputValue); + } + } + }; + + return ( + + {startIcon} + + onSubmit('clicked')}>{endIcon} + + ); +}; + +export { SearchField, type SearchFieldProps }; diff --git a/src/components/search-field/styled.ts b/src/components/search-field/styled.ts new file mode 100644 index 000000000..910fc560f --- /dev/null +++ b/src/components/search-field/styled.ts @@ -0,0 +1,73 @@ +import styled, { css } from 'styled-components'; +import { SearchFieldProps } from './search-field'; + +const SearchField = styled.div` + min-width: 10rem; + max-width: 30rem; + height: 3.6rem; + background-color: #ffffff; + display: flex; + align-items: center; + box-sizing: border-box; + border: solid 1px #d5d7d9; + border-radius: 4px; + align-self: flex-end; + + :hover { + outline: 0.07rem solid #121619; + } + + svg { + ${({ iconPos }) => + iconPos && iconPos === 'right' + ? css` + margin-right: 1.2rem; + ` + : css` + margin-left: 1.2rem; + `} + } + + ${({ error }) => + error && + css` + input { + color: #803353; + } + input::placeholder { + color: #803353; + } + border-color: #803353; + background-color: #f7f0f3; + :hover { + border-color: #803353; + } + `} + + @media (max-width: 900px) { + align-self: center; + } +`; + +const Input = styled.input` + margin: 0 1.6rem 0 1.6rem; + width: 100%; + font-size: 1.6rem; + background: none; + border: 0; + + :focus-visible { + outline: none; + border: 0; + } +`; + +const SvgWrapper = styled.figure` + display: contents; + + :hover { + cursor: pointer; + } +`; + +export { Input, SearchField, SvgWrapper }; diff --git a/src/components/side-menu/index.tsx b/src/components/side-menu/index.tsx index baed92941..2893f4b55 100644 --- a/src/components/side-menu/index.tsx +++ b/src/components/side-menu/index.tsx @@ -4,8 +4,9 @@ import { NavLink as RouteLink } from 'react-router-dom'; import SC from './styled'; interface MenuItem { - id: string; + id?: string; title: string; + items?: MenuItem[]; } interface Props { @@ -16,10 +17,25 @@ interface Props { const SideMenu: FC = ({ menuItems = [], isSticky, ...props }) => (
    - {menuItems.map(({ id, title: menuItemTitle }) => ( - - {menuItemTitle} - + {menuItems.map(({ id, title: menuItemTitle, items }) => ( + <> + 0 && !id}> + {id ? ( + {menuItemTitle} + ) : ( + {menuItemTitle} + )} + + {items?.map(({ id: subId, title: subMenuItemTitle }) => ( + + {subId ? ( + {subMenuItemTitle} + ) : ( + {subMenuItemTitle} + )} + + ))} + ))}
diff --git a/src/components/side-menu/styled.ts b/src/components/side-menu/styled.ts index 97b83be1f..e25e102ca 100644 --- a/src/components/side-menu/styled.ts +++ b/src/components/side-menu/styled.ts @@ -38,11 +38,24 @@ const Menu = styled.nav` `} `; -const MenuItem = styled.li` +const MenuItem = styled.li<{ isGroup?: boolean }>` align-items: center; display: flex; list-style: none; + ${({ isGroup = false }) => + isGroup + ? css` + font-weight: ${t.fontWeight('FW500')}; + ` + : css` + &:hover { + background-color: ${({ theme }) => theme.light}; + border-radius: 5px; + flex: 1; + } + `} + & > a { color: ${({ theme }) => theme.dark}; flex: 1; @@ -57,16 +70,14 @@ const MenuItem = styled.li` flex: 1; } - &:hover { - background-color: ${({ theme }) => theme.light}; - border-radius: 5px; - flex: 1; - } - ${onMobileView} { margin: 0; padding: 12px 0; } `; -export default { Menu, MenuItem }; +const SubMenuItem = styled(MenuItem)` + margin-left: 1em; +`; + +export default { Menu, MenuItem, SubMenuItem }; diff --git a/src/components/transport-portal-footer/index.tsx b/src/components/transport-portal-footer/index.tsx index f352a81d8..ac5e8a164 100644 --- a/src/components/transport-portal-footer/index.tsx +++ b/src/components/transport-portal-footer/index.tsx @@ -3,6 +3,7 @@ import { Link as RouteLink } from 'react-router-dom'; import Link from '@fellesdatakatalog/link'; import { + PATHNAME_ABOUT_NAP, PATHNAME_ABOUT_REGISTRATION, PATHNAME_DATASETS } from '../../constants/constants'; @@ -31,9 +32,7 @@ const TransportPortalFooter: FC = () => ( {translations.footer.searchDatasets} - - {translations.menu.aboutNap} - + {translations.menu.aboutNap} {translations.footer.guideToRegister} diff --git a/src/components/with-community/index.tsx b/src/components/with-community/index.tsx index 34fcc616d..fe8d5ebfe 100644 --- a/src/components/with-community/index.tsx +++ b/src/components/with-community/index.tsx @@ -4,13 +4,21 @@ import { connect } from 'react-redux'; import * as actions from './redux/actions'; -import type { CommunityPost, CommunityTopic } from '../../types'; +import type { + CommunityCategory, + CommunityPost, + CommunityTopic, + Pagination +} from '../../types'; export interface Props { topics: CommunityTopic[]; multiplePages: boolean; posts: CommunityPost[]; communityActions: typeof actions; + requests: CommunityTopic[]; + pagination: Pagination; + requestCategory: CommunityCategory; } const withCommunity = (Component: ComponentType) => { @@ -19,7 +27,10 @@ const withCommunity = (Component: ComponentType) => { const mapStateToProps = (state: any) => ({ topics: state.CommunityReducer.get('topics').toJS(), multiplePages: state.CommunityReducer.get('multiplePages'), - posts: state.CommunityReducer.get('posts').toJS() + posts: state.CommunityReducer.get('posts').toJS(), + requests: state.CommunityReducer.get('requests').toJS(), + pagination: state.CommunityReducer.get('pagination').toJS(), + requestCategory: state.CommunityReducer.get('requestCategory').toJS() }); const mapDispatchToProps = (dispatch: Dispatch) => ({ diff --git a/src/components/with-community/redux/action-types.ts b/src/components/with-community/redux/action-types.ts index 37ceb6928..3389aea34 100644 --- a/src/components/with-community/redux/action-types.ts +++ b/src/components/with-community/redux/action-types.ts @@ -8,3 +8,14 @@ export const GET_RECENT_POSTS_FAILED = 'GET_RECENT_POSTS_FAILED' as const; export const RESET_TOPICS = 'RESET_TOPICS' as const; export const RESET_POSTS = 'RESET_POSTS' as const; + +export const SEARCH_REQUESTS_REQUESTED = 'SEARCH_REQUESTS_REQUESTED' as const; +export const SEARCH_REQUESTS_SUCCEEDED = 'SEARCH_REQUESTS_SUCCEEDED' as const; +export const SEARCH_REQUESTS_FAILED = 'SEARCH_REQUESTS_FAILED' as const; + +export const GET_REQUEST_CATEGORY_REQUESTED = + 'GET_REQUEST_CATEGORY_REQUESTED' as const; +export const GET_REQUEST_CATEGORY_SUCCEEDED = + 'GET_REQUEST_CATEGORY_SUCCEEDED' as const; +export const GET_REQUEST_CATEGORY_FAILED = + 'GET_REQUEST_CATEGORY_FAILED' as const; diff --git a/src/components/with-community/redux/actions.ts b/src/components/with-community/redux/actions.ts index b073e6e39..39567bcae 100644 --- a/src/components/with-community/redux/actions.ts +++ b/src/components/with-community/redux/actions.ts @@ -6,10 +6,21 @@ import { GET_RECENT_POSTS_SUCCEEDED, GET_RECENT_POSTS_FAILED, RESET_TOPICS, - RESET_POSTS + RESET_POSTS, + SEARCH_REQUESTS_REQUESTED, + SEARCH_REQUESTS_SUCCEEDED, + SEARCH_REQUESTS_FAILED, + GET_REQUEST_CATEGORY_REQUESTED, + GET_REQUEST_CATEGORY_SUCCEEDED, + GET_REQUEST_CATEGORY_FAILED } from './action-types'; -import type { CommunityPost, CommunityTopic } from '../../../types'; +import type { + CommunityCategory, + CommunityPost, + CommunityTopic, + Pagination +} from '../../../types'; import { CommunityTerm } from '../../../types/enums'; export function searchTopicsRequested(queryTerm: string) { @@ -43,6 +54,43 @@ export function searchTopicsFailed(message: string) { }; } +export function searchRequestsRequested( + queryTerm: string | undefined, + page: string | undefined, + sortOption: string | undefined +) { + return { + type: SEARCH_REQUESTS_REQUESTED, + payload: { + queryTerm, + page, + sortOption + } + }; +} + +export function searchRequestsSucceeded( + requests: CommunityTopic[], + pagination: Pagination +) { + return { + type: SEARCH_REQUESTS_SUCCEEDED, + payload: { + requests, + pagination + } + }; +} + +export function searchRequestsFailed(message: string) { + return { + type: SEARCH_REQUESTS_FAILED, + payload: { + message + } + }; +} + export function getRecentPostsRequested(term: CommunityTerm) { return { type: GET_RECENT_POSTS_REQUESTED, @@ -81,3 +129,29 @@ export function resetPosts() { type: RESET_POSTS }; } + +export function getRequestCategoryRequested() { + return { + type: GET_REQUEST_CATEGORY_REQUESTED + }; +} + +export function getRequestCategorySucceeded( + requestCategory: CommunityCategory +) { + return { + type: GET_REQUEST_CATEGORY_SUCCEEDED, + payload: { + requestCategory + } + }; +} + +export function getRequestCategoryFailed(message: string) { + return { + type: GET_REQUEST_CATEGORY_FAILED, + payload: { + message + } + }; +} diff --git a/src/components/with-community/redux/reducer.ts b/src/components/with-community/redux/reducer.ts index 920f1a10a..018580d38 100644 --- a/src/components/with-community/redux/reducer.ts +++ b/src/components/with-community/redux/reducer.ts @@ -9,7 +9,13 @@ import { GET_RECENT_POSTS_SUCCEEDED, GET_RECENT_POSTS_FAILED, RESET_TOPICS, - RESET_POSTS + RESET_POSTS, + SEARCH_REQUESTS_REQUESTED, + SEARCH_REQUESTS_SUCCEEDED, + SEARCH_REQUESTS_FAILED, + GET_REQUEST_CATEGORY_REQUESTED, + GET_REQUEST_CATEGORY_SUCCEEDED, + GET_REQUEST_CATEGORY_FAILED } from './action-types'; import type { Actions } from '../../../types'; @@ -17,7 +23,10 @@ import type { Actions } from '../../../types'; const initialState = fromJS({ topics: [], multiplePages: false, - posts: [] + posts: [], + requests: [], + pagination: {}, + requestCategory: {} }); export default function reducer( @@ -31,16 +40,32 @@ export default function reducer( return state .set('topics', fromJS(action.payload.topics)) .set('multiplePages', fromJS(action.payload.multiplePages)); + case SEARCH_TOPICS_FAILED: case GET_RECENT_POSTS_REQUESTED: return state.set('posts', fromJS([])); case GET_RECENT_POSTS_SUCCEEDED: return state.set('posts', fromJS(action.payload.posts)); - case SEARCH_TOPICS_FAILED: case RESET_TOPICS: return state.set('topics', fromJS([])); case GET_RECENT_POSTS_FAILED: case RESET_POSTS: return state.set('posts', fromJS([])); + case SEARCH_REQUESTS_REQUESTED: + return state.set('requests', fromJS([])); + case SEARCH_REQUESTS_SUCCEEDED: + return state + .set('requests', fromJS(action.payload.requests)) + .set('pagination', fromJS(action.payload.pagination)); + case SEARCH_REQUESTS_FAILED: + case GET_REQUEST_CATEGORY_REQUESTED: + return state.set('requestCategory', fromJS([])); + case GET_REQUEST_CATEGORY_SUCCEEDED: + return state.set( + 'requestCategory', + fromJS(action.payload.requestCategory) + ); + case GET_REQUEST_CATEGORY_FAILED: + default: return state; } diff --git a/src/components/with-community/redux/saga.ts b/src/components/with-community/redux/saga.ts index 06df1a02d..e6d009ac5 100644 --- a/src/components/with-community/redux/saga.ts +++ b/src/components/with-community/redux/saga.ts @@ -2,19 +2,27 @@ import { all, call, put, takeLatest } from 'redux-saga/effects'; import { SEARCH_TOPICS_REQUESTED, - GET_RECENT_POSTS_REQUESTED + GET_RECENT_POSTS_REQUESTED, + SEARCH_REQUESTS_REQUESTED, + GET_REQUEST_CATEGORY_REQUESTED } from './action-types'; import * as actions from './actions'; import { extractTopicsFromSearch, getRecentPosts, + getRequestCategory, getTopicById, pruneNodebbTemplateTags, - searchCommunity + searchCommunity, + searchCommunityRequests } from '../../../api/community-api/search'; -import type { CommunityPost, CommunityTopic } from '../../../types'; +import type { + CommunityCategory, + CommunityPost, + CommunityTopic +} from '../../../types'; function* searchTopicsRequested({ payload: { queryTerm } @@ -40,6 +48,55 @@ function* searchTopicsRequested({ } } +function* getRequestCategoryRequested() { + try { + const requestCategory: CommunityCategory = yield call(getRequestCategory); + + if (requestCategory !== null && requestCategory !== undefined) { + yield put(actions.getRequestCategorySucceeded(requestCategory)); + } else { + yield put(actions.getRequestCategoryFailed('')); + } + } catch (e: any) { + yield put(actions.getRequestCategoryFailed(e.message)); + } +} + +function* searchRequestsRequested({ + payload: { queryTerm, sortOption, page } +}: ReturnType) { + try { + const postHits: CommunityPost = yield call( + searchCommunityRequests, + queryTerm, + page, + sortOption + ); + const { pagination } = postHits; + + const allRequestTopics: CommunityCategory = yield call(getRequestCategory); + const { topics } = allRequestTopics; + + const requests: CommunityTopic[] = ( + (yield all( + postHits.posts.map(({ tid }) => + topics.filter(topic => topic.tid === tid) + ) + )) as CommunityTopic[] + ) + .filter(Boolean) + .flat(); + + if (requests.length > 0) { + yield put(actions.searchRequestsSucceeded(requests, pagination)); + } else { + yield put(actions.searchTopicsFailed('')); + } + } catch (e: any) { + yield put(actions.searchRequestsFailed(e.message)); + } +} + function* recentPostsRequested({ payload: { term } }: ReturnType) { @@ -63,6 +120,8 @@ function* recentPostsRequested({ export default function* saga() { yield all([ takeLatest(SEARCH_TOPICS_REQUESTED, searchTopicsRequested), - takeLatest(GET_RECENT_POSTS_REQUESTED, recentPostsRequested) + takeLatest(GET_RECENT_POSTS_REQUESTED, recentPostsRequested), + takeLatest(SEARCH_REQUESTS_REQUESTED, searchRequestsRequested), + takeLatest(GET_REQUEST_CATEGORY_REQUESTED, getRequestCategoryRequested) ]); } diff --git a/src/constants/constants.js b/src/constants/constants.js index 59cde63b1..42446559c 100644 --- a/src/constants/constants.js +++ b/src/constants/constants.js @@ -33,7 +33,20 @@ export const PATHNAME_COMMUNITY_COMMENTS = '/category/12/kommentartråder'; export const PATHNAME_AI = '/kunstig-intelligens'; export const EXTERNAL_AI_PAGE = 'https://www.digdir.no/kunstig-intelligens/kunstig-intelligens-i-offentlig-sektor/4276'; +export const PATHNAME_REQUESTS = '/requests'; export const PATHNAME_TRANSPORT = '/transport'; +export const PATHNAME_TRANSPORT_GENERAL = `${PATHNAME_TRANSPORT}/general`; +export const PATHNAME_TRANSPORT_GENERAL_ROLES = `${PATHNAME_TRANSPORT_GENERAL}/roles-and-responsibilities`; +export const PATHNAME_TRANSPORT_GENERAL_ITS = `${PATHNAME_TRANSPORT_GENERAL}/its-directive-and-delegated-regulations`; +export const PATHNAME_TRANSPORT_GENERAL_INFO_ABOUT_THE_PORTAL = `${PATHNAME_TRANSPORT_GENERAL}/information-about-the-portal`; +export const PATHNAME_TRANSPORT_USERS = `${PATHNAME_TRANSPORT}/users`; +export const PATHNAME_TRANSPORT_USERS_NEWS = `${PATHNAME_TRANSPORT_USERS}/news`; +export const PATHNAME_TRANSPORT_USERS_WHERE = `${PATHNAME_TRANSPORT_USERS}/where-do-i-find-the-data`; +export const PATHNAME_TRANSPORT_USERS_WHAT = `${PATHNAME_TRANSPORT_USERS}/what-data-is-available`; +export const PATHNAME_TRANSPORT_USERS_DATA_IN_NAP = `${PATHNAME_TRANSPORT_USERS}/data-in-nap`; +export const PATHNAME_TRANSPORT_PROVIDERS = `${PATHNAME_TRANSPORT}/providers`; +export const PATHNAME_TRANSPORT_PROVIDERS_ADD = `${PATHNAME_TRANSPORT_PROVIDERS}/add-data`; +export const PATHNAME_TRANSPORT_PROVIDERS_COMPLIANCE = `${PATHNAME_TRANSPORT_PROVIDERS}/declaration-of-compliance`; export const PARAGRAPH__BODY = 'paragraph--body'; export const PARAGRAPH__IMAGE = 'paragraph--image'; diff --git a/src/l10n/en.json b/src/l10n/en.json index 92d4f5dbc..63121de48 100644 --- a/src/l10n/en.json +++ b/src/l10n/en.json @@ -97,6 +97,7 @@ "relatedBy": "Related by", "collaborationBetween": "A collaboration between:", "dateCreated": "Date created", + "date": "Date", "head": { "title": "National Data Catalog", "description": "The National Data Catalog – data.norge.no is the public website providing an overview of descriptions of datasets, concepts, APIs and information models. The content is supplied by various establishments, both public and private. The Digitalization Agency is responsible for the operation and development of the website." @@ -165,7 +166,20 @@ "aboutDataServices": "About data service catalog", "aboutConcepts": "About concept catalog", "aboutInformationModels": "About information model catalog", - "ai": "Artificial intelligence" + "ai": "Artificial intelligence", + "requests": "Requests", + "transportGeneral": "General information", + "transportInformationAboutThePortal": "Information about the portal", + "transportRolesAndResponsibilies": "Roles and responsibilities", + "transportItsDirectiveAndDelegatedRegulations": "ITS directive and delegated regulations", + "transportUsers": "For data users", + "transportNews": "News", + "transportWhereDoIFindTheData": "Where do I find the data?", + "transportWhatDataIsAvailable": "What data is available?", + "transportDataInNap": "Data in NAP", + "transportProviders": "For data providers", + "transportAddData": "Add data", + "transportDeclarationOfCompliance": "Declaration of Compliance" }, "query": { "intro": "Example: public transport", @@ -1171,5 +1185,20 @@ "email": "E-mail:", "submitNewProject": "You can register new projects here:", "submit": "Register" + }, + "requestsPage": { + "title": "Requests", + "ingress": "Here you will find an overview of all requests published in the {lenke}. You can subscribe to, vote on, or comment on requests.", + "requests": "Requests from the Data Community", + "votes": "Number of Votes", + "views": "Number of Views", + "createRequest": "Create request", + "requestData": "Request data", + "requestDataInfo": "Do you want to request data, APIs, or anything else you can do it here. You will be redirected to datalandsbyen.no.", + "newestToOldest": "Newest to Oldest", + "mostVotes": "Most Votes", + "mostViews": "Most Views", + "sort": "Sorting", + "search": "Free text search in titles" } } diff --git a/src/l10n/nb.json b/src/l10n/nb.json index f3de55c94..a5f7ce649 100644 --- a/src/l10n/nb.json +++ b/src/l10n/nb.json @@ -97,6 +97,7 @@ "relatedBy": "Relatert av", "collaborationBetween": "Et samarbeid mellom:", "dateCreated": "Dato opprettet", + "date": "Dato", "head": { "title": "Felles datakatalog", "description": "Felles datakatalog – data.norge.no er det offentlige nettstedet som gir oversikt over beskrivelser av datasett, begrep, api-er og informasjonsmodeller. Innholdet blir levert av ulike virksomheter, offentlige og private. Det er Digitaliseringsdirektoratet som er ansvarlig for drift og utvikling av nettstedet." @@ -165,7 +166,20 @@ "aboutDataServices": "Om API-katalogen", "aboutConcepts": "Om begrepskatalogen", "aboutInformationModels": "Om informasjonsmodell-katalogen", - "ai": "Kunstig intelligens" + "ai": "Kunstig intelligens", + "transportGeneral": "Generell informasjon", + "transportInformationAboutThePortal": "Informasjon om portalen", + "transportRolesAndResponsibilies": "Roller og ansvar", + "transportItsDirectiveAndDelegatedRegulations": "ITS direktivet og forordninger", + "transportUsers": "For databrukere", + "transportNews": "Nyheter", + "transportWhereDoIFindTheData": "Hvor finner jeg data", + "transportWhatDataIsAvailable": "Hva finnes av data", + "transportDataInNap": "Data i NAP", + "transportProviders": "For datatilbydere", + "transportAddData": "Tilby data", + "transportDeclarationOfCompliance": "Samsvarserklæring", + "requests": "Etterspørsler" }, "query": { "intro": "Eksempel: kollektivtransport", @@ -1171,5 +1185,20 @@ "email": "E-post:", "submitNewProject": "Her kan du melde inn nye prosjekter:", "submit": "Meld inn" + }, + "requestsPage": { + "title": "Etterspørsler", + "ingress": "Her finner du en oversikt over alle etterspørsler publisert i {lenke}. Du kan abonnere på, stemme opp eller kommentere på etterspørsler.", + "requests": "Etterspørsler fra Datalandsbyen", + "votes": "Antall stemmer", + "views": "Antall visninger", + "createRequest": "Lag etterspørsel", + "requestData": "Etterspør data", + "requestDataInfo": "Ønsker du å etterspørre data, API-er eller annet du kan gjøre det her. Du blir sendt til Datalandsbyen.", + "newestToOldest": "Nyeste til eldste", + "mostVotes": "Flest stemmer", + "mostViews": "Flest visninger", + "sort": "Sortering", + "search": "Fritekstsøk i titler" } } diff --git a/src/l10n/nn.json b/src/l10n/nn.json index b1f098930..dd375a85b 100644 --- a/src/l10n/nn.json +++ b/src/l10n/nn.json @@ -165,7 +165,20 @@ "aboutDataServices": "Om API-katalogen", "aboutConcepts": "Om omgrepskatalogen", "aboutInformationModels": "Om informasjonsmodellkatalogen", - "ai": "Kunstig intelligens" + "ai": "Kunstig intelligens", + "requests": "Etterspurnadar", + "transportGeneral": "Generell informasjon", + "transportInformationAboutThePortal": "Informasjon om portalen", + "transportRolesAndResponsibilies": "Roller og ansvar", + "transportItsDirectiveAndDelegatedRegulations": "ITS direktivet og forordninger", + "transportUsers": "For databrukere", + "transportNews": "Nyheter", + "transportWhereDoIFindTheData": "Hvor finner jeg data", + "transportWhatDataIsAvailable": "Hva finnes av data", + "transportDataInNap": "Data i NAP", + "transportProviders": "For datatilbydere", + "transportAddData": "Tilby data", + "transportDeclarationOfCompliance": "Samsvarserklæring" }, "query": { "intro": "Eksempel: offentleg transport", @@ -1172,5 +1185,20 @@ "email": "E-post:", "submitNewProject": "Her kan du melde inn nye prosjekt:", "submit": "Meld inn" + }, + "requestsPage": { + "title": "Etterspurnadar", + "ingress": "Her finn du ei oversikt over alle etterspurnadar publisert i {lenke}. Du kan abonnere på, stemme opp eller kommentere på etterspurnadar.", + "requests": "Etterspurnadar frå Datalandsbyen", + "votes": "Antall stemmer", + "views": "Antall visningar", + "createRequest": "Lag etterspurnad", + "requestData": "Etterspør data", + "requestDataInfo": "Ynskjer du å etterspørre data, APIer eller anna kan du gjere det her. Du blir sendt til datalandsbyen.no.", + "newestToOldest": "Nyeste til eldste", + "mostVotes": "Flest stemmer", + "mostViews": "Flest visningar", + "sort": "Sortering", + "search": "Fritekstsøk i titler" } } diff --git a/src/pages/cms-information-page/styled.ts b/src/pages/cms-information-page/styled.ts index a9901668b..0a17b3ff3 100644 --- a/src/pages/cms-information-page/styled.ts +++ b/src/pages/cms-information-page/styled.ts @@ -84,7 +84,7 @@ const ImageText = styled.span` `; const SideMenu = styled(SideMenuBase)` - min-width: 180px; + min-width: 260px; ${onMobileView} { display: none; width: auto; diff --git a/src/pages/cms-transport-page/index.tsx b/src/pages/cms-transport-page/index.tsx index d4895a87f..28a1afea0 100644 --- a/src/pages/cms-transport-page/index.tsx +++ b/src/pages/cms-transport-page/index.tsx @@ -9,7 +9,18 @@ import localization from '../../lib/localization'; import { useGetTransportArticleQuery } from '../../api/generated/cms/graphql'; -import { PATHNAME_TRANSPORT } from '../../constants/constants'; +import { + PATHNAME_TRANSPORT_GENERAL, + PATHNAME_TRANSPORT_GENERAL_INFO_ABOUT_THE_PORTAL, + PATHNAME_TRANSPORT_GENERAL_ITS, + PATHNAME_TRANSPORT_GENERAL_ROLES, + PATHNAME_TRANSPORT_PROVIDERS_ADD, + PATHNAME_TRANSPORT_PROVIDERS_COMPLIANCE, + PATHNAME_TRANSPORT_USERS_DATA_IN_NAP, + PATHNAME_TRANSPORT_USERS_NEWS, + PATHNAME_TRANSPORT_USERS_WHAT, + PATHNAME_TRANSPORT_USERS_WHERE +} from '../../constants/constants'; import ErrorPage from '../error-page'; @@ -23,16 +34,26 @@ import { themeNAP } from '../../app/theme'; import SC from './styled'; import YoutubeEmbed from '../../components/youtube-embed'; +import { Entity } from '../../types/enums'; interface Props extends RouteComponentProps {} const { FDK_CMS_BASE_URI } = env; const articleIds: { [pathname: string]: string } = { - [PATHNAME_TRANSPORT]: '3' + [PATHNAME_TRANSPORT_GENERAL]: '2', + [PATHNAME_TRANSPORT_GENERAL_ROLES]: '3', + [PATHNAME_TRANSPORT_GENERAL_ITS]: '4', + [PATHNAME_TRANSPORT_GENERAL_INFO_ABOUT_THE_PORTAL]: '5', + [PATHNAME_TRANSPORT_USERS_DATA_IN_NAP]: '', + [PATHNAME_TRANSPORT_USERS_NEWS]: '6', + [PATHNAME_TRANSPORT_USERS_WHERE]: '7', + [PATHNAME_TRANSPORT_USERS_WHAT]: '8', + [PATHNAME_TRANSPORT_PROVIDERS_COMPLIANCE]: '9', + [PATHNAME_TRANSPORT_PROVIDERS_ADD]: '10' }; -const InformationPage: FC = () => { +const TransportPage: FC = () => { const [navOpen, setNavOpen] = useState(false); const [isSticky, setSticky] = useState(false); @@ -119,14 +140,62 @@ const InformationPage: FC = () => { const menuItems = [ { - id: PATHNAME_TRANSPORT, - title: 'Transport' + id: PATHNAME_TRANSPORT_GENERAL, + title: localization.menu.transportGeneral, + items: [ + { + id: PATHNAME_TRANSPORT_GENERAL_INFO_ABOUT_THE_PORTAL, + title: localization.menu.transportInformationAboutThePortal + }, + { + id: PATHNAME_TRANSPORT_GENERAL_ROLES, + title: localization.menu.transportRolesAndResponsibilies + }, + { + id: PATHNAME_TRANSPORT_GENERAL_ITS, + title: localization.menu.transportItsDirectiveAndDelegatedRegulations + } + ] + }, + { + title: localization.menu.transportUsers, + items: [ + { + id: PATHNAME_TRANSPORT_USERS_NEWS, + title: localization.menu.transportNews + }, + { + id: PATHNAME_TRANSPORT_USERS_WHERE, + title: localization.menu.transportWhereDoIFindTheData + }, + { + id: PATHNAME_TRANSPORT_USERS_WHAT, + title: localization.menu.transportWhatDataIsAvailable + }, + { + id: PATHNAME_TRANSPORT_USERS_DATA_IN_NAP, + title: localization.menu.transportDataInNap + } + ] + }, + { + title: localization.menu.transportProviders, + items: [ + { + id: PATHNAME_TRANSPORT_PROVIDERS_ADD, + title: localization.menu.transportAddData + }, + { + id: PATHNAME_TRANSPORT_PROVIDERS_COMPLIANCE, + title: localization.menu.transportDeclarationOfCompliance + } + ] } ]; return ( - - + + setNavOpen(!navOpen)}> @@ -140,9 +209,9 @@ const InformationPage: FC = () => { {navOpen && } {page()} - + ); }; -export default compose>(memo, withRouter)(InformationPage); +export default compose>(memo, withRouter)(TransportPage); diff --git a/src/pages/cms-transport-page/styled.ts b/src/pages/cms-transport-page/styled.ts index 7af7bf2b2..2a19a19d6 100644 --- a/src/pages/cms-transport-page/styled.ts +++ b/src/pages/cms-transport-page/styled.ts @@ -9,8 +9,7 @@ import HamburgerIconBase from '../../images/hamburger-menu-stroke.svg'; const onMobileView = '@media (max-width: 900px)'; const customBreakingPoint = '@media (max-width: 992px)'; -const InformationPage = styled.article` - background-color: ${({ theme: t }) => t.lighter}; +const TransportPage = styled.article` display: flex; gap: ${theme.spacing('S16', Unit.EM)}; word-break: break-word; @@ -41,6 +40,7 @@ const Article = styled.main` flex-direction: column; gap: ${theme.spacing('S10')}; z-index: 5; + overflow: hidden; `; const Title = styled.h1` @@ -83,7 +83,7 @@ const ImageText = styled.span` `; const SideMenu = styled(SideMenuBase)` - min-width: 180px; + min-width: 260px; ${onMobileView} { display: none; width: auto; @@ -107,11 +107,15 @@ const SideMenuSmall = styled(SideMenuBase)` a { background-color: transparent !important; color: ${({ theme: t }) => t.dark}; - margin-left: 20px; &.active { color: ${({ theme: t }) => t.dark} !important; } } + + a, + span { + margin-left: 20px; + } } } } @@ -154,7 +158,7 @@ const HamburgerIcon = styled(HamburgerIconBase)` `; export default { - InformationPage, + TransportPage, Aside, Article, Title, diff --git a/src/pages/requests/index.tsx b/src/pages/requests/index.tsx new file mode 100644 index 000000000..e488e3929 --- /dev/null +++ b/src/pages/requests/index.tsx @@ -0,0 +1,200 @@ +import React, { FC, useEffect, useState } from 'react'; +import { compose } from 'redux'; +import Link from '@fellesdatakatalog/link'; +import Button from '@fellesdatakatalog/button'; +import Select from 'react-select'; +import ReactPaginate from 'react-paginate'; +import { Breadcrumb } from 'reactstrap'; +import withCommunity, { + Props as CommunityProps +} from '../../components/with-community'; +import withErrorBoundary from '../../components/with-error-boundary'; +import ErrorPage from '../error-page'; +import SC from './styled'; +import { formatDate } from '../../lib/date-utils'; +import Banner from '../../components/banner'; +import localization from '../../lib/localization'; +import env from '../../env'; +import { CommunityTopic, SelectOption } from '../../types'; +import { SearchField } from '../../components/search-field/search-field'; + +const { FDK_COMMUNITY_BASE_URI } = env; +interface Props extends CommunityProps {} + +const RequestsPage: FC = ({ + requests, + pagination, + requestCategory, + communityActions: { searchRequestsRequested, getRequestCategoryRequested } +}) => { + useEffect(() => { + searchRequestsRequested(undefined, '1', undefined); + getRequestCategoryRequested(); + }, []); + + const requestDataGuideUri = 'topic/56/etterspørr-data-api'; + const notDeletedRequests = (topics: CommunityTopic[]) => + topics?.filter(topic => topic.deleted === 0); + + const [search, setSearch] = useState(); + const [sortOption, setSortOption] = useState(); + const [showAll, setShowAll] = useState(false); + + const sortOptions: SelectOption[] = [ + { + value: 'timestamp', + label: localization.requestsPage.newestToOldest + }, + { + value: 'upvotes', + label: localization.requestsPage.mostVotes + }, + { + value: 'topic.viewcount', + label: localization.requestsPage.mostViews + } + ]; + + return ( + <> + + +
+ + +

+ {localization.formatString(localization.requestsPage.ingress, { + lenke: ( + + {localization.community.title} + + ) + })} +

+
+ + + +
+ +
+

{localization.requestsPage.sort}

+