From 8eb9bcd72c08bf121b96ea23b1d371b435c7191f Mon Sep 17 00:00:00 2001 From: Julien Bouquillon Date: Mon, 17 Sep 2018 17:30:19 +0200 Subject: [PATCH] feat: add server-side routing --- pages/question.js | 53 +++++++++++++++ pages/theme.js | 128 ++++++++++++++++++++++++++++++++++++ routes.js | 10 ++- src/search/Answer.js | 17 +++++ src/search/Search.js | 104 ++++++++++++++--------------- src/search/SearchAnswer.js | 25 +++---- src/search/SearchResults.js | 20 +++--- 7 files changed, 277 insertions(+), 80 deletions(-) create mode 100644 pages/question.js create mode 100644 pages/theme.js create mode 100644 src/search/Answer.js diff --git a/pages/question.js b/pages/question.js new file mode 100644 index 0000000000..438bbc2173 --- /dev/null +++ b/pages/question.js @@ -0,0 +1,53 @@ +import React from "react"; +import { withRouter } from "next/router"; +import Head from "next/head"; +import fetch from "isomorphic-unfetch"; +import { Container, Alert } from "@socialgouv/code-du-travail-ui"; + +import Search from "../src/search/Search"; +import Answer from "../src/search/Answer"; +import api from "../conf/api.js"; + +const BigError = ({ children }) => ( + + {children} + +); + +class Question extends React.Component { + static async getInitialProps({ res, query }) { + console.log("getInitialProps", query); + return await fetch(`${api.BASE_URL}/items/faq/${query.slug}`) + .then(r => r.json()) + .then(data => ({ + data + })) + .catch(e => { + console.log("e", e); + res.statusCode = 404; + throw e; + }); + } + + render() { + const { data } = this.props; + return ( + + + {data._source.title} + + + {!data && Cette question n'a pas été trouvée} + {data && ( + + )} + + ); + } +} + +export default withRouter(Question); diff --git a/pages/theme.js b/pages/theme.js new file mode 100644 index 0000000000..bb7e3ead5e --- /dev/null +++ b/pages/theme.js @@ -0,0 +1,128 @@ +import React from "react"; +import { withRouter } from "next/router"; +import Head from "next/head"; +import { BreadCrumbs, Container, Alert } from "@socialgouv/code-du-travail-ui"; + +import find from "unist-util-find"; +import parents from "unist-util-parents"; + +import { Link } from "../routes"; +import Search from "../src/search/Search"; +import Categories from "../src/Categories"; +import themes from "../src/data/themes2"; + +const BigError = ({ children }) => ( +
+ {children} +
+
+
+); + +// check if node definition match given slug +const slugMatch = (node, slug) => + Array.isArray(node.slug) ? node.slug.join("/") === slug : node.slug === slug; + +// build list of parents data for the breadcrumbs +const getParents = node => { + const p = []; + if (node.type !== "root") { + p.push(node); + } + let cur = node && node.parent; + while (cur) { + p.push(cur); + cur = cur.parent; + } + return p.reverse(); +}; + +// return breadcrumbs components +const getBreadcrumbs = parents => + (parents && + parents.map( + (parent, i) => + i === 0 ? ( + + {parent.title} + + ) : i === parents.length - 1 ? ( + {parent.title} + ) : ( + + {parent.title} + + ) + )) || + []; + +// Theme page +class Theme extends React.Component { + static async getInitialProps({ res, query }) { + // build a unist tree from themes.json + const themeTree = parents({ + type: "root", + title: "Thèmes", + children: themes + }); + // get current theme + const theme = + find(themeTree, n => slugMatch(n, query.slug || "/")) || themeTree; + + // get theme parents for breadcrumbs + const themeParents = getParents(theme); + + if (!theme && res) { + res.statusCode = 404; + } + return { theme, parents: themeParents }; + } + + render() { + const { theme, parents } = this.props; + const breadCrumbs = getBreadcrumbs(parents); + return ( + + + {theme && theme.title} + + + + {!theme && Ce thème n'a pas été trouvé} + {(breadCrumbs && + breadCrumbs.length && ( +

+ +

+ )) || ( +

+ Choisissez un thème : +

+ )} + {(theme && + theme.children && + theme.children.length && ( + + )) || ( + + Aucun contenu actuellement disponible sur ce thème :/ + + )} +
+
+ ); + } +} + +export default withRouter(Theme); diff --git a/routes.js b/routes.js index c78d450b4c..28bbff0234 100644 --- a/routes.js +++ b/routes.js @@ -5,6 +5,12 @@ module.exports = routes() // - http://localhost:3000/ // - http://localhost:3000/?q=travail // - http://localhost:3000/questions/Zm5o72QB0wLMRXWgrAhM - .add("index", "/:type(questions)/:id") - .add("explorer", "/explorer"); + .add({ name: "question", page: "question", pattern: "/questions/:slug" }) + + .add({ name: "theme", page: "theme", pattern: "/themes/:slug+" }) // slug is an array of slugs + .add({ name: "themes", page: "theme", pattern: "/themes" }) + + .add({ name: "explorer", page: "explorer", pattern: "/explorer" }) + + .add({ name: "index", page: "index", pattern: "/" }); diff --git a/src/search/Answer.js b/src/search/Answer.js new file mode 100644 index 0000000000..be47282550 --- /dev/null +++ b/src/search/Answer.js @@ -0,0 +1,17 @@ +import React from "react"; + +import { Section } from "@socialgouv/code-du-travail-ui"; + +const Answer = ({ title, html, footer }) => ( +
+
+

{title}

+
+
+
+

{footer}

+
+
+); + +export default Answer; diff --git a/src/search/Search.js b/src/search/Search.js index 34e76ed577..b37fe199ea 100755 --- a/src/search/Search.js +++ b/src/search/Search.js @@ -1,14 +1,45 @@ import * as nodeUrl from "url"; import memoize from "memoize-state"; import React from "react"; -import { Router } from "../../routes"; import { withRouter } from "next/router"; +import { Alert, Section } from "@socialgouv/code-du-travail-ui"; -import Alert from "../common/Alert"; +import { Router } from "../../routes"; import api from "../../conf/api.js"; -import SearchAnswer from "./SearchAnswer"; import SearchResults from "./SearchResults"; -import Categories from "./Categories"; + +const Disclaimer = () => ( +

+ Ce site est en cours de construction : les données qui s'y trouvent + peuvent être erronées ou imprécises. +
+ + L'ouverture officielle du site est prévue pour 2020. + +

+); + +const SearchForm = ({ query, onChange, onKeyDown, onSubmit }) => ( +
+ + +
+); class Search extends React.Component { state = { @@ -92,12 +123,13 @@ class Search extends React.Component { render() { const { data, error, pendingXHR, query } = this.state; - const { router } = this.props; + // const { router } = this.props; + // console.log({ data, error, pendingXHR, query }); const xhrErrorJsx = error ? (
- {error.message} + {error.message}
) : null; @@ -108,63 +140,29 @@ class Search extends React.Component {

) : null; - const showAnswer = router.query && router.query.type === "questions"; - - let content = null; - if (showAnswer) { - content = ; - } else { - if (!data) { - // No query. - content = ; - } else { - content = ; - } - } - return (
-
-
+
+

Posez votre question sur le droit du travail

-

- Ce site est en cours de construction : les données qui - s'y trouvent peuvent être erronées ou imprécises. -
- - L'ouverture officielle du site est prévue pour 2020. - -

+
-
- - -
- {loadingJsx} +
-
-
+ +
+ {loadingJsx} {xhrErrorJsx} - {content} + {data && } ); } diff --git a/src/search/SearchAnswer.js b/src/search/SearchAnswer.js index 4e0f9ed247..b5883980fd 100755 --- a/src/search/SearchAnswer.js +++ b/src/search/SearchAnswer.js @@ -2,8 +2,9 @@ import React from "react"; import Alert from "../common/Alert"; import api from "../../conf/api.js"; -import FeedbackForm from "../common/FeedbackForm.js"; -import SeeAlso from "../common/SeeAlso"; +import Answer from "./Answer"; +// import FeedbackForm from "../common/FeedbackForm.js"; +// import SeeAlso from "../common/SeeAlso"; // Display the details of a single result. @@ -73,21 +74,11 @@ class SearchAnswer extends React.Component { return ( -
-
-
-
-

{data._source.title}

-
-
-
-

{source}

-
-
-
-
- - +
); } diff --git a/src/search/SearchResults.js b/src/search/SearchResults.js index 9c85e882db..cb3192dd3b 100755 --- a/src/search/SearchResults.js +++ b/src/search/SearchResults.js @@ -8,7 +8,9 @@ import { Link } from "../../routes"; const Results = ({ data }) => (
    - {data.map(result => )} + {data.map(result => ( + + ))}
); @@ -60,13 +62,15 @@ const ResultItem = ({ _id, _source, highlight }) => { ); if (isInternal) { - return ( -
  • - - {body} - -
  • - ); + if (_source.source === "faq") { + return ( +
  • + + {body} + +
  • + ); + } } return (