diff --git a/.env b/.env new file mode 100644 index 0000000..fc40a43 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +SKIP_PREFLIGHT_CHECK=true +ACCESS_API_URL=https://coleman-access-api.dev.insee.io +AUTHENTICATION_MODE=anonymous \ No newline at end of file diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..1bd1ef6 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,27 @@ +{ + "parser": "babel-eslint", + "extends": ["airbnb", "prettier"], + "plugins": ["prettier"], + "rules": { + "prettier/prettier": ["error"], + "linebreak-style": 0, + "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], + "react/jsx-curly-brace-presence": "off", + "jsx-a11y/label-has-for": "off", + "no-irregular-whitespace": [1, { "skipStrings": true }] + }, + "env": { + "jest": true + }, + "settings": { + "import/resolver": { + "node": { + "paths": ["src"] + } + } + }, + "globals": { + "fetch": true, + "window": true + } +} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..f7e5b5a --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,32 @@ +name: Build + +on: + pull_request: + branches: + - "*" + +jobs: + install: + runs-on: ubuntu-latest + steps: + - name: Use Node.js 14C + uses: actions/setup-node@v1 + with: + node-version: 14 + - run: yarn --frozen-lockfile + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Use Node.js 14 + uses: actions/setup-node@v1 + with: + node-version: 14 + - run: yarn + - run: yarn run test + - run: yarn build + - name: Upload build + uses: actions/upload-artifact@v2 + with: + name: build + path: build diff --git a/.github/workflows/develop-release.yml b/.github/workflows/develop-release.yml new file mode 100644 index 0000000..023fb61 --- /dev/null +++ b/.github/workflows/develop-release.yml @@ -0,0 +1,55 @@ +name: Release Candidate + +on: + push: + branches: + - 'develop' + tags: + - '*' + +jobs: + install: + runs-on: ubuntu-latest + steps: + - name: Use Node.js 14C + uses: actions/setup-node@v1 + with: + node-version: 14 + - run: yarn --frozen-lockfile + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Use Node.js 14 + uses: actions/setup-node@v1 + with: + node-version: 14 + - run: yarn + - run: yarn build + - name: Upload build + uses: actions/upload-artifact@v2 + with: + name: build + path: build + release: + runs-on: ubuntu-latest + steps: + - name: Checkout current branch + uses: actions/checkout@v2 + - name: Get current version + id: version + uses: notiz-dev/github-action-json-property@release + with: + path: 'package.json' + prop_path: 'version' + - run: echo ${{steps.version.outputs.prop}} + - name: Release snapshot + id: release-snapshot + uses: actions/create-release@latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{steps.version.outputs.prop}}-rc + release_name: Release Candidate ${{steps.version.outputs.prop}} + draft: false + prerelease: false \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..707e0ef --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,81 @@ +name: Release & Docker + +on: + push: + branches: + - 'main' + tags: + - '*' + +jobs: + install: + runs-on: ubuntu-latest + steps: + - name: Use Node.js 14C + uses: actions/setup-node@v1 + with: + node-version: 14 + - run: yarn --frozen-lockfile + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Use Node.js 14 + uses: actions/setup-node@v1 + with: + node-version: 14 + - run: yarn + - run: yarn build + - name: Upload build + uses: actions/upload-artifact@v2 + with: + name: build + path: build + release: + runs-on: ubuntu-latest + steps: + - name: Checkout current branch + uses: actions/checkout@v2 + - name: Get current version + id: version + uses: notiz-dev/github-action-json-property@release + with: + path: 'package.json' + prop_path: 'version' + - run: echo ${{steps.version.outputs.prop}} + - name: Release snapshot + id: release-snapshot + uses: actions/create-release@latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{steps.version.outputs.prop}} + release_name: ${{steps.version.outputs.prop}} + draft: false + prerelease: false + docker: + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Download build + id: download + uses: actions/download-artifact@v2 + with: + name: build + path: build + - name: Get current version + id: version + uses: notiz-dev/github-action-json-property@release + with: + path: 'package.json' + prop_path: 'version' + - run: echo ${{steps.version.outputs.prop}} + - name: Publish to Registry + uses: elgohr/Publish-Docker-Github-Action@master + with: + name: inseefr/coleman-access + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + tags: ${{steps.version.outputs.prop}} + \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c367d5b --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# See https://help.github.com/ignore-files/ for more about ignoring files. + +# dependencies +/node_modules + +# testing +/coverage + +# production +/build + +# misc +/exemple +/graphisme +/specif +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local +*.zip + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/.k8s/deployment.yaml b/.k8s/deployment.yaml new file mode 100644 index 0000000..ceb36d5 --- /dev/null +++ b/.k8s/deployment.yaml @@ -0,0 +1,17 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: coleman-access +spec: + replicas: 1 + selector: + matchLabels: + app: coleman-access + template: + metadata: + labels: + app: coleman-access + spec: + containers: + - name: coleman-access + image: inseefr/coleman-access:0.10.0 diff --git a/.k8s/ingress.yaml b/.k8s/ingress.yaml new file mode 100644 index 0000000..4053e6e --- /dev/null +++ b/.k8s/ingress.yaml @@ -0,0 +1,21 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: coleman-access-ingress + annotations: + kubernetes.io/ingress.class: nginx +spec: + tls: + - hosts: + - particuliers.dev.insee.io + rules: + - host: particuliers.dev.insee.io + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: coleman-access-svc + port: + number: 80 diff --git a/.k8s/service.yaml b/.k8s/service.yaml new file mode 100644 index 0000000..dba0a25 --- /dev/null +++ b/.k8s/service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: coleman-access-svc +spec: + selector: + app: coleman-access + ports: + - protocol: TCP + port: 80 + targetPort: 80 diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..8a7a83b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "printWidth": 100, + "singleQuote": true, + "trailingComma": "es5", + "linebreak-style": "windows" +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7701558 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM nginx +COPY build /usr/share/nginx/html +RUN rm etc/nginx/conf.d/default.conf +COPY nginx-coleman-promotion.conf etc/nginx/conf.d/ + + +# Copy .env file and shell script to container +WORKDIR /usr/share/nginx/html +COPY ./scripts/env.sh . +COPY .env . + +# Make shell script executable +RUN chmod +x env.sh + +# Start Nginx server +CMD ["/bin/bash", "-c", "/usr/share/nginx/html/env.sh && nginx -g \"daemon off;\""] + diff --git a/README.md b/README.md new file mode 100644 index 0000000..f44b61c --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +Portail de promotion des enquêtes ménages de la plateforme coleman mis en place dans le cadre du project Métallica + diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..738e8a4 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "baseUrl": "./src" + } +} diff --git a/nginx-coleman-promotion.conf b/nginx-coleman-promotion.conf new file mode 100644 index 0000000..8313900 --- /dev/null +++ b/nginx-coleman-promotion.conf @@ -0,0 +1,29 @@ +server { + listen 80 default_server; + server_name /usr/share/nginx/html; + + root /usr/share/nginx/html; + index index.html; + + location ~* \.(?:manifest|appcache|html?|xml|json)$ { + expires -1; + # access_log logs/static.log; # I don't usually include a static log + } + + location ~* \.(?:css|js)$ { + try_files $uri =404; + expires 1y; + access_log off; + add_header Cache-Control "public"; + } + + # Any route containing a file extension (e.g. /devicesfile.js) + location ~ ^.+\..+$ { + try_files $uri =404; + } + + # Any route that doesn't have a file extension (e.g. /devices) + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..4ba0f61 --- /dev/null +++ b/package.json @@ -0,0 +1,55 @@ +{ + "name": "colempub", + "version": "0.10.0", + "dependencies": { + "axios": "^0.19.2", + "bootstrap": "^3.3.7", + "keycloak-js": "^10.0.2", + "node-sass": "^4.11.0", + "prop-types": "^15.6.2", + "react": "^16.12.0", + "react-app-polyfill": "^1.0.3", + "react-bootstrap": "^0.32.1", + "react-dom": "^16.12.0", + "react-helmet": "^5.2.1", + "react-loading": "^2.0.3", + "react-markdown": "^4.1.0", + "react-pdf": "^4.0.5", + "react-responsive-carousel": "^3.2.9", + "react-router-bootstrap": "^0.24.4", + "react-router-dom": "^4.3.1", + "react-scripts": "3.4.0" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ], + "devDependencies": { + "@types/react": "^16.9.23", + "@types/react-router-dom": "^5.1.3", + "bestzip": "^2.1.2", + "copy-and-watch": "^0.1.4", + "cross-env": "^5.2.0", + "eslint": "^6.6.0", + "eslint-config-airbnb": "^18.0.1", + "eslint-config-prettier": "^6.5.0", + "eslint-plugin-import": "^2.18.2", + "eslint-plugin-jsx-a11y": "^6.2.3", + "eslint-plugin-prettier": "^3.1.1", + "eslint-plugin-react": "^7.16.0", + "ftp-deploy": "^2.3.0", + "jest": "^24.9.0", + "prettier": "^1.18.2" + } +} diff --git a/public/configuration.json b/public/configuration.json new file mode 100644 index 0000000..7d1f1da --- /dev/null +++ b/public/configuration.json @@ -0,0 +1,5 @@ +{ + "urlColemanPromotionBack_commentaire": "access portal's api url", + "urlColemanPromotionBack": "${ACCESS_API_URL}", + "authType": "${AUTHENTICATION_MODE}" +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..8fd60d1 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/img/arm/courrier/img-01.png b/public/img/arm/courrier/img-01.png new file mode 100644 index 0000000..7882240 Binary files /dev/null and b/public/img/arm/courrier/img-01.png differ diff --git a/public/img/arm_resultats.png b/public/img/arm_resultats.png new file mode 100644 index 0000000..7882240 Binary files /dev/null and b/public/img/arm_resultats.png differ diff --git a/public/img/chi/courrier/img-01.png b/public/img/chi/courrier/img-01.png new file mode 100644 index 0000000..e8c9475 Binary files /dev/null and b/public/img/chi/courrier/img-01.png differ diff --git a/public/img/chi_resultats.png b/public/img/chi_resultats.png new file mode 100644 index 0000000..e8c9475 Binary files /dev/null and b/public/img/chi_resultats.png differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..1100600 --- /dev/null +++ b/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + Les enquêtes de l'Insee auprès des particuliers + + + + + +
+ + + diff --git a/public/keycloak.json b/public/keycloak.json new file mode 100644 index 0000000..ddda32e --- /dev/null +++ b/public/keycloak.json @@ -0,0 +1,8 @@ +{ + "realm": "insee-realm", + "auth-server-url": "http://localhost:8180/auth", + "ssl-required": "external", + "resource": "coleman-frontend", + "public-client": true, + "confidential-port": 0 +} diff --git a/scripts/env.sh b/scripts/env.sh new file mode 100644 index 0000000..4d8a177 --- /dev/null +++ b/scripts/env.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Read each line in .env file +# Each line represents key=value pairs +while read -r line || [[ -n "$line" ]]; +do + # Split env variables by character `=` + if printf '%s\n' "$line" | grep -q -e '='; then + varname=$(printf '%s\n' "$line" | sed -e 's/=.*//') + varvalue=$(printf '%s\n' "$line" | sed -e 's/^[^=]*=//') + fi + + # Read value of current variable if exists as Environment variable + value=$(printf '%s\n' "${!varname}") + # Otherwise use value from .env file + [[ -z $value ]] && value=${varvalue} + + export $varname=$value + +done < .env +envsubst < "./configuration.json" > "configuration.temp" +mv configuration.temp configuration.json diff --git a/src/App.js b/src/App.js new file mode 100644 index 0000000..6345208 --- /dev/null +++ b/src/App.js @@ -0,0 +1,67 @@ +import React, { Component } from 'react'; +import { Route, BrowserRouter, Switch } from 'react-router-dom'; +import { Helmet } from 'react-helmet'; +import Main from 'components/main'; +import Home from 'components/home'; + +/** adresse du fichier de conf. Les propriétées sont fetchées dans dans la méthode componentDidMount */ +const apiConfigPath = `${window.location.origin}/configuration.json`; + +class App extends Component { + constructor(props) { + super(props); + this.state = { urlBackEnd: null, isConfigLoaded: false, keycloakAuth: null }; + } + + componentDidMount() { + fetch(apiConfigPath) + .then(response => response.json()) + .then(data => + this.setState({ + urlBackEnd: data.urlColemanPromotionBack, + isConfigLoaded: true, + keycloakAuth: data.authType === 'keycloak', + }) + ); + } + + render() { + const { isConfigLoaded, urlBackEnd, keycloakAuth } = this.state; + + return ( + <> + + {`Répondre à une enquête internet de l’Insee auprès des particuliers`} + + + + {isConfigLoaded && ( + <> + + + ( +
+ )} + /> + + ( + + )} + /> + + + + )} + + ); + } +} +export default App; diff --git a/src/components/auth/component.js b/src/components/auth/component.js new file mode 100644 index 0000000..e4d72e6 --- /dev/null +++ b/src/components/auth/component.js @@ -0,0 +1,69 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import Loading from 'components/loading/loading'; +import ErrorComponent from 'components/template/error-component'; +import { extractQuestionnaireUrl } from 'utils/url-utils'; +import { getSurveyVerifMailById } from 'utils/read-content'; +import { getQuestionnaireUrl } from 'utils/api'; +import useAuth from 'utils/hook/auth'; +import NoSurveyPage from 'components/content/ineligible'; + +const Auth = ({ urlBackEnd, id, history, keycloakAuth }) => { + const { loading, authenticated, authError } = useAuth(keycloakAuth); + const [error, setError] = useState(null); + const [ineligible, setIneligible] = useState(false); + + const redirectToUrl = url => { + window.location = url; + }; + + const redirectToQuestionnaire = useCallback(async () => { + try { + const response = await getQuestionnaireUrl(urlBackEnd, keycloakAuth); + if (response.data && response.data.length) { + if (getSurveyVerifMailById(id) && keycloakAuth) { + history.push({ + pathname: 'repondant/mail', + state: { + urlQuestionnaire: extractQuestionnaireUrl(response), + }, + }); + } else { + redirectToUrl(extractQuestionnaireUrl(response)); + } + } else { + setIneligible(true); + } + } catch (e) { + // including 410 status - no habilitation found + setError('technique'); + } + }, [history, id, urlBackEnd]); + + useEffect(() => { + if (authenticated && !loading) redirectToQuestionnaire(); + if (!authenticated && !loading && authError) setError('authentification'); + }, [authenticated, loading, authError, redirectToQuestionnaire]); + + return ( + <> + {loading && } + {error && } + {ineligible && } + + ); +}; + +Auth.propTypes = { + urlBackEnd: PropTypes.string.isRequired, + keycloakAuth: PropTypes.bool.isRequired, + id: PropTypes.string.isRequired, + history: PropTypes.shape({ + push: PropTypes.func.isRequired, + }).isRequired, + location: PropTypes.shape({ + search: PropTypes.string.isRequired, + }).isRequired, +}; + +export default Auth; diff --git a/src/components/auth/index.js b/src/components/auth/index.js new file mode 100644 index 0000000..b404d7f --- /dev/null +++ b/src/components/auth/index.js @@ -0,0 +1 @@ +export { default } from './component'; diff --git a/src/components/content/accessibilite.js b/src/components/content/accessibilite.js new file mode 100644 index 0000000..09203e0 --- /dev/null +++ b/src/components/content/accessibilite.js @@ -0,0 +1,24 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ReactMarkdown from 'react-markdown'; +import {getAccessibleContentById} from 'utils/read-content'; + +const Accessibilite = ({ title, body, id }) => ( +
+
+

{title}

+

+ {undefined===getAccessibleContentById(id) ? + : + } +

+
+
+); + +export default Accessibilite; +Accessibilite.propTypes = { + body: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + id: PropTypes.string.isRequired, +}; diff --git a/src/components/content/donnees-personnelles.js b/src/components/content/donnees-personnelles.js new file mode 100644 index 0000000..76e6d8d --- /dev/null +++ b/src/components/content/donnees-personnelles.js @@ -0,0 +1,20 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ReactMarkdown from 'react-markdown'; + +import { getDonneesPersonnellesSpecificCartoucheTexteById , getDonneesPersonnellesSpecificContextTexteById } from 'utils/read-content'; + +const DonneesPersonnelles = ({ id }) => ( +
+
+ + +

+ +
+
+); +export default DonneesPersonnelles; +DonneesPersonnelles.propTypes = { + id: PropTypes.string.isRequired, +}; diff --git a/src/components/content/faq/component.js b/src/components/content/faq/component.js new file mode 100644 index 0000000..cb39307 --- /dev/null +++ b/src/components/content/faq/component.js @@ -0,0 +1,71 @@ +import React, { Component } from 'react'; +import { getFaqData } from 'utils/read-content'; +import PropTypes from 'prop-types'; +import FaqItem from './item'; + +class Faq extends Component { + constructor(props) { + super(props); + this.state = { itemDisplayed: 0 }; + this.handleClick = this.handleClick.bind(this); + this.handleKeyPress = this.handleKeyPress.bind(this); + } + + handleClick(id) { + const { itemDisplayed } = this.state; + if (id === itemDisplayed) this.setState({ itemDisplayed: 0 }); + else this.setState({ itemDisplayed: id }); + } + + handleKeyPress(event, id) { + const { itemDisplayed } = this.state; + if (event.keyCode === 13) { + if (id === itemDisplayed) this.setState({ itemDisplayed: 0 }); + else this.setState({ itemDisplayed: id }); + } + } + + render() { + const { enquete } = this.props; + const faqData = getFaqData(enquete); + const { itemDisplayed } = this.state; + return ( +
+

{`Questions générales`}

+ {faqData.map(({ id, title, body, type }) => + type === 'general' ? ( + + ) : null + )} + +

{`Questions relatives à l'enquête`}

+ {faqData.map(({ id, title, body, type }) => + type === 'specific' ? ( + + ) : null + )} +
+ ); + } +} +export default Faq; +Faq.propTypes = { + enquete: PropTypes.string.isRequired, +}; diff --git a/src/components/content/faq/faq.scss b/src/components/content/faq/faq.scss new file mode 100644 index 0000000..533baf3 --- /dev/null +++ b/src/components/content/faq/faq.scss @@ -0,0 +1,12 @@ +.faq-button { + all: unset; + color: #337ab7; + text-decoration: none; + &:hover { + text-decoration: underline; + } +} + +.panel-icon { + margin-right: 2%; +} diff --git a/src/components/content/faq/index.js b/src/components/content/faq/index.js new file mode 100644 index 0000000..b404d7f --- /dev/null +++ b/src/components/content/faq/index.js @@ -0,0 +1 @@ +export { default } from './component'; diff --git a/src/components/content/faq/item.js b/src/components/content/faq/item.js new file mode 100644 index 0000000..36d30c6 --- /dev/null +++ b/src/components/content/faq/item.js @@ -0,0 +1,42 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Panel } from 'react-bootstrap'; +import ReactMarkdown from 'react-markdown'; +import './faq.scss'; + +function FaqItem({ display, body, title, id, handleClick, handleKeyPress }) { + const iconClass = display + ? 'glyphicon glyphicon-chevron-down' + : 'glyphicon glyphicon-chevron-right'; + + return ( + true} + onClick={() => handleClick(id)} + onKeyPress={e => (e.key === 'Enter' ? handleClick(id) : '')} + tabIndex="0" + > + + + + {title} + + + + + + + + + ); +} +export default FaqItem; +FaqItem.propTypes = { + display: PropTypes.bool.isRequired, + body: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + id: PropTypes.number.isRequired, + handleClick: PropTypes.func.isRequired, +}; diff --git a/src/components/content/importantInfo.js b/src/components/content/importantInfo.js new file mode 100644 index 0000000..9ce33de --- /dev/null +++ b/src/components/content/importantInfo.js @@ -0,0 +1,20 @@ +import React from 'react'; +import ReactMarkdown from 'react-markdown'; +import { getImportantInfo } from 'utils/read-content'; + +const ImportantInfo = () => { + const { message, state } = getImportantInfo(); + + return ( + <> + {state && ( +
+

{message.title}

+ +
+ )} + + ); +}; + +export default ImportantInfo; diff --git a/src/components/content/ineligible.js b/src/components/content/ineligible.js new file mode 100644 index 0000000..9407c6f --- /dev/null +++ b/src/components/content/ineligible.js @@ -0,0 +1,26 @@ +import React from 'react'; +import ReactMarkdown from 'react-markdown'; +import { getIneligibleText } from 'utils/read-content'; +import PropTypes from 'prop-types'; +import { Link } from 'react-router-dom'; + +const NoSurveyPage = ({ id }) => { + return ( + <> +
+
+

+ + {` En cas de difficultés, vous pouvez `} + {`contacter l'assistance`} +

+
+
+ + ); +}; + +export default NoSurveyPage; +NoSurveyPage.propTypes = { + id: PropTypes.string.isRequired, +}; diff --git a/src/components/content/resultats-enquete.js b/src/components/content/resultats-enquete.js new file mode 100644 index 0000000..08e2acb --- /dev/null +++ b/src/components/content/resultats-enquete.js @@ -0,0 +1,26 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Image } from 'react-bootstrap'; +import ReactMarkdown from 'react-markdown'; + +function ResultatsEnquete({ title, pictureUrl, legende }) { + return ( + <> +
+

{title}

+ +
+

+ +

+
+ + ); +} +export default ResultatsEnquete; + +ResultatsEnquete.propTypes = { + title: PropTypes.string.isRequired, + pictureUrl: PropTypes.string.isRequired, + legende: PropTypes.string.isRequired, +}; diff --git a/src/components/content/survey-description.js b/src/components/content/survey-description.js new file mode 100644 index 0000000..29aa282 --- /dev/null +++ b/src/components/content/survey-description.js @@ -0,0 +1,21 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ReactMarkdown from 'react-markdown'; + +const SurveyDescription = ({ title, body }) => ( +
+
+

{title}

+

+ +

+
+
+); + +export default SurveyDescription; + +SurveyDescription.propTypes = { + title: PropTypes.string.isRequired, + body: PropTypes.string.isRequired, +}; diff --git a/src/components/content/utilite-reponse.js b/src/components/content/utilite-reponse.js new file mode 100644 index 0000000..2975dfa --- /dev/null +++ b/src/components/content/utilite-reponse.js @@ -0,0 +1,28 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ReactMarkdown from 'react-markdown'; + +import { + getResponseUseText, + getReponseUseGenericText, + getResponseUseGenericInformationText, +} from 'utils/read-content'; + +const UtiliteReponse = ({ id }) => ( +
+
+

+ + +
+

+ +
+

+
+
+); +export default UtiliteReponse; +UtiliteReponse.propTypes = { + id: PropTypes.string.isRequired, +}; diff --git a/src/components/form-elements/email.js b/src/components/form-elements/email.js new file mode 100644 index 0000000..17b66a1 --- /dev/null +++ b/src/components/form-elements/email.js @@ -0,0 +1,15 @@ +import React from 'react'; + +const Email = props => { + const { valid, touched } = props; + + const formControl = touched && !valid ? 'form-control control-error' : 'form-control'; + + return ( +
+ +
+ ); +}; + +export default Email; diff --git a/src/components/form-elements/passwordInput.js b/src/components/form-elements/passwordInput.js new file mode 100644 index 0000000..ac5413c --- /dev/null +++ b/src/components/form-elements/passwordInput.js @@ -0,0 +1,20 @@ +import React from 'react'; + +const PasswordInput = props => { + let formControl = 'form-control'; + let validationError = null; + + if (props.touched && !props.valid) { + formControl = 'form-control control-error'; + validationError =

{props.errorMessage}

; + } + + return ( +
+ + {validationError} +
+ ); +}; + +export default PasswordInput; diff --git a/src/components/form-elements/select.js b/src/components/form-elements/select.js new file mode 100644 index 0000000..4dbd10d --- /dev/null +++ b/src/components/form-elements/select.js @@ -0,0 +1,29 @@ +import React from 'react'; + +const Select = props => { + let formControl = 'form-control'; + + if (props.touched && !props.valid) { + formControl = 'form-control control-error'; + } + + return ( +
+ +
+ ); +}; + +export default Select; diff --git a/src/components/form-elements/textArea.js b/src/components/form-elements/textArea.js new file mode 100644 index 0000000..2123649 --- /dev/null +++ b/src/components/form-elements/textArea.js @@ -0,0 +1,28 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const TextArea = props => { + const { touched, valid } = props; + const formControl = touched && !valid ? 'form-control control-error' : 'form-control'; + + const myProps = { ...props }; + delete myProps.touched; + delete myProps.valid; + + return ( +
+