├── .prettierrc.js ├── client ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── index.html ├── src │ ├── themes │ │ ├── index.js │ │ ├── dark.js │ │ └── light.js │ ├── components │ │ ├── Headlines │ │ │ ├── index.js │ │ │ ├── H3.js │ │ │ ├── H2.js │ │ │ └── H1.js │ │ ├── Forms │ │ │ ├── FieldGroup.js │ │ │ ├── FormReport.js │ │ │ ├── BiggerField.js │ │ │ ├── Field.js │ │ │ ├── IssueCrisisPotential.js │ │ │ ├── index.js │ │ │ ├── TaskEntry.js │ │ │ ├── TextEntry.js │ │ │ ├── DetailLink.js │ │ │ ├── LinkEntry.js │ │ │ ├── Radio.js │ │ │ ├── Switch.js │ │ │ └── Toggle.js │ │ ├── Buttons │ │ │ ├── NoStyleButton.js │ │ │ ├── Button.js │ │ │ ├── SvgButton.js │ │ │ ├── TextButton.js │ │ │ ├── SubmitButton.js │ │ │ ├── EnlargeButton.js │ │ │ ├── SVGSquareButton.js │ │ │ ├── index.js │ │ │ ├── SvgTextButton.js │ │ │ ├── SvgTextFooterButton.js │ │ │ └── SliderDotsButton.js │ │ ├── ContainerFlexRow.js │ │ ├── ContainerFlexCol.js │ │ ├── LogoCompanyUrl.js │ │ ├── Main.js │ │ ├── Aside.js │ │ ├── Logo.js │ │ ├── Footer.js │ │ ├── Task.js │ │ ├── CreateMemo.js │ │ ├── Header.js │ │ ├── LogoCompanySvg.js │ │ ├── LogoCompanySvgColored.js │ │ └── Issue.js │ ├── stories │ │ ├── Logo.stories.js │ │ ├── Button.stories.js │ │ └── LogoCompanyUrl.stories.js │ ├── assets │ │ ├── images │ │ │ ├── tasks.svg │ │ │ └── incidentmanagerlogo.svg │ │ ├── SVGIcon.js │ │ ├── SVGIconSmall.js │ │ ├── Icons │ │ │ ├── Next.js │ │ │ ├── Todo.js │ │ │ ├── Plus.js │ │ │ ├── Avatar.js │ │ │ ├── Tasks.js │ │ │ ├── Done.js │ │ │ ├── index.js │ │ │ ├── Enlarge.js │ │ │ ├── Demonstration.js │ │ │ ├── Fire.js │ │ │ ├── Accident.js │ │ │ ├── Weather.js │ │ │ ├── FireAnimated.js │ │ │ ├── Strike.js │ │ │ ├── Spillage.js │ │ │ └── Theft.js │ │ └── SVGIncidentManagerBig.js │ ├── setupTests.js │ ├── App.test.js │ ├── hooks │ │ ├── useFetch.js │ │ └── useSessionStorage.js │ ├── index.js │ ├── pages │ │ ├── index.js │ │ ├── Settings.js │ │ ├── ReportFive.js │ │ ├── IssueList.js │ │ ├── SendMemo.js │ │ ├── StartScreen.js │ │ ├── TaskList.js │ │ ├── Login.js │ │ ├── ReportOne.js │ │ ├── Summary.js │ │ ├── ReportTwo.js │ │ ├── ReportFour.js │ │ └── ReportThree.js │ ├── GlobalStyles.js │ ├── utils │ │ └── calculateCrisisPotential.js │ ├── App.js │ └── serviceWorker.js ├── .storybook │ ├── preview-head.html │ ├── addons.js │ └── config.js └── package.json ├── now.json ├── app.json ├── .gitignore ├── .eslintrc.js ├── lib └── db.js ├── mailgun.js ├── express.js ├── package.json ├── server.js ├── README.md └── db.json /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "singleQuote": false 3 | } 4 | -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: / 4 | -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manuelwecker/incidentmanager/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manuelwecker/incidentmanager/HEAD/client/public/logo192.png -------------------------------------------------------------------------------- /client/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manuelwecker/incidentmanager/HEAD/client/public/logo512.png -------------------------------------------------------------------------------- /client/src/themes/index.js: -------------------------------------------------------------------------------- 1 | export { default as light } from "./light.js"; 2 | export { default as dark } from "./dark.js"; 3 | -------------------------------------------------------------------------------- /client/src/components/Headlines/index.js: -------------------------------------------------------------------------------- 1 | export { default as H1 } from "./H1.js"; 2 | export { default as H2 } from "./H2.js"; 3 | export { default as H3 } from "./H3.js"; 4 | -------------------------------------------------------------------------------- /client/.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /client/src/components/Forms/FieldGroup.js: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | 3 | const FieldGroup = styled.div` 4 | margin: 4px 0 0 0; 5 | `; 6 | 7 | export default FieldGroup; 8 | -------------------------------------------------------------------------------- /client/.storybook/addons.js: -------------------------------------------------------------------------------- 1 | import "@storybook/addon-actions/register"; 2 | import "@storybook/addon-links/register"; 3 | import "@storybook/addon-knobs/register"; 4 | import "@storybook/addon-viewport/register"; 5 | -------------------------------------------------------------------------------- /client/src/components/Buttons/NoStyleButton.js: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | 3 | const NoStyleButton = styled.button` 4 | color: ${props => props.theme.colors.secondary}; 5 | `; 6 | 7 | export default NoStyleButton; 8 | -------------------------------------------------------------------------------- /client/src/stories/Logo.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Logo from "../components/Logo"; 3 | 4 | export default { 5 | title: "AppDesign| Logo general" 6 | }; 7 | 8 | export function IssueReporter() { 9 | return ; 10 | } 11 | -------------------------------------------------------------------------------- /now.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "issuereporter", 3 | "version": 2, 4 | "builds": [ 5 | { 6 | "src": "package.json", 7 | "use": "@now/static-build", 8 | "config": { "distDir": "client/storybook-static" } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /client/src/components/ContainerFlexRow.js: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | 3 | const ContainerFlexRow = styled.div` 4 | display: flex; 5 | flex-direction: row; 6 | justify-content: center; 7 | vertical-align: middle; 8 | `; 9 | 10 | export default ContainerFlexRow; 11 | -------------------------------------------------------------------------------- /client/src/assets/images/tasks.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/ContainerFlexCol.js: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | 3 | const ContainerFlexCol = styled.div` 4 | display: flex; 5 | flex-direction: column; 6 | justify-content: center; 7 | vertical-align: middle; 8 | `; 9 | 10 | export default ContainerFlexCol; 11 | -------------------------------------------------------------------------------- /client/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import "@testing-library/jest-dom/extend-expect"; 6 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "incidentmanager", 3 | "scripts": {}, 4 | "env": {}, 5 | "formation": { 6 | "web": { 7 | "quantity": 1 8 | } 9 | }, 10 | "addons": [], 11 | "buildpacks": [ 12 | { 13 | "url": "heroku/nodejs" 14 | } 15 | ], 16 | "stack": "heroku-18" 17 | } 18 | -------------------------------------------------------------------------------- /client/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "@testing-library/react"; 3 | import App from "./App"; 4 | 5 | test("renders learn react link", () => { 6 | const { getByText } = render(); 7 | const linkElement = getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /client/src/components/Headlines/H3.js: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | 3 | const H3 = styled.h3` 4 | display: block; 5 | width: 100%; 6 | height: 16px; 7 | padding: 0px; 8 | margin: 0px; 9 | text-align: left; 10 | font-size: 14px; 11 | color: ${props => props.theme.colors.font}; 12 | `; 13 | 14 | export default H3; 15 | -------------------------------------------------------------------------------- /client/src/components/Forms/FormReport.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | 4 | const Form = styled.form` 5 | text-decoration: unset; 6 | margin: 0px; 7 | padding: 0px; 8 | `; 9 | 10 | function FormReport(props) { 11 | return
; 12 | } 13 | 14 | export default FormReport; 15 | -------------------------------------------------------------------------------- /client/src/components/Headlines/H2.js: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | 3 | const H2 = styled.h2` 4 | display: block; 5 | width: 100%; 6 | height: 24px; 7 | padding: 0px; 8 | margin: 0px; 9 | text-align: left; 10 | font-size: 14px; 11 | color: ${props => props.theme.colors.fontSofter}; 12 | `; 13 | 14 | export default H2; 15 | -------------------------------------------------------------------------------- /client/src/components/Headlines/H1.js: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | 3 | const H1 = styled.h1` 4 | display: block; 5 | width: 100%; 6 | height: 32px; 7 | padding: 0px; 8 | margin: 0px; 9 | text-align: left; 10 | font-size: 18px; 11 | color: ${props => props.theme.colors.corporateDesignPrimary}; 12 | `; 13 | 14 | export default H1; 15 | -------------------------------------------------------------------------------- /client/src/components/Buttons/Button.js: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | 3 | const Button = styled.button` 4 | min-width: 34px; 5 | min-height: 34px; 6 | margin: 4px; 7 | padding: 4px; 8 | background-color: ${props => props.theme.colors.primary}; 9 | border-radius: ${props => props.theme.company.borderRadius}; 10 | border: none; 11 | `; 12 | 13 | export default Button; 14 | -------------------------------------------------------------------------------- /client/src/components/Buttons/SvgButton.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | 4 | const SvgButtonOnly = styled.button` 5 | min-width: 34px; 6 | min-height: 34px; 7 | background-color: white; 8 | `; 9 | 10 | function SvgButton({ svg, ...other }) { 11 | return {svg}; 12 | } 13 | 14 | export default SvgButton; 15 | -------------------------------------------------------------------------------- /client/src/components/Forms/BiggerField.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import Field from "./Field"; 4 | 5 | const BiggerField = styled(Field)` 6 | min-height: 136px; 7 | max-height: auto; 8 | 9 | display: flex; 10 | flex-direction: column; 11 | justify-content: flex; 12 | padding: 4px 0 12px 0; 13 | `; 14 | 15 | export default BiggerField; 16 | -------------------------------------------------------------------------------- /client/src/components/LogoCompanyUrl.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | 4 | const placeholder = "/assets/images/greenchem.png"; 5 | 6 | const LogoCompany = styled.img` 7 | width: 100px; 8 | `; 9 | 10 | export default function LogoCompanyUrl({ src }) { 11 | const imageUrl = src ? src : placeholder; 12 | return ; 13 | } 14 | -------------------------------------------------------------------------------- /client/src/assets/SVGIcon.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function SVGIcon(props) { 4 | return ( 5 | 15 | {props.children} 16 | 17 | ); 18 | } 19 | 20 | export default SVGIcon; 21 | -------------------------------------------------------------------------------- /client/src/components/Buttons/TextButton.js: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | import Button from "./Button"; 3 | 4 | const TextButton = styled(Button)` 5 | min-width: 100%; 6 | min-height: 28px; 7 | background-color: ${props => props.theme.colors.corporateDesignSecondary}; 8 | color: ${props => props.theme.colors.background}; 9 | font-weight: 400; 10 | border: none; 11 | `; 12 | 13 | export default TextButton; 14 | -------------------------------------------------------------------------------- /client/src/assets/SVGIconSmall.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function SVGIconSmall(props) { 4 | return ( 5 | 15 | {props.children} 16 | 17 | ); 18 | } 19 | 20 | export default SVGIconSmall; 21 | -------------------------------------------------------------------------------- /client/src/components/Main.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | 4 | const Container = styled.div` 5 | padding: 4px; 6 | max-height: calc(100%-72px); 7 | margin: 0 0 +72px 0; 8 | display: flex; 9 | flex-direction: column; 10 | justify-content: flex-start; 11 | overflow: auto; 12 | `; 13 | function Main(props) { 14 | return ; 15 | } 16 | 17 | export default Main; 18 | -------------------------------------------------------------------------------- /client/src/hooks/useFetch.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function useFetch(url) { 4 | const [data, setData] = React.useState(null); 5 | 6 | React.useEffect(() => { 7 | async function doFetch() { 8 | const response = await fetch(url); 9 | const newData = await response.json(); 10 | setData(newData); 11 | } 12 | 13 | doFetch(); 14 | }, []); 15 | 16 | return data; 17 | } 18 | 19 | export default useFetch; 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | /.pnp 6 | .pnp.js 7 | .env 8 | 9 | # testing 10 | /coverage 11 | 12 | # production 13 | build 14 | storybook-static 15 | client/build 16 | 17 | # misc 18 | .DS_Store 19 | .env.local 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | -------------------------------------------------------------------------------- /client/src/stories/Button.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { action } from "@storybook/addon-actions"; 3 | import { text } from "@storybook/addon-knobs"; 4 | import { Button } from "../components/Buttons"; 5 | 6 | export default { 7 | title: "UserInput| Buttons" 8 | }; 9 | 10 | export function TextButtonVersion() { 11 | return ( 12 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./App"; 4 | import * as serviceWorker from "./serviceWorker"; 5 | 6 | ReactDOM.render(, document.getElementById("root")); 7 | 8 | // If you want your app to work offline and load faster, you can change 9 | // unregister() to register() below. Note this comes with some pitfalls. 10 | // Learn more about service workers: https://bit.ly/CRA-PWA 11 | serviceWorker.unregister(); 12 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true, 5 | node: true, 6 | jest: true 7 | }, 8 | extends: ["eslint:recommended", "plugin:react/recommended"], 9 | globals: { 10 | Atomics: "readonly", 11 | SharedArrayBuffer: "readonly" 12 | }, 13 | parserOptions: { 14 | ecmaFeatures: { 15 | jsx: true 16 | }, 17 | ecmaVersion: 2018, 18 | sourceType: "module" 19 | }, 20 | plugins: ["react"], 21 | rules: {} 22 | }; 23 | -------------------------------------------------------------------------------- /client/src/components/Aside.js: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | 3 | const Aside = styled.div` 4 | background-position: absolute; 5 | position: absolute; 6 | left: 0px; 7 | bottom: +20px; 8 | height: 52px; 9 | width: 100%; 10 | background-color: ${props => props.theme.colors.primary}; 11 | padding: 4px; 12 | @media only screen and (min-width: ${props => 13 | props.theme.company.deviceWidth}) { 14 | max-width: ${props => props.theme.company.deviceWidth}; 15 | } 16 | `; 17 | 18 | export default Aside; 19 | -------------------------------------------------------------------------------- /client/src/components/Buttons/SubmitButton.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import Button from "./Button"; 4 | import { Avatar } from "../../assets/Icons"; 5 | 6 | const SubmitButtonForm = styled(Button)` 7 | display: flex; 8 | `; 9 | 10 | function SubmitButton({ text }) { 11 | return ( 12 | 13 | {text} 14 | 17 | 18 | ); 19 | } 20 | 21 | export default SubmitButton; 22 | -------------------------------------------------------------------------------- /client/src/assets/Icons/Next.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import SVGIconSmall from "../SVGIconSmall"; 4 | 5 | const Path = styled.path` 6 | fill: ${props => props.theme.colors.corporateDesignSecondary}; 7 | `; 8 | 9 | function Next() { 10 | return ( 11 | 12 | <> 13 | 14 | 15 | 16 | 17 | ); 18 | } 19 | 20 | export default Next; 21 | -------------------------------------------------------------------------------- /client/src/components/Forms/Field.js: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | 3 | const Field = styled.div` 4 | display: flex; 5 | align-items: center; 6 | justify-content: flex-start; 7 | min-height: 34px; 8 | max-height: auto; 9 | width: 100%; 10 | background-color: ${props => props.theme.colors.primary}; 11 | border: none; 12 | color: ${props => props.theme.colors.fontSofter}; 13 | border-radius: ${props => props.theme.company.borderRadius}; 14 | font-size: 14px; 15 | margin: 4px 0 0 0; 16 | `; 17 | 18 | export default Field; 19 | -------------------------------------------------------------------------------- /client/src/themes/dark.js: -------------------------------------------------------------------------------- 1 | const dark = { 2 | company: { 3 | logo: "", 4 | deviceWidth: "500px", 5 | borderRadius: "4px" 6 | }, 7 | colors: { 8 | corporateDesignPrimary: "#004da0", 9 | corporateDesignSecondary: "#00a6eb", 10 | background: "#333333", 11 | 12 | primary: "#52565A", 13 | secondary: "#333333", 14 | tertiary: "#7e8890", 15 | 16 | warningLevel: { 3: "#b70000", 2: "#f49d00", 1: "#7EB61C" }, 17 | 18 | font: "#ffffff", 19 | fontSofter: "#bec4c7" 20 | } 21 | }; 22 | 23 | export default dark; 24 | -------------------------------------------------------------------------------- /client/src/themes/light.js: -------------------------------------------------------------------------------- 1 | const light = { 2 | company: { 3 | logo: "", 4 | deviceWidth: "500px", 5 | borderRadius: "4px" 6 | }, 7 | colors: { 8 | corporateDesignPrimary: "#004da0", 9 | corporateDesignSecondary: "#00a6eb", 10 | background: "#eeeeee", 11 | 12 | primary: "#ffffff", 13 | secondary: "#b1b1b1", 14 | tertiary: "#bec4c7", 15 | 16 | warningLevel: { 3: "#b70000", 2: "#f49d00", 1: "#7EB61C" }, 17 | 18 | font: "#333333", 19 | fontlighter: "#7e8890" 20 | } 21 | }; 22 | 23 | export default light; 24 | -------------------------------------------------------------------------------- /lib/db.js: -------------------------------------------------------------------------------- 1 | const MongoClient = require("MongoDB").MongoClient; 2 | 3 | let db = null; 4 | 5 | async function dbInit(url, dbName) { 6 | const client = new MongoClient(url, { 7 | useNewUrlParser: true, 8 | useUnifiedTopology: true 9 | }); 10 | await client.connect(); 11 | db = client.db(dbName); 12 | } 13 | 14 | async function getCollection(collectionName) { 15 | if (!db) { 16 | throw new Error("Not initialized"); 17 | } 18 | return db.collection(collectionName); 19 | } 20 | 21 | exports.dbInit = dbInit; 22 | exports.getCollection = getCollection; 23 | -------------------------------------------------------------------------------- /client/src/assets/Icons/Todo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import SVGIconSmall from "../SVGIconSmall"; 4 | 5 | const Path = styled.path` 6 | fill: ${props => props.theme.colors.corporateDesignSecondary}; 7 | `; 8 | 9 | function Todo() { 10 | return ( 11 | 12 | <> 13 | 14 | 15 | 16 | 17 | ); 18 | } 19 | 20 | export default Todo; 21 | -------------------------------------------------------------------------------- /client/src/assets/Icons/Plus.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import SVGIconSmall from "../SVGIconSmall"; 4 | 5 | const Path = styled.path` 6 | fill: ${props => props.theme.colors.corporateDesignSecondary}; 7 | `; 8 | 9 | function Plus(props) { 10 | return ( 11 | 12 | 13 | 14 | 15 | ); 16 | } 17 | 18 | export default Plus; 19 | -------------------------------------------------------------------------------- /client/src/assets/Icons/Avatar.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import SVGIconSmall from "../SVGIconSmall"; 4 | 5 | const Path = styled.path` 6 | fill: ${props => props.theme.colors.corporateDesignSecondary}; 7 | `; 8 | 9 | function Avatar() { 10 | return ( 11 | 12 | <> 13 | 14 | 15 | 16 | 17 | ); 18 | } 19 | 20 | export default Avatar; 21 | -------------------------------------------------------------------------------- /client/src/assets/Icons/Tasks.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import SVGIconSmall from "../SVGIconSmall"; 4 | 5 | const Path = styled.path` 6 | fill: ${props => props.theme.colors.corporateDesignSecondary}; 7 | `; 8 | 9 | function Tasks(props) { 10 | return ( 11 | 12 | 13 | 14 | 15 | ); 16 | } 17 | 18 | export default Tasks; 19 | -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /client/src/assets/Icons/Done.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import SVGIconSmall from "../SVGIconSmall"; 4 | 5 | const Path = styled.path` 6 | fill: ${props => props.theme.colors.corporateDesignSecondary}; 7 | `; 8 | 9 | function Done() { 10 | return ( 11 | 12 | <> 13 | 14 | 15 | 16 | 17 | ); 18 | } 19 | 20 | export default Done; 21 | -------------------------------------------------------------------------------- /client/src/components/Forms/IssueCrisisPotential.js: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | 3 | const IssueCrisisPotential = styled.div` 4 | width: 34px; 5 | height: 34px; 6 | margin: 0px; 7 | padding: 4px; 8 | color: #ffffff; 9 | 10 | background-color: ${props => 11 | props.theme.colors.warningLevel[props.crisisPotential] || 12 | props.theme.colors.secondary}; 13 | border-radius: 4px; 14 | border: none; 15 | text-align: center; 16 | font-weight: bold; 17 | vertical-align: middle; 18 | display: flex; 19 | justify-content: center; 20 | align-items: center; 21 | `; 22 | 23 | export default IssueCrisisPotential; 24 | -------------------------------------------------------------------------------- /client/src/components/Buttons/EnlargeButton.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import { Enlarge } from "../../assets/Icons"; 4 | 5 | const SvgButtonOnly = styled.button` 6 | width: 34px; 7 | height: 34px; 8 | background-color: ${props => props.theme.colors.secondary}; 9 | margin: 0px; 10 | padding: 0px; 11 | border-radius: 4px; 12 | border: none; 13 | `; 14 | 15 | function EnlargeButton(props) { 16 | return ( 17 | <> 18 | 19 | 20 | 21 | 22 | ); 23 | } 24 | 25 | export default EnlargeButton; 26 | -------------------------------------------------------------------------------- /client/src/components/Buttons/SVGSquareButton.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import { Avatar } from "../../assets/Icons"; 4 | 5 | const SvgWrapper = styled.div` 6 | width: 34px; 7 | height: 34px; 8 | background-color: none; 9 | background-color: ${props => props.theme.colors.primary}; 10 | margin: 4px; 11 | padding: 0px; 12 | border-radius: 4px; 13 | border: none; 14 | display: flex; 15 | align-items: center; 16 | justify-content: center; 17 | `; 18 | 19 | function SVGSquareButton({ children }) { 20 | return {children}; 21 | } 22 | 23 | export default SVGSquareButton; 24 | -------------------------------------------------------------------------------- /client/src/stories/LogoCompanyUrl.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import LogoCompanyUrl from "../components/LogoCompanyUrl"; 3 | import LogoCompanySvg from "../components/LogoCompanySvg"; 4 | import { text } from "@storybook/addon-knobs"; 5 | 6 | export default { 7 | title: "AppDesign| Logo Company" 8 | }; 9 | 10 | export function LogoFromUrlWithoutUrl() { 11 | return ; 12 | } 13 | 14 | export function LogoFromUrlWithUrl() { 15 | return ( 16 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /mailgun.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | const API_KEY = process.env.API_KEY_MAILGUN; 3 | const DOMAIN = process.env.DOMAIN_MAILGUN; 4 | const mailgun = require("mailgun-js")({ apiKey: API_KEY, domain: DOMAIN }); 5 | 6 | const emailtext = "A new issue has been reported"; 7 | 8 | const data = { 9 | from: "alert@incidentmanager.eu", 10 | to: "manuelwecker@web.de", 11 | subject: "New issues on IncidentManager", 12 | text: `Important: ${emailtext}! Please check open tasks and follow the instructions in the IncidentManager.app` 13 | }; 14 | 15 | mailgun.messages().send(data, (error, body) => { 16 | console.log(body); 17 | console.log(data.text); 18 | }); 19 | -------------------------------------------------------------------------------- /client/src/hooks/useSessionStorage.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function useSessionStorage(key, defaultValue) { 4 | let initialValue; 5 | try { 6 | const item = sessionStorage.getItem(key); 7 | // initialValue = JSON.parse(item) || defaultValue; 8 | initialValue = item || defaultValue; 9 | } catch (error) { 10 | initialValue = defaultValue; 11 | } 12 | 13 | const [value, setValue] = React.useState(initialValue); 14 | 15 | React.useEffect(() => { 16 | // sessionStorage.setItem(key, JSON.stringify(value)); 17 | sessionStorage.setItem(key, value); 18 | }, [value, key]); 19 | 20 | return [value, setValue]; 21 | } 22 | -------------------------------------------------------------------------------- /client/src/components/Logo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import LogoCompanySvg from "./LogoCompanySvg"; 4 | 5 | const SvgWrapper = styled.div` 6 | width: 100px; 7 | max-height: 32px; 8 | margin: 4px 0 0 0; 9 | `; 10 | 11 | const Container = styled.div` 12 | display: flex; 13 | flex-direction: column; 14 | align-items: center; 15 | font-size: 8px; 16 | color: #ffffff; 17 | `; 18 | 19 | function Logo() { 20 | return ( 21 | 22 | IncidentManager 23 | 24 | 25 | 26 | 27 | ); 28 | } 29 | 30 | export default Logo; 31 | -------------------------------------------------------------------------------- /client/src/components/Buttons/index.js: -------------------------------------------------------------------------------- 1 | export { default as Button } from "./Button.js"; 2 | export { default as TextButton } from "./TextButton.js"; 3 | export { default as SvgButton } from "./SvgButton.js"; 4 | export { default as SvgTextButton } from "./SvgTextButton.js"; 5 | export { default as SubmitButton } from "./SubmitButton.js"; 6 | export { default as SliderDotsButton } from "./SliderDotsButton.js"; 7 | export { default as EnlargeButton } from "./EnlargeButton.js"; 8 | export { default as SvgTextFooterButton } from "./SvgTextFooterButton"; 9 | export { default as SVGSquareButton } from "./SVGSquareButton.js"; 10 | export { default as NoStyleButton } from "./NoStyleButton.js"; 11 | -------------------------------------------------------------------------------- /client/src/components/Forms/index.js: -------------------------------------------------------------------------------- 1 | export { default as FormReport } from "./FormReport.js"; 2 | export { default as Switch } from "./Switch.js"; 3 | export { default as Field } from "./Field.js"; 4 | export { default as FieldGroup } from "./FieldGroup.js"; 5 | export { default as Radio } from "./Radio.js"; 6 | export { default as TextEntry } from "./TextEntry.js"; 7 | export { default as TaskEntry } from "./TaskEntry.js"; 8 | export { default as DetailLink } from "./DetailLink.js"; 9 | export { default as LinkEntry } from "./LinkEntry.js"; 10 | export { default as BiggerField } from "./BiggerField.js"; 11 | export { default as IssueCrisisPotential } from "./IssueCrisisPotential.js"; 12 | -------------------------------------------------------------------------------- /client/src/pages/index.js: -------------------------------------------------------------------------------- 1 | export { default as ReportOne } from "./ReportOne.js"; 2 | export { default as ReportTwo } from "./ReportTwo.js"; 3 | export { default as ReportThree } from "./ReportThree.js"; 4 | export { default as ReportFour } from "./ReportFour.js"; 5 | export { default as ReportFive } from "./ReportFive.js"; 6 | export { default as IssueList } from "./IssueList.js"; 7 | export { default as SendMemo } from "./SendMemo.js"; 8 | export { default as StartScreen } from "./StartScreen.js"; 9 | export { default as Summary } from "./Summary.js"; 10 | export { default as TaskList } from "./TaskList.js"; 11 | export { default as Settings } from "./Settings.js"; 12 | export { default as Login } from "./Login.js"; 13 | -------------------------------------------------------------------------------- /express.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | 3 | const app = express(); 4 | 5 | app.get("/api/users", async (request, response) => { 6 | response.json(["leon", "marwin"]); 7 | }); 8 | 9 | // Serve any static files 10 | app.use(express.static(path.join(__dirname, "client/build"))); 11 | 12 | // Handle React routing, return all requests to React app 13 | app.get("*", function(req, res) { 14 | res.sendFile(path.join(__dirname, "client/build", "index.html")); 15 | }); 16 | 17 | initDb(process.env.MONGO_URL, process.env.DB_NAME).then(() => { 18 | console.log("DB initialized"); 19 | app.listen(process.env.PORT, () => { 20 | console.log(`Server ready on http://localhost:${process.env.PORT}`); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /client/src/assets/Icons/index.js: -------------------------------------------------------------------------------- 1 | export { default as Accident } from "./Accident"; 2 | export { default as Enlarge } from "./Enlarge"; 3 | export { default as Tasks } from "./Tasks"; 4 | export { default as Avatar } from "./Avatar"; 5 | export { default as Plus } from "./Plus"; 6 | export { default as Next } from "./Next"; 7 | export { default as Todo } from "./Todo"; 8 | export { default as Done } from "./Done"; 9 | export { default as Fire } from "./Fire"; 10 | export { default as Theft } from "./Theft"; 11 | export { default as Spillage } from "./Spillage"; 12 | export { default as Demonstration } from "./Demonstration"; 13 | export { default as FireAnimated } from "./FireAnimated"; 14 | export { default as Weather } from "./Weather"; 15 | -------------------------------------------------------------------------------- /client/src/components/Buttons/SvgTextButton.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | 4 | const SvgButtonOnly = styled.button` 5 | width: 80px; 6 | height: 80px; 7 | margin: 4px; 8 | background-color: ${props => props.theme.colors.background}; 9 | flex-direction: column; 10 | justify-content: center; 11 | align-items: center; 12 | display: inline-flex; 13 | `; 14 | 15 | function SvgTextButton({ svg, text, ...other }) { 16 | return ( 17 | 18 | {svg} 19 | {text} 20 | 21 | ); 22 | } 23 | 24 | export default SvgTextButton; 25 | 26 | // const a = styled(SvgTextButton)` 27 | // color: red; 28 | // `; 29 | // export default a; 30 | -------------------------------------------------------------------------------- /client/src/components/Forms/TaskEntry.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import { SVGSquareButton } from "../Buttons"; 4 | 5 | const TextEntryWrapper = styled.div` 6 | width: 100%; 7 | height: 44px; 8 | border-bottom: 1px solid ${props => props.theme.colors.background}; 9 | display: flex; 10 | flex-direction: row; 11 | justify-content: space-between; 12 | align-items: center; 13 | padding: 0 0 0 4px; 14 | margin: 0px; 15 | } 16 | `; 17 | 18 | function TaskEntry({ text, svg, children }) { 19 | return ( 20 | 21 | {text} 22 | 23 | {svg} 24 | {children} 25 | 26 | 27 | ); 28 | } 29 | 30 | export default TaskEntry; 31 | -------------------------------------------------------------------------------- /client/src/GlobalStyles.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Global, css } from "@emotion/core"; 3 | 4 | function GlobalStyles() { 5 | return ( 6 | css` 8 | *, 9 | *:before, 10 | *:after { 11 | box-sizing: border-box; 12 | } 13 | input[type="text"] { 14 | all: unset; 15 | padding: 0 0 0 4px; 16 | } 17 | a { 18 | text-decoration: none; 19 | color: ${theme.colors.font}; 20 | } 21 | 22 | body { 23 | font-size: 16px; 24 | margin: 0; 25 | background: ${theme.colors.background}; 26 | font-family: "Istok Web", "Frutiger", "Arial", sans-serif; 27 | } 28 | `} 29 | /> 30 | ); 31 | } 32 | 33 | export default GlobalStyles; 34 | -------------------------------------------------------------------------------- /client/src/pages/Settings.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useLocation, Link } from "react-router-dom"; 3 | import { H1, H2, H3 } from "../components/Headlines"; 4 | import Aside from "../components/Aside"; 5 | import { SvgTextFooterButton } from "../components/Buttons"; 6 | import { Next } from "../assets/Icons"; 7 | 8 | function Settings() { 9 | const location = useLocation(); 10 | return ( 11 | <> 12 |

Set up your company and personal profile

13 |

Settings

14 | 15 |

Under construction

16 | client management is not part of the MVP. 17 | 18 | 26 | 27 | ); 28 | } 29 | 30 | export default Settings; 31 | -------------------------------------------------------------------------------- /client/src/utils/calculateCrisisPotential.js: -------------------------------------------------------------------------------- 1 | function calculateCrisisPotential(typeStored) { 2 | let resultCrisisPotential = "1"; 3 | switch (typeStored) { 4 | case "demonstration": 5 | resultCrisisPotential = "3"; 6 | console.log(typeStored); 7 | console.log(resultCrisisPotential); 8 | break; 9 | case `strike`: 10 | resultCrisisPotential = "2"; 11 | console.log(typeStored); 12 | console.log(resultCrisisPotential); 13 | break; 14 | case `"fire"`: 15 | resultCrisisPotential = "1"; 16 | console.log(typeStored); 17 | console.log(resultCrisisPotential); 18 | break; 19 | case "": 20 | console.log("type not selected"); 21 | break; 22 | case null: 23 | console.log("type not yet defined"); 24 | break; 25 | default: 26 | console.log("why default: error?"); 27 | } 28 | return resultCrisisPotential; 29 | } 30 | 31 | export default calculateCrisisPotential; 32 | -------------------------------------------------------------------------------- /client/src/components/Forms/TextEntry.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import { SVGSquareButton } from "../../components/Buttons"; 4 | import { Link } from "react-router-dom"; 5 | 6 | const LinkWrapper = styled(Link)` 7 | width: 100%; 8 | display: block; 9 | `; 10 | 11 | const TextEntryWrapper = styled.div` 12 | width: 100%; 13 | height: 44px; 14 | border-bottom: 1px solid ${props => props.theme.colors.background}; 15 | display: flex; 16 | flex-direction: row; 17 | justify-content: space-between; 18 | align-items: center; 19 | padding: 0 0 0 4px; 20 | margin: 0px; 21 | } 22 | `; 23 | 24 | function TextEntry({ sessionStorageValue, svg, children }) { 25 | return ( 26 | 27 | {sessionStorage.getItem(sessionStorageValue)} 28 | 29 | {svg} 30 | {children} 31 | 32 | 33 | ); 34 | } 35 | 36 | export default TextEntry; 37 | -------------------------------------------------------------------------------- /client/src/assets/Icons/Enlarge.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import SVGIconSmall from "../SVGIconSmall"; 4 | 5 | const Path = styled.path` 6 | fill: ${props => props.theme.colors.corporateDesignSecondary}; 7 | `; 8 | 9 | function Enlarge(props) { 10 | return ( 11 | 12 | {props.isClicked ? ( 13 | 20 | ) : ( 21 | 22 | )} 23 | 24 | ); 25 | } 26 | 27 | export default Enlarge; 28 | -------------------------------------------------------------------------------- /client/src/components/Forms/DetailLink.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import { SVGSquareButton } from "../Buttons"; 4 | import { Link } from "react-router-dom"; 5 | 6 | const LinkWrapper = styled(Link)` 7 | width: 100%; 8 | display: block; 9 | `; 10 | 11 | const TextEntryWrapper = styled.div` 12 | width: 100%; 13 | height: 44px; 14 | border-bottom: 1px solid ${props => props.theme.colors.background}; 15 | display: flex; 16 | flex-direction: row; 17 | justify-content: space-between; 18 | align-items: center; 19 | padding: 0 0 0 4px; 20 | margin: 0px; 21 | } 22 | `; 23 | 24 | function DetailLink({ url, text, svg, children }) { 25 | return ( 26 | 27 | 28 | {text} 29 | 30 | {svg} 31 | {children} 32 | 33 | 34 | 35 | ); 36 | } 37 | 38 | export default DetailLink; 39 | -------------------------------------------------------------------------------- /client/.storybook/config.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { configure, addDecorator } from "@storybook/react"; 3 | import GlobalStyles from "../src/GlobalStyles"; 4 | import { ThemeProvider } from "emotion-theming"; 5 | import light from "../src/themes/light"; 6 | import dark from "../src/themes/dark"; 7 | import { withKnobs } from "@storybook/addon-knobs"; 8 | import { select } from "@storybook/addon-knobs"; 9 | 10 | function chooseTheme(choice) { 11 | if (choice === "Dark") { 12 | return dark; 13 | } 14 | if (choice === "Light") { 15 | return light; 16 | } 17 | } 18 | 19 | const GlobalStyleDecorator = storyFn => { 20 | return ( 21 | 24 | 25 | {storyFn()} 26 | 27 | ); 28 | }; 29 | 30 | addDecorator(GlobalStyleDecorator); 31 | addDecorator(withKnobs); 32 | 33 | configure(require.context("../src/stories", true, /\.stories\.js$/), module); 34 | -------------------------------------------------------------------------------- /client/src/components/Buttons/SvgTextFooterButton.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import SVGSquareButton from "./SVGSquareButton"; 4 | 5 | const ButtonWrapper = styled.button` 6 | display: flex; 7 | align-items: center; 8 | justify-content: space-between; 9 | height: 44px; 10 | min-width: 200px; 11 | width: 100%; 12 | background-color: ${props => props.theme.colors.corporateDesignSecondary}; 13 | border: none; 14 | color: ${props => props.theme.colors.primary}; 15 | border-radius: ${props => props.theme.company.borderRadius}; 16 | font-size: 14px; 17 | font-weight: 400; 18 | margin: 0px; 19 | padding: 0 0 0 4px; 20 | `; 21 | 22 | const Span = styled.span` 23 | margin: 4px; 24 | `; 25 | 26 | function SvgTextFooterButton({ svg, text, ...other }) { 27 | return ( 28 | 29 | {text} 30 | {svg} 31 | 32 | ); 33 | } 34 | 35 | export default SvgTextFooterButton; 36 | -------------------------------------------------------------------------------- /client/src/pages/ReportFive.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { SliderDotsButton, SvgTextFooterButton } from "../components/Buttons"; 3 | import { H1, H2, H3 } from "../components/Headlines"; 4 | import { useLocation, Link } from "react-router-dom"; 5 | import Aside from "../components/Aside"; 6 | import { Next } from "../assets/Icons"; 7 | import { Field, FieldGroup, Switch } from "../components/Forms"; 8 | 9 | export default function ReportFive() { 10 | const [value, setValue] = React.useState(true); 11 | console.log(value); 12 | 13 | const location = useLocation(false); 14 | return ( 15 | <> 16 |

Toogle:

17 |

Toogle?

18 | 19 | setValue(!value)} /> 20 | 21 | 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /client/src/assets/Icons/Demonstration.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import SVGIcon from "../SVGIcon"; 4 | 5 | const Path = styled.path` 6 | fill: ${props => props.theme.colors.corporateDesignSecondary}; 7 | `; 8 | 9 | function Demonstration(props) { 10 | return ( 11 | 12 | 13 | 14 | ); 15 | } 16 | 17 | export default Demonstration; 18 | -------------------------------------------------------------------------------- /client/src/components/Forms/LinkEntry.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import { SVGSquareButton } from "../Buttons"; 4 | import { Link } from "react-router-dom"; 5 | 6 | const LinkWrapper = styled(Link)` 7 | width: 100%; 8 | display: block; 9 | `; 10 | 11 | const TextEntryWrapper = styled.div` 12 | width: 100%; 13 | height: 44px; 14 | border-bottom: 1px solid ${props => props.theme.colors.background}; 15 | display: flex; 16 | flex-direction: row; 17 | justify-content: space-between; 18 | align-items: center; 19 | padding: 0 0 0 4px; 20 | margin: 0px; 21 | } 22 | `; 23 | 24 | function LinkEntry({ url, sessionStorageValue, svg, children }) { 25 | return ( 26 | 27 | 28 | {sessionStorage.getItem(sessionStorageValue)} 29 | 30 | {/* */} 31 | {svg} 32 | {children} 33 | 34 | 35 | 36 | ); 37 | } 38 | 39 | export default LinkEntry; 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "issuereporter", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build-client": "cd client && npm run build", 8 | "build-storybook": "cd client && npm run build-storybook", 9 | "build": "npm run build-storybook && npm run build-client", 10 | "client": "cd client && npm start", 11 | "server": "node server.js", 12 | "storybook": "cd client && npm run storybook", 13 | "test": "cd client && npm test", 14 | "postinstall": "cd client && npm install", 15 | "fake-api": "json-server --watch db.json --port 7070", 16 | "start": "node server.js" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/manuelwecker/issuereporter.git" 21 | }, 22 | "author": "", 23 | "license": "UNLICENSED", 24 | "bugs": { 25 | "url": "https://github.com/manuelwecker/issuereporter/issues" 26 | }, 27 | "homepage": "https://github.com/manuelwecker/issuereporter#readme", 28 | "devDependencies": { 29 | "@storybook/addon-actions": "^5.2.8", 30 | "@storybook/addon-viewport": "^5.2.8", 31 | "eslint": "^6.7.2", 32 | "eslint-plugin-react": "^7.17.0", 33 | "json-server": "^0.15.1", 34 | "nodemon": "^2.0.2", 35 | "prettier": "^1.19.1" 36 | }, 37 | "dependencies": { 38 | "dotenv": "^8.2.0", 39 | "express": "^4.17.1", 40 | "mailgun-js": "^0.22.0", 41 | "mongodb": "^3.4.1" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /client/src/components/Forms/Radio.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | 4 | const RadioInput = styled.input` 5 | visibility: hidden; 6 | width: 0px; 7 | height: 0px; 8 | &:checked { 9 | border: 3px solid ${props => props.theme.colors.corporateDesignSecondary}; 10 | background-color: ${props => props.theme.colors.primary}; 11 | } 12 | `; 13 | 14 | const Label = styled.label` 15 | width: 88px; 16 | height: 88px; 17 | margin: 0px; 18 | padding: 0px; 19 | font-size: 12px; 20 | 21 | background-color: ${props => props.theme.colors.background}; 22 | flex-direction: column; 23 | justify-content: center; 24 | align-items: center; 25 | display: inline-flex; 26 | 27 | &:hover { 28 | border: 3px solid ${props => props.theme.colors.corporateDesignSecondary}; 29 | background-color: ${props => props.theme.colors.primary}; 30 | } 31 | `; 32 | 33 | const Span = styled.span` 34 | text-align: center; 35 | `; 36 | 37 | function Radio({ id, svg, text, isChecked, typeStored, ...other }) { 38 | if (id === typeStored) { 39 | isChecked = false; 40 | } else { 41 | isChecked = true; 42 | } 43 | let renderChecked = !isChecked ? "" : "checked"; 44 | 45 | return ( 46 | <> 47 | 48 | 52 | 53 | ); 54 | } 55 | 56 | export default Radio; 57 | -------------------------------------------------------------------------------- /client/src/components/Footer.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import { useLocation, useHistory } from "react-router-dom"; 4 | import { Link } from "react-router-dom"; 5 | import NoStyleButton from "./Buttons/NoStyleButton"; 6 | 7 | const FooterNavigation = styled.footer` 8 | background-color: ${props => props.theme.colors.primary}; 9 | display: flex; 10 | justify-content: space-between; 11 | align-items: center; 12 | align-content: center; 13 | padding: 4px; 14 | position: absolute; 15 | left: 0px; 16 | bottom: 0px; 17 | height: 20px; 18 | width: 100%; 19 | background-color: ${props => props.theme.colors.primary}; 20 | padding: 4px; 21 | z-index: 1; 22 | @media only screen and (min-width: ${props => 23 | props.theme.company.deviceWidth}) { 24 | max-width: ${props => props.theme.company.deviceWidth}; 25 | } 26 | `; 27 | 28 | const Button = styled.button``; 29 | 30 | function Footer({ name, value, onClick }) { 31 | const location = useLocation(); 32 | return ( 33 | 34 | 35 | crisis manual 36 | 37 | 38 | switch theme 39 | 40 | 41 | legal notice 42 | 43 | 44 | ); 45 | } 46 | 47 | export default Footer; 48 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "issuereporter", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@emotion/core": "^10.0.22", 7 | "@emotion/styled": "^10.0.23", 8 | "@testing-library/jest-dom": "^4.2.4", 9 | "@testing-library/react": "^9.4.0", 10 | "@testing-library/user-event": "^7.1.2", 11 | "emotion-theming": "^10.0.19", 12 | "prop-types": "^15.7.2", 13 | "react": "^16.12.0", 14 | "react-dom": "^16.12.0", 15 | "react-router-dom": "^5.1.2", 16 | "react-scripts": "3.3.0" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build-app": "react-scripts build", 21 | "build": "npm run build-app && npm run build-storybook", 22 | "test": "react-scripts test", 23 | "eject": "react-scripts eject", 24 | "storybook": "start-storybook -p 6006", 25 | "build-storybook": "build-storybook -s public -o build/storybook" 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | }, 39 | "devDependencies": { 40 | "@babel/core": "^7.7.5", 41 | "@storybook/addon-actions": "^5.2.8", 42 | "@storybook/addon-knobs": "^5.2.8", 43 | "@storybook/addon-links": "^5.2.8", 44 | "@storybook/addons": "^5.2.8", 45 | "@storybook/react": "^5.2.8", 46 | "babel-loader": "^8.0.6" 47 | }, 48 | "proxy": "http://localhost:7070" 49 | } 50 | -------------------------------------------------------------------------------- /client/src/components/Forms/Switch.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | 4 | // Thx to https://upmostly.com/tutorials/build-a-react-switch-toggle-component 5 | 6 | const Input = styled.input` 7 | height: 0; 8 | width: 0; 9 | visibility: hidden; 10 | &:checked + label button { 11 | left: calc(100% - 2px); 12 | transform: translateX(-100%); 13 | } 14 | `; 15 | 16 | const Label = styled.label` 17 | content: ""; 18 | top: 2px; 19 | left: 2px; 20 | width: 48px; 21 | height: 24px; 22 | border-radius: 12px; 23 | transition: 0.2s; 24 | background: ${props => props.theme.colors.secondary}; 25 | box-shadow: none; 26 | display: flex; 27 | align-items: center; 28 | justify-content: space-between; 29 | cursor: pointer; 30 | position: relative; 31 | transition: background-color 0.2s; 32 | `; 33 | 34 | const Button = styled.button` 35 | content: ""; 36 | position: absolute; 37 | top: 2px; 38 | left: 2px; 39 | width: 20px; 40 | height: 20px; 41 | border-radius: 50%; 42 | transition: 0.2s; 43 | background: ${props => props.theme.colors.primary}; 44 | box-shadow: none; 45 | `; 46 | 47 | const Switch = ({ isOn, handleToggle, onColor }) => { 48 | return ( 49 | <> 50 | 57 |