diff --git a/package-lock.json b/package-lock.json index 76dad3c00..c3dc3c64c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "license": "Apache-2.0", "dependencies": { "@apollo/client": "^3.8.3", + "@digdir/design-system-react": "^0.27.0", + "@digdir/design-system-tokens": "^0.7.0", "@emotion/styled": "^11.10.5", "@fellesdatakatalog/alert": "^0.3.6", "@fellesdatakatalog/button": "^0.2.10", @@ -65,7 +67,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 +140,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 +163,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,11 +177,16 @@ } }, "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/@altinn/figma-design-tokens": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@altinn/figma-design-tokens/-/figma-design-tokens-6.0.1.tgz", + "integrity": "sha512-8MbIZDZdDzeYwvQ1DaRUoVd6VkWjax/mlbq1haABwv0WX8++L426O9g2wg9YtZ+WeIa3JUSOhbiXMgpTVcXBDQ==" + }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -2870,6 +2877,27 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@digdir/design-system-react": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@digdir/design-system-react/-/design-system-react-0.27.0.tgz", + "integrity": "sha512-w4PUb6ZFso1YOXa9EDVjsNvodpN6JABMmBifNjnXrg7lDF6CUFeqcq6Fn1jiFPj9bW8bLY/2fiVGJ7RwlAE0ow==", + "dependencies": { + "@altinn/figma-design-tokens": "^6.0.1", + "@digdir/design-system-tokens": "^0.7.0", + "@floating-ui/react": "0.25.2", + "@navikt/aksel-icons": "^3.2.4", + "react-number-format": "5.2.2" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@digdir/design-system-tokens": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@digdir/design-system-tokens/-/design-system-tokens-0.7.0.tgz", + "integrity": "sha512-6DwU6+KciZ8r0EZSBq0puSLlR5q5rejr95u6OLcdGCvUoQvr26j3ks/wk3Q/1MoPd/FM1hNgDmeu7neoTvYfSQ==" + }, "node_modules/@digitalbazaar/http-client": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@digitalbazaar/http-client/-/http-client-1.2.0.tgz", @@ -3261,18 +3289,53 @@ } }, "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/react": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.25.2.tgz", + "integrity": "sha512-3e10G9LFOgl32/SMWLBOwT7oVCtB+d5zBsU2GxTSVOvRgZexwno5MlYbc0BaXr+TR5EEGpqe9tg9OUbjlrVRnQ==", + "dependencies": { + "@floating-ui/react-dom": "^2.0.1", + "@floating-ui/utils": "^0.1.1", + "tabbable": "^6.0.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.2.tgz", + "integrity": "sha512-5qhlDvjaLmAst/rKb3VdlCinwTF4EYMiVxuuc/HVUjs46W0zgtbMmAZ1UTsDrRTxRmUEzl92mOtWbeeXL26lSQ==", + "dependencies": { + "@floating-ui/dom": "^1.5.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "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", @@ -6239,6 +6302,11 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, + "node_modules/@navikt/aksel-icons": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@navikt/aksel-icons/-/aksel-icons-3.4.2.tgz", + "integrity": "sha512-9fj+Jb4UrX3SDDzqJpKP3fvizdxtACIFNXgDiPaP/N/61pbeY4tZz/zrtWGtExLpDeg7dAyaiFH/hfo7nBg6nw==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -13433,9 +13501,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 +13525,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": { @@ -22182,6 +22244,18 @@ "react": "^16.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-number-format": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.2.2.tgz", + "integrity": "sha512-wCh64Z1HCwXcO2dbgkeYIaB+Rmp/fcsH8kAeRtUkc46dv1pIrgDjie2WkOqKBw8YqyqhwNdYgNFNQuuY+iGJ/g==", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-paginate": { "version": "8.1.4", "resolved": "https://registry.npmjs.org/react-paginate/-/react-paginate-8.1.4.tgz", @@ -22332,9 +22406,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", @@ -24403,6 +24477,11 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -25803,9 +25882,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 +26327,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..e06d4b537 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,8 @@ }, "dependencies": { "@apollo/client": "^3.8.3", + "@digdir/design-system-react": "^0.27.0", + "@digdir/design-system-tokens": "^0.7.0", "@emotion/styled": "^11.10.5", "@fellesdatakatalog/alert": "^0.3.6", "@fellesdatakatalog/button": "^0.2.10", @@ -106,7 +108,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 +181,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 +204,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..c2117309c 100644 --- a/src/api/community-api/search.ts +++ b/src/api/community-api/search.ts @@ -24,6 +24,10 @@ export const getRecentPosts = (term: CommunityTerm) => .get(`${FDK_COMMUNITY_BASE_URI}/api/recent/posts/${term}`) .then(({ data }) => data); +export const getRequests = () => + axios + .get(`${FDK_COMMUNITY_BASE_URI}/api/category/6`) + .then(({ data }) => data); export const extractTopicsFromSearch = ( searchResponse: any ): CommunityTopic[] => { diff --git a/src/app/app.jsx b/src/app/app.jsx index d4028008f..fbda08b88 100644 --- a/src/app/app.jsx +++ b/src/app/app.jsx @@ -3,6 +3,7 @@ import { Redirect, Route, Switch } from 'react-router-dom'; import { Helmet } from 'react-helmet'; import PropTypes from 'prop-types'; import cx from 'classnames'; +import '@digdir/design-system-tokens/brand/digdir/tokens.css'; import localization from '../lib/localization'; import { MainPage } from '../pages/main-page/main-page'; import { SearchPage } from '../pages/search-page/search-page'; @@ -46,7 +47,8 @@ import { PATHNAME_ABOUT_CONCEPTS, PATHNAME_ABOUT_INFORMATIONMODELS, PATHNAME_AI, - PATHNAME_TRANSPORT + PATHNAME_TRANSPORT, + PATHNAME_REQUESTS } from '../constants/constants'; import ScrollToTop from '../components/scroll-to-top'; import { getConfig } from '../config'; @@ -58,6 +60,7 @@ import { CmsArticlePage } from '../pages/cms-article-page/cms-article-page'; import OrganizationsRouter from '../pages/organizations'; import InformationPage from '../pages/cms-information-page'; import TransportPage from '../pages/cms-transport-page'; +import RequestsPage from '../pages/requests'; import { AiProjectPage } from '../pages/ai-project-page'; import { parseSearchParams } from '../lib/location-history-helper'; import routes from '../routes'; @@ -113,7 +116,8 @@ export function App({ language, onChangeLanguage }) { [PATHNAME_ABOUT_CONCEPTS]: InformationPage, [PATHNAME_ABOUT_INFORMATIONMODELS]: InformationPage, [PATHNAME_AI]: AiProjectPage, - [PATHNAME_TRANSPORT]: TransportPage + [PATHNAME_TRANSPORT]: TransportPage, + [PATHNAME_REQUESTS]: RequestsPage }; return ( diff --git a/src/components/banner/index.tsx b/src/components/banner/index.tsx new file mode 100644 index 000000000..51bc0101c --- /dev/null +++ b/src/components/banner/index.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import type { FC } from 'react'; +import { Heading } from '@digdir/design-system-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..1bf6901a4 --- /dev/null +++ b/src/components/banner/styled.ts @@ -0,0 +1,63 @@ +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; +`; + +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/with-community/index.tsx b/src/components/with-community/index.tsx index 34fcc616d..1f60cb002 100644 --- a/src/components/with-community/index.tsx +++ b/src/components/with-community/index.tsx @@ -4,13 +4,18 @@ import { connect } from 'react-redux'; import * as actions from './redux/actions'; -import type { CommunityPost, CommunityTopic } from '../../types'; +import type { + CommunityPost, + CommunityRequestCategory, + CommunityTopic +} from '../../types'; export interface Props { topics: CommunityTopic[]; multiplePages: boolean; posts: CommunityPost[]; communityActions: typeof actions; + requests: CommunityRequestCategory; } const withCommunity = (Component: ComponentType) => { @@ -19,7 +24,8 @@ 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() }); 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..f711add72 100644 --- a/src/components/with-community/redux/action-types.ts +++ b/src/components/with-community/redux/action-types.ts @@ -8,3 +8,7 @@ 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 GET_REQUESTS = 'GET_REQUESTS' as const; +export const GET_REQUESTS_SUCCEEDED = 'GET_REQUESTS_SUCCEEDED' as const; +export const GET_REQUESTS_FAILED = 'GET_REQUESTS_FAILED' as const; diff --git a/src/components/with-community/redux/actions.ts b/src/components/with-community/redux/actions.ts index b073e6e39..f3af3ae1b 100644 --- a/src/components/with-community/redux/actions.ts +++ b/src/components/with-community/redux/actions.ts @@ -6,10 +6,17 @@ import { GET_RECENT_POSTS_SUCCEEDED, GET_RECENT_POSTS_FAILED, RESET_TOPICS, - RESET_POSTS + RESET_POSTS, + GET_REQUESTS, + GET_REQUESTS_FAILED, + GET_REQUESTS_SUCCEEDED } from './action-types'; -import type { CommunityPost, CommunityTopic } from '../../../types'; +import type { + CommunityPost, + CommunityRequestCategory, + CommunityTopic +} from '../../../types'; import { CommunityTerm } from '../../../types/enums'; export function searchTopicsRequested(queryTerm: string) { @@ -69,6 +76,31 @@ export function getRecentPostsFailed(message: string) { } }; } +export function getCommunityRequests() { + return { + type: GET_REQUESTS + }; +} + +export function getCommunityRequestsSucceeded( + requests: CommunityRequestCategory +) { + return { + type: GET_REQUESTS_SUCCEEDED, + payload: { + requests + } + }; +} + +export function getCommunityRequestsFailed(message: string) { + return { + type: GET_REQUESTS_FAILED, + payload: { + message + } + }; +} export function resetTopics() { return { diff --git a/src/components/with-community/redux/reducer.ts b/src/components/with-community/redux/reducer.ts index 920f1a10a..1d6e29a62 100644 --- a/src/components/with-community/redux/reducer.ts +++ b/src/components/with-community/redux/reducer.ts @@ -9,7 +9,9 @@ import { GET_RECENT_POSTS_SUCCEEDED, GET_RECENT_POSTS_FAILED, RESET_TOPICS, - RESET_POSTS + RESET_POSTS, + GET_REQUESTS, + GET_REQUESTS_SUCCEEDED } from './action-types'; import type { Actions } from '../../../types'; @@ -17,7 +19,8 @@ import type { Actions } from '../../../types'; const initialState = fromJS({ topics: [], multiplePages: false, - posts: [] + posts: [], + requests: {} }); export default function reducer( @@ -41,6 +44,10 @@ export default function reducer( case GET_RECENT_POSTS_FAILED: case RESET_POSTS: return state.set('posts', fromJS([])); + case GET_REQUESTS: + return state.set('requests', fromJS([])); + case GET_REQUESTS_SUCCEEDED: + return state.set('requests', fromJS(action.payload.requests)); default: return state; } diff --git a/src/components/with-community/redux/saga.ts b/src/components/with-community/redux/saga.ts index 06df1a02d..0c6e19db3 100644 --- a/src/components/with-community/redux/saga.ts +++ b/src/components/with-community/redux/saga.ts @@ -2,7 +2,8 @@ import { all, call, put, takeLatest } from 'redux-saga/effects'; import { SEARCH_TOPICS_REQUESTED, - GET_RECENT_POSTS_REQUESTED + GET_RECENT_POSTS_REQUESTED, + GET_REQUESTS } from './action-types'; import * as actions from './actions'; @@ -11,10 +12,15 @@ import { getRecentPosts, getTopicById, pruneNodebbTemplateTags, - searchCommunity + searchCommunity, + getRequests } from '../../../api/community-api/search'; -import type { CommunityPost, CommunityTopic } from '../../../types'; +import type { + CommunityPost, + CommunityRequestCategory, + CommunityTopic +} from '../../../types'; function* searchTopicsRequested({ payload: { queryTerm } @@ -60,9 +66,24 @@ function* recentPostsRequested({ } } +function* getCommunityRequests() { + try { + const requests: CommunityRequestCategory = yield call(getRequests); + + if (requests !== null && requests !== undefined) { + yield put(actions.getCommunityRequestsSucceeded(requests)); + } else { + yield put(actions.getCommunityRequestsFailed('')); + } + } catch (e: any) { + yield put(actions.getCommunityRequestsFailed(e.message)); + } +} + export default function* saga() { yield all([ takeLatest(SEARCH_TOPICS_REQUESTED, searchTopicsRequested), - takeLatest(GET_RECENT_POSTS_REQUESTED, recentPostsRequested) + takeLatest(GET_RECENT_POSTS_REQUESTED, recentPostsRequested), + takeLatest(GET_REQUESTS, getCommunityRequests) ]); } diff --git a/src/constants/constants.js b/src/constants/constants.js index 59cde63b1..a21e967eb 100644 --- a/src/constants/constants.js +++ b/src/constants/constants.js @@ -34,6 +34,7 @@ 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_TRANSPORT = '/transport'; +export const PATHNAME_REQUESTS = '/requests'; export const PARAGRAPH__BODY = 'paragraph--body'; export const PARAGRAPH__IMAGE = 'paragraph--image'; diff --git a/src/pages/requests/index.tsx b/src/pages/requests/index.tsx new file mode 100644 index 000000000..3197a182e --- /dev/null +++ b/src/pages/requests/index.tsx @@ -0,0 +1,45 @@ +import React, { FC, useEffect } from 'react'; +import { compose } from 'redux'; +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'; + +interface Props extends CommunityProps {} + +const RequestsPage: FC = ({ + requests, + communityActions: { getCommunityRequests } +}) => { + useEffect(() => { + getCommunityRequests(); + }, []); + + return ( +
+ + Etterspørsler fra Datalandsbyen + Dato + Antall stemmer + Antall visninger + + {requests?.topics && + requests.topics.map(topic => ( + + {topic.title} + + {formatDate(new Date(topic.timestampISO))} + + {topic.upvotes} + {topic.viewcount} + + ))} +
+ ); +}; + +const enhance = compose(withCommunity, withErrorBoundary(ErrorPage)); +export default enhance(RequestsPage); diff --git a/src/pages/requests/styled.ts b/src/pages/requests/styled.ts new file mode 100755 index 000000000..625ee773b --- /dev/null +++ b/src/pages/requests/styled.ts @@ -0,0 +1,44 @@ +import styled from 'styled-components'; + +const RequestRow = styled.div` + display: flex; + background-color: white; + width: 100%; + height: 90px; + margin-bottom: 10px; + border-radius: 8px; + border: solid 1px #d5d7d9; + padding-left: 37px; + padding-right: 37px; + align-items: center; + font-size: 18px; +`; + +const RequestsTitleRow = styled.div` + background-color: #d2eafd; + height: 57px; + border-radius: 8px; + display: flex; + align-items: center; + padding-left: 37px; + padding-right: 37px; + font-weight: bold; +`; + +const RequestTitle = styled.div` + width: 50%; + font-weight: bold; +`; + +const RequestInfo = styled.div` + width: 16%; + display: flex; + justify-content: center; +`; + +export default { + RequestRow, + RequestsTitleRow, + RequestTitle, + RequestInfo +}; diff --git a/src/routes.ts b/src/routes.ts index 52adb35c7..9529bc315 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -26,7 +26,8 @@ import { PATHNAME_ABOUT_CONCEPTS, PATHNAME_ABOUT_INFORMATIONMODELS, PATHNAME_AI, - PATHNAME_TRANSPORT + PATHNAME_TRANSPORT, + PATHNAME_REQUESTS } from './constants/constants'; const routes: any = { @@ -61,7 +62,8 @@ const routes: any = { PATHNAME_ABOUT_CONCEPTS, PATHNAME_ABOUT_INFORMATIONMODELS, PATHNAME_AI, - PATHNAME_TRANSPORT + PATHNAME_TRANSPORT, + PATHNAME_REQUESTS ], publishing: [ PATHNAME_PUBLISHING, diff --git a/src/types/domain.d.ts b/src/types/domain.d.ts index 818ec234e..ea0f257b1 100644 --- a/src/types/domain.d.ts +++ b/src/types/domain.d.ts @@ -1019,6 +1019,27 @@ export interface CommunityPost { multiplePages: boolean; } +export interface CommunityRequestCategory { + description: string; + name: string; + post_count: number; + topics: CommunityRequest[]; +} + +export interface CommunityRequest { + cid: number; + timestamp: number; + lastposttime: number; + lastposttimeISO: Date; + timestampISO: Date; + postcount: number; + title: string; + viewcount: number; + postercount: number; + downvotes: number; + upvotes: number; +} + export interface CommunityTeaser { pid: number; uid: number;