├── .husky ├── .gitignore ├── pre-push └── pre-commit ├── .prettierrc.json ├── robots.txt ├── .env.example ├── client ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── mstile-150x150.png │ ├── apple-touch-icon.png │ ├── browserconfig.xml │ ├── site.webmanifest │ ├── manifest.json │ ├── safari-pinned-tab.svg │ └── index.html ├── src │ ├── assets │ │ ├── exit.png │ │ ├── discoBall.gif │ │ ├── fastBiker.gif │ │ ├── weirdBiker.gif │ │ ├── stillLoading.gif │ │ ├── stillLoadingWario.gif │ │ ├── arrow.svg │ │ ├── backward.svg │ │ ├── calendar.svg │ │ ├── back.svg │ │ ├── filter.svg │ │ ├── cargoBox.svg │ │ ├── container.svg │ │ ├── settingsIcon.svg │ │ ├── telephone.svg │ │ ├── buttonPlus.svg │ │ ├── defaultAvatar.svg │ │ ├── stopwatch.svg │ │ ├── customers.svg │ │ ├── login.svg │ │ ├── dayRide.svg │ │ ├── register.svg │ │ ├── mountain.svg │ │ ├── jockey.svg │ │ ├── dayRide2.svg │ │ ├── shuttle.svg │ │ ├── micha.svg │ │ ├── carriage.svg │ │ ├── packages.svg │ │ ├── sexyBikeRider2.svg │ │ ├── sexyBikeRider.svg │ │ ├── sexyBike.svg │ │ └── colors.svg │ ├── App.test.js │ ├── components │ │ ├── LoadingData.js │ │ ├── helpers │ │ │ ├── CenterContent.js │ │ │ ├── CardGrid.js │ │ │ ├── SeedGenerator.js │ │ │ ├── CardContainer.js │ │ │ ├── Wrapper.js │ │ │ └── RiderSelect.js │ │ ├── Input.js │ │ ├── Todo.js │ │ ├── LinkButton.js │ │ ├── StatusBar.js │ │ ├── HeaderHome.js │ │ ├── ButtonPlus.js │ │ ├── Countdown.js │ │ ├── InfoInput.js │ │ ├── RiderLabel.js │ │ ├── WeekDaysSelector.js │ │ ├── TodoList.js │ │ ├── Badge.js │ │ ├── Header.js │ │ ├── CardButton.js │ │ ├── Button.js │ │ ├── HeaderMain.js │ │ ├── CardCustomer.js │ │ └── CardRider.js │ ├── setupTests.js │ ├── utils │ │ ├── time.js │ │ ├── date.js │ │ ├── cookies.js │ │ ├── prefetch.js │ │ └── api.js │ ├── stories │ │ ├── Countdown.stories.js │ │ ├── Input.stories.js │ │ ├── Badge.stories.js │ │ ├── Header.stories.js │ │ ├── Page.stories.js │ │ └── Card.stories.js │ ├── reportWebVitals.js │ ├── hooks │ │ ├── useOnlineStatus.js │ │ ├── useBroadcastOrQuery.js │ │ └── useBroadcastUpdate.js │ ├── index.js │ ├── pages │ │ ├── RiderInfo.js │ │ ├── CustomerInfo.js │ │ ├── Customers.js │ │ ├── Riders.js │ │ ├── TourInfo.js │ │ ├── Tours.js │ │ ├── Launch.js │ │ ├── ToursToday.js │ │ ├── Register.js │ │ ├── MainMenu.js │ │ ├── AddCustomer.js │ │ └── AddRider.js │ ├── context │ │ └── user.js │ ├── App.js │ ├── GlobalStyles.js │ ├── service-worker.js │ └── serviceWorkerRegistration.js ├── .storybook │ ├── manager.js │ ├── preview-head.html │ ├── main.js │ └── preview.js ├── .eslintrc.json ├── package.json └── README.md ├── .eslintrc.json ├── README.md ├── .github ├── pull_request_template.md └── workflows │ ├── node.js.yml │ └── IssuesToProject.yml ├── humans.txt ├── lib ├── database.js └── serverMethods.js ├── .eslintignore ├── .prettierignore ├── LICENSE ├── .gitignore ├── server.js ├── routes ├── users.js ├── customers.js ├── tours.js └── riders.js └── package.json /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | Allow: 4 | * -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname $0)/_/husky.sh" 3 | 4 | npm test -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | DB_USERNAME= 2 | DB_PASSWORD= 3 | DB_URI=mongodb+srv:// 4 | DB_NAME= -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname $0)/_/husky.sh" 3 | 4 | npx lint-staged -------------------------------------------------------------------------------- /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/DiscoDevs/DispoDisco/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscoDevs/DispoDisco/HEAD/client/public/logo192.png -------------------------------------------------------------------------------- /client/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscoDevs/DispoDisco/HEAD/client/public/logo512.png -------------------------------------------------------------------------------- /client/src/assets/exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscoDevs/DispoDisco/HEAD/client/src/assets/exit.png -------------------------------------------------------------------------------- /client/src/assets/discoBall.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscoDevs/DispoDisco/HEAD/client/src/assets/discoBall.gif -------------------------------------------------------------------------------- /client/src/assets/fastBiker.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscoDevs/DispoDisco/HEAD/client/src/assets/fastBiker.gif -------------------------------------------------------------------------------- /client/public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscoDevs/DispoDisco/HEAD/client/public/mstile-150x150.png -------------------------------------------------------------------------------- /client/src/assets/weirdBiker.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscoDevs/DispoDisco/HEAD/client/src/assets/weirdBiker.gif -------------------------------------------------------------------------------- /client/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscoDevs/DispoDisco/HEAD/client/public/apple-touch-icon.png -------------------------------------------------------------------------------- /client/src/assets/stillLoading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscoDevs/DispoDisco/HEAD/client/src/assets/stillLoading.gif -------------------------------------------------------------------------------- /client/src/assets/stillLoadingWario.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiscoDevs/DispoDisco/HEAD/client/src/assets/stillLoadingWario.gif -------------------------------------------------------------------------------- /client/.storybook/manager.js: -------------------------------------------------------------------------------- 1 | import { addons } from "@storybook/addons"; 2 | import { themes } from "@storybook/theming"; 3 | 4 | addons.setConfig({ 5 | theme: themes.dark, 6 | }); 7 | -------------------------------------------------------------------------------- /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 App", () => { 6 | render(); 7 | }); 8 | -------------------------------------------------------------------------------- /client/src/components/LoadingData.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components/macro"; 2 | 3 | const LoadingData = styled.h2` 4 | margin-top: 20vh; 5 | color: white; 6 | `; 7 | export default LoadingData; 8 | -------------------------------------------------------------------------------- /client/src/components/helpers/CenterContent.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components/macro"; 2 | 3 | export const CenterContent = styled.div` 4 | display: grid; 5 | place-items: center; 6 | height: 100%; 7 | `; 8 | -------------------------------------------------------------------------------- /client/.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /client/src/components/Input.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components/macro"; 2 | 3 | const Input = styled.input` 4 | width: 100%; 5 | padding: 0.5rem 1rem; 6 | ::placeholder { 7 | font-style: italic; 8 | } 9 | `; 10 | 11 | export default Input; 12 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": ["eslint:recommended"], 8 | "parserOptions": { 9 | "ecmaVersion": 12, 10 | "sourceType": "module" 11 | }, 12 | "rules": {} 13 | } 14 | -------------------------------------------------------------------------------- /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'; 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DispoDisco 2 | 3 | Final project for neueFische Bootcamp CGN20/04 WebDevelopment 4 | 5 | Main-Deployment: https://dispodisco.herokuapp.com 6 | Dev-Deployment: https://dispodisco-dev.herokuapp.com 7 | 8 | Daten für Test-Login: 9 | username: example 10 | password: example 11 | -------------------------------------------------------------------------------- /client/src/utils/time.js: -------------------------------------------------------------------------------- 1 | export function add30Minutes(start) { 2 | const time = new Date(new Date(start).getTime() + 1800000); 3 | return time; 4 | } 5 | 6 | export function add90Minutes(start) { 7 | const time = new Date(new Date(start).getTime() + 5400000); 8 | return time; 9 | } 10 | -------------------------------------------------------------------------------- /client/.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "stories": [ 3 | "../src/**/*.stories.mdx", 4 | "../src/**/*.stories.@(js|jsx|ts|tsx)" 5 | ], 6 | "addons": [ 7 | "@storybook/addon-links", 8 | "@storybook/addon-essentials", 9 | "@storybook/preset-create-react-app" 10 | ] 11 | } -------------------------------------------------------------------------------- /client/public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #603cba 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /client/src/utils/date.js: -------------------------------------------------------------------------------- 1 | export function getCurrentDateString() { 2 | return new Date().toISOString().substr(0, 10); 3 | } 4 | 5 | export function getCurrentDateShort() { 6 | return new Date().toLocaleDateString("de-DE", { 7 | year: "2-digit", 8 | month: "2-digit", 9 | day: "2-digit", 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /client/src/stories/Countdown.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Countdown from "../components/Countdown"; 4 | 5 | export default { 6 | title: "DispoDisco/Countdown", 7 | component: Countdown, 8 | }; 9 | 10 | const Template = (args) => ; 11 | 12 | export const Timer = Template.bind(); 13 | Timer.args = { 14 | finish: "2020-12-24T18:00", 15 | }; 16 | -------------------------------------------------------------------------------- /client/src/components/helpers/CardGrid.js: -------------------------------------------------------------------------------- 1 | const { default: styled } = require("styled-components"); 2 | 3 | const CardGrid = styled.div` 4 | display: grid; 5 | grid-gap: 1rem; 6 | grid-template-columns: repeat(auto-fit, minmax(317px, 1fr)); 7 | align-items: center; 8 | justify-items: center; 9 | max-width: 1400px; 10 | margin: auto; 11 | padding: 0 0.5rem; 12 | `; 13 | 14 | export default CardGrid; 15 | -------------------------------------------------------------------------------- /client/src/components/helpers/SeedGenerator.js: -------------------------------------------------------------------------------- 1 | const roboRoot = `https://robohash.org/`; 2 | const roboParams = `?set=set5`; 3 | const avatarSize = `&size=75x75`; 4 | const avatarFile = `.png`; 5 | const hash = () => Math.floor(Math.random() * 1000000).toString(); 6 | const generateNewAvatarUrl = (query = "") => 7 | `${roboRoot}${hash()}${query}${avatarFile}${roboParams}${avatarSize}`; 8 | 9 | export default generateNewAvatarUrl; 10 | -------------------------------------------------------------------------------- /client/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Fixes # (issue) 4 | 5 | ## Type of change 6 | 7 | Please delete options that are not relevant. 8 | 9 | - [ ] Bug fix (non-breaking change which fixes an issue) 10 | - [ ] New feature (non-breaking change which adds functionality) 11 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 12 | - [ ] This change requires a documentation update 13 | -------------------------------------------------------------------------------- /humans.txt: -------------------------------------------------------------------------------- 1 | /* TEAM */ 2 | One eyed illustrator: 3 | Twitter: 4 | From:Madrid, Spain 5 | 6 | Standard Man: 7 | Twitter: 8 | From: Cologne, Germany 9 | 10 | Web designer: 11 | Twitter: 12 | From: 13 | 14 | /* THANKS */ 15 | 16 | EN Translator: 17 | Twitter: 18 | 19 | 20 | Media Queries by: Marta Armada (@martuishere) and Javier Usobiaga (@htmlboy) 21 | 22 | 23 | /* SITE */ 24 | Last update: 25 | Language: 26 | Doctype:HTML5 27 | IDE: VSCode -------------------------------------------------------------------------------- /client/src/components/helpers/CardContainer.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components/macro"; 2 | 3 | const CardContainer = styled.div` 4 | position: relative; 5 | 6 | width: 100%; 7 | width: clamp(320px, 20vw, 336px); 8 | margin: auto 0.5rem; 9 | padding: 1rem; 10 | text-align: center; 11 | font-weight: bold; 12 | color: var(--text-primary); 13 | background: var(--gradient-normal); 14 | 15 | border-radius: var(--border-radius); 16 | `; 17 | 18 | export default CardContainer; 19 | -------------------------------------------------------------------------------- /client/src/assets/arrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/database.js: -------------------------------------------------------------------------------- 1 | const { MongoClient } = require("mongodb"); 2 | 3 | let client; 4 | let db; 5 | 6 | async function connectToDb(url, dbName) { 7 | client = await MongoClient.connect(url, { useUnifiedTopology: true }); 8 | db = client.db(dbName); 9 | } 10 | 11 | function closeDbConnection() { 12 | client.close(); 13 | } 14 | 15 | function collection(name) { 16 | return db.collection(name); 17 | } 18 | 19 | exports.connectToDb = connectToDb; 20 | exports.closeDbConnection = closeDbConnection; 21 | exports.collection = collection; 22 | -------------------------------------------------------------------------------- /client/public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Dispo Disco", 3 | "short_name": "Dispo Disco", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /client/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "jest": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "react-app", 10 | "react-app/jest", 11 | "plugin:react/recommended" 12 | ], 13 | "parserOptions": { 14 | "ecmaFeatures": { 15 | "jsx": true 16 | } 17 | }, 18 | "settings": { 19 | "react": { 20 | "version": "latest" 21 | } 22 | }, 23 | "plugins": ["react"], 24 | "rules": { 25 | "import/no-anonymous-default-export": "off" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /client/src/assets/backward.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import GlobalStyle from "../src/GlobalStyles"; 2 | 3 | export const parameters = { 4 | actions: { argTypesRegex: "^on[A-Z].*" }, 5 | layout: "fullscreen", 6 | }; 7 | export const decorators = [ 8 | (Story) => ( 9 | <> 10 | 11 |
20 | 21 |
22 | 23 | ), 24 | ]; 25 | -------------------------------------------------------------------------------- /client/src/components/Todo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components/macro"; 3 | import PropTypes from "prop-types"; 4 | 5 | const TodoElement = styled.div` 6 | color: var(--text-primary); 7 | display: flex; 8 | align-items: center; 9 | input { 10 | height: 2rem; 11 | margin-right: 1rem; 12 | } 13 | `; 14 | 15 | export default function Todo({ children }) { 16 | return ( 17 | 18 | 19 |

{children}

20 |
21 | ); 22 | } 23 | Todo.propTypes = { 24 | children: PropTypes.node, 25 | }; 26 | -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Dispo Disco", 3 | "short_name": "Dispo Disco", 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/calendar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/LinkButton.js: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom"; 2 | import styled from "styled-components"; 3 | 4 | const LinkButton = styled(Link)` 5 | background: var(--gradient-menu); 6 | margin: auto; 7 | padding: 0.5rem 1rem; 8 | border: none; 9 | border-radius: 6px; 10 | font-family: "Goldman"; 11 | font-size: 2rem; 12 | color: var(--text-primary); 13 | display: flex; 14 | justify-content: center; 15 | align-items: center; 16 | text-decoration: none; 17 | > :first-child { 18 | min-width: 35px; 19 | margin-right: 10px; 20 | } 21 | `; 22 | export default LinkButton; 23 | -------------------------------------------------------------------------------- /client/src/components/helpers/Wrapper.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components/macro"; 2 | 3 | export const ContentWrapper = styled.div` 4 | position: relative; 5 | max-width: 400px; 6 | display: flex; 7 | flex-direction: column; 8 | align-items: center; 9 | margin: 2rem auto; 10 | padding: 0 1rem; 11 | > div { 12 | margin-bottom: 2rem; 13 | } 14 | `; 15 | const Wrapper = styled.div` 16 | min-height: 100vh; 17 | overflow-x: hidden; 18 | height: 1px; 19 | width: 100%; 20 | margin: auto; 21 | padding-top: 11rem; 22 | padding-top: clamp(11rem, 25vw, 250px); 23 | `; 24 | export default Wrapper; 25 | -------------------------------------------------------------------------------- /client/src/hooks/useOnlineStatus.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | const useOnline = () => { 3 | const [online, setOnline] = useState(navigator.onLine); 4 | useEffect(() => { 5 | const handleOnlineChange = () => { 6 | setOnline(navigator.onLine); 7 | }; 8 | window.addEventListener("offline", handleOnlineChange); 9 | window.addEventListener("online", handleOnlineChange); 10 | return () => { 11 | window.removeEventListener("offline", handleOnlineChange); 12 | window.removeEventListener("online", handleOnlineChange); 13 | }; 14 | }, []); 15 | return online; 16 | }; 17 | export default useOnline; 18 | -------------------------------------------------------------------------------- /client/src/stories/Input.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Input from "../components/Input"; 4 | import Todo from "../components/Todo"; 5 | 6 | export default { 7 | title: "DispoDisco/Inputs", 8 | component: "Input", 9 | }; 10 | 11 | const Template = (args) => ; 12 | const TodoTemplate = (args) => ( 13 | Schnell weg, wenn geliefert 💨 14 | ); 15 | 16 | export const StandardInput = Template.bind(); 17 | StandardInput.args = { 18 | type: "text", 19 | placeholder: "default", 20 | }; 21 | export const RidePageTodo = TodoTemplate.bind(); 22 | RidePageTodo.arg = { 23 | children: " ", 24 | }; 25 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [main, develop] 9 | pull_request: 10 | branches: [main, develop] 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Use Node.js 14.x 19 | uses: actions/setup-node@v1 20 | with: 21 | node-version: 14.x 22 | - run: npm ci 23 | - run: npm run build --if-present 24 | - run: npm test 25 | -------------------------------------------------------------------------------- /client/src/utils/cookies.js: -------------------------------------------------------------------------------- 1 | export const createCookie = (cookieName, cookieValue, daysToExpire) => { 2 | const date = new Date(); 3 | date.setTime(date.getTime() + daysToExpire * 24 * 60 * 60 * 1000); 4 | document.cookie = `${cookieName}=${JSON.stringify( 5 | cookieValue 6 | )}; expires=${date.toGMTString()}`; 7 | }; 8 | 9 | export const accessCookie = (cookieName) => { 10 | let matches = document.cookie.match( 11 | new RegExp( 12 | "(?:^|; )" + 13 | cookieName.replace(/([.$?*|{}()[\]\\/+^])/g, "\\$1") + 14 | "=([^;]*)" 15 | ) 16 | ); 17 | return matches ? decodeURIComponent(matches[1]) : undefined; 18 | }; 19 | 20 | export const deleteCookie = (cookieName) => { 21 | document.cookie = `${cookieName}= ; expires = Thu, 01 Jan 1970 00:00:00 GMT`; 22 | }; 23 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | *.pid.lock 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | # Compiled binary addons (http://nodejs.org/api/addons.html) 28 | build/Release 29 | 30 | # Dependency directories 31 | node_modules 32 | jspm_packages 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional REPL history 38 | .node_repl_history 39 | **/node_modules/ 40 | **/build/ 41 | **/storybook-static/ 42 | -------------------------------------------------------------------------------- /client/src/stories/Badge.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import Badge from "../components/Badge"; 4 | export default { 5 | title: "DispoDisco/Badge", 6 | component: Badge, 7 | }; 8 | 9 | const Template = (args) => ; 10 | 11 | export const CargoS = Template.bind({}); 12 | CargoS.args = { 13 | type: "cargoS", 14 | }; 15 | export const CargoM = Template.bind({}); 16 | CargoM.args = { 17 | type: "cargoM", 18 | }; 19 | export const CargoL = Template.bind({}); 20 | CargoL.args = { 21 | type: "cargoL", 22 | }; 23 | export const Carriage = Template.bind({}); 24 | Carriage.args = { 25 | type: "carriage", 26 | }; 27 | export const Direkt = Template.bind({}); 28 | Direkt.args = { 29 | type: "direct", 30 | }; 31 | export const OnTime = Template.bind({}); 32 | OnTime.args = { 33 | type: "onTimeRide", 34 | }; 35 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | *.pid.lock 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | # Compiled binary addons (http://nodejs.org/api/addons.html) 28 | build/Release 29 | **/build 30 | **/storybook-static 31 | **/coverage 32 | **/ISSUE_TEMPLATE 33 | 34 | # Dependency directories 35 | node_modules 36 | jspm_packages 37 | 38 | # Optional npm cache directory 39 | .npm 40 | 41 | # Optional REPL history 42 | .node_repl_history 43 | -------------------------------------------------------------------------------- /client/src/hooks/useBroadcastOrQuery.js: -------------------------------------------------------------------------------- 1 | import { useQuery } from "react-query"; 2 | import { getSortedDataByQuery } from "../utils/api"; 3 | import useBroadcastUpdate from "./useBroadcastUpdate"; 4 | 5 | const useBroadcastOrQuery = ({ endpoint, today, option = "today" }) => { 6 | const broadcastedTours = useBroadcastUpdate(`/api/${endpoint}`); 7 | 8 | const { isLoading, isError, data, error, refetch } = useQuery( 9 | [endpoint, option], 10 | () => 11 | getSortedDataByQuery({ 12 | collectionName: endpoint, 13 | type: "date", 14 | query: today, 15 | }) 16 | ); 17 | console.log({ data }, { broadcastedTours }); 18 | if (data?.length <= broadcastedTours?.length) { 19 | return { data, isLoading, isError, error, refetch }; 20 | } 21 | return data; 22 | }; 23 | 24 | export default useBroadcastOrQuery; 25 | -------------------------------------------------------------------------------- /client/src/assets/back.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/assets/filter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/stories/Header.stories.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import HeaderMain from "../components/HeaderMain"; 4 | import HeaderHome from "../components/HeaderHome"; 5 | import Header from "../components/Header"; 6 | 7 | export default { 8 | title: "DispoDisco/Header", 9 | component: HeaderMain, 10 | }; 11 | 12 | const HomeTemplate = (args) => ; 13 | const MainTemplate = (args) => ; 14 | const HeaderTemplate = (args) =>
; 15 | const HeaderTextTemplate = (args) =>
; 16 | 17 | export const DefaultHeader = HeaderTemplate.bind({}); 18 | export const HeaderWithHeading = HeaderTextTemplate.bind({}); 19 | export const MainMenuHeader = MainTemplate.bind({}); 20 | export const HomeHeader = HomeTemplate.bind({}); 21 | 22 | HeaderWithHeading.args = { 23 | title: "Überschrift", 24 | }; 25 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./App"; 4 | import * as serviceWorkerRegistration from "./serviceWorkerRegistration"; 5 | import reportWebVitals from "./reportWebVitals"; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById("root") 12 | ); 13 | 14 | // If you want your app to work offline and load faster, you can change 15 | // unregister() to register() below. Note this comes with some pitfalls. 16 | // Learn more about service workers: https://cra.link/PWA 17 | serviceWorkerRegistration.register(); 18 | // If you want to start measuring performance in your app, pass a function 19 | // to log results (for example: reportWebVitals(console.log)) 20 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 21 | reportWebVitals(); 22 | -------------------------------------------------------------------------------- /client/src/utils/prefetch.js: -------------------------------------------------------------------------------- 1 | import { getEntryList, getSortedData, getSortedDataByQuery } from "./api"; 2 | import { getCurrentDateString } from "./date"; 3 | const today = getCurrentDateString(); 4 | 5 | const prefetchData = async ({ queryClient, companyName }) => { 6 | await queryClient.prefetchQuery(["tours", today], () => 7 | getSortedDataByQuery({ 8 | collectionName: "tours", 9 | type: "date", 10 | query: today, 11 | company: companyName, 12 | }) 13 | ); 14 | await queryClient.prefetchQuery("riders", () => 15 | getEntryList({ 16 | collectionName: "riders", 17 | key: "alias", 18 | company: companyName, 19 | }) 20 | ); 21 | await queryClient.prefetchQuery("customers", () => 22 | getSortedData({ 23 | collectionName: "customers", 24 | company: companyName, 25 | }) 26 | ); 27 | }; 28 | 29 | export default prefetchData; 30 | -------------------------------------------------------------------------------- /client/src/components/StatusBar.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components/macro"; 3 | import useOnline from "../hooks/useOnlineStatus"; 4 | import { useSpring, animated } from "react-spring"; 5 | 6 | const StatusBar = () => { 7 | let online = useOnline(); 8 | 9 | const floatIn = useSpring({ 10 | backgroundColor: online ? "green" : "red", 11 | fontSize: online ? "0px" : "14px", 12 | marginTop: online ? "3px" : "10px", 13 | height: online ? "0px" : "26px", 14 | }); 15 | 16 | return ( 17 | 18 | {online ? Du bist online : Du bist offline} 19 | 20 | ); 21 | }; 22 | const ContentWrapper = styled(animated.div)` 23 | display: flex; 24 | justify-content: center; 25 | width: 100%; 26 | min-height: 0px; 27 | border-radius: 6px; 28 | span { 29 | text-align: center; 30 | padding-bottom: 0.5rem; 31 | } 32 | `; 33 | 34 | export default StatusBar; 35 | -------------------------------------------------------------------------------- /client/src/components/HeaderHome.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "styled-components/macro"; 3 | import MirrorBallSrc from "../assets/discoBall.gif"; 4 | 5 | const HeaderHome = () => ( 6 | <> 7 | 8 | 9 | Disp 10 | 11 | 12 | Disco 13 | 14 | 15 | ); 16 | 17 | const HeaderElement = styled.header` 18 | margin: 1rem auto; 19 | display: flex; 20 | flex-direction: column; 21 | padding: 1rem auto; 22 | color: var(--text-primary); 23 | `; 24 | 25 | const First = styled.h2` 26 | align-self: flex-end; 27 | font-size: clamp(2rem, 10vw, 4rem); 28 | `; 29 | 30 | const Second = styled(First)``; 31 | 32 | const MirrorBall = styled.img` 33 | height: 70px; 34 | height: clamp(70px, 30vw, 100px); 35 | padding: 0 1rem; 36 | margin-top: -1.5rem; 37 | filter: drop-shadow(0px 3px 6px var(--text-secondary)); 38 | `; 39 | export default HeaderHome; 40 | -------------------------------------------------------------------------------- /client/src/assets/cargoBox.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/workflows/IssuesToProject.yml: -------------------------------------------------------------------------------- 1 | name: Link PR or Issue to DispoDisco Project 2 | on: [issues, pull_request] 3 | jobs: 4 | github-actions-automate-projects: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: add-new-issues-to-repository-based-project-column 8 | uses: docker://takanabe/github-actions-automate-projects:v0.0.1 9 | if: github.event_name == 'issues' && github.event.action == 'opened' 10 | env: 11 | GITHUB_TOKEN: ${{ secrets.PRISSUETOPROJECT }} 12 | GITHUB_PROJECT_URL: https://github.com/DiscoDevs/DispoDisco/projects/1 13 | GITHUB_PROJECT_COLUMN_NAME: To do 14 | - name: add-new-prs-to-repository-based-project-column 15 | uses: docker://takanabe/github-actions-automate-projects:v0.0.1 16 | if: github.event_name == 'pull_request' && github.event.action == 'opened' 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.PRISSUETOPROJECT }} 19 | GITHUB_PROJECT_URL: https://github.com/DiscoDevs/DispoDisco/projects/1 20 | GITHUB_PROJECT_COLUMN_NAME: To do 21 | -------------------------------------------------------------------------------- /client/src/components/ButtonPlus.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import styled from "styled-components/macro"; 4 | /** 5 | * Primary UI component for user interaction 6 | */ 7 | 8 | const ButtonPlus = ({ onClick }) => { 9 | ButtonPlus.propTypes = { 10 | onClick: PropTypes.func, 11 | }; 12 | 13 | return ( 14 | 15 |
+
16 |
17 | ); 18 | }; 19 | 20 | const ButtonElement = styled.button` 21 | position: fixed; 22 | bottom: 2rem; 23 | left: calc(50% - 25px); 24 | display: grid; 25 | align-items: center; 26 | border-radius: 6px; 27 | background: var(--text-secondary); 28 | width: 50px; 29 | height: 50px; 30 | border: none; 31 | 32 | div { 33 | display: grid; 34 | color: var(--text-primary); 35 | font-size: 1.5rem; 36 | font-family: "Goldman"; 37 | align-items: center; 38 | border: 2px solid white; 39 | height: 38px; 40 | width: 38px; 41 | margin: auto; 42 | border-radius: inherit; 43 | } 44 | `; 45 | 46 | export default ButtonPlus; 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 DiscoDevs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /client/src/assets/container.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/pages/RiderInfo.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { useParams } from "react-router-dom"; 3 | import styled from "styled-components/macro"; 4 | import CardRider from "../components/CardRider"; 5 | import Header from "../components/Header"; 6 | import Wrapper, { ContentWrapper } from "../components/helpers/Wrapper"; 7 | import { getDataByID } from "../utils/api"; 8 | 9 | export default function RiderInfo() { 10 | const { id } = useParams(); 11 | const [rider, setRider] = useState([]); 12 | 13 | useEffect(() => { 14 | const doFetch = async () => { 15 | const data = await getDataByID({ 16 | collectionName: "riders", 17 | id, 18 | }); 19 | setRider(data); 20 | }; 21 | 22 | doFetch(); 23 | }, [id]); 24 | return ( 25 | 26 | 27 |
28 | 35 | 36 | 37 | ); 38 | } 39 | 40 | const InfoWrapper = styled(Wrapper)` 41 | background-color: var(--text-secondary); 42 | `; 43 | -------------------------------------------------------------------------------- /client/src/components/Countdown.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import styled from "styled-components"; 3 | import PropType from "prop-types"; 4 | 5 | export default function Countdown({ finish }) { 6 | const [counter, setCounter] = useState(timer()); 7 | 8 | useEffect(() => { 9 | setCounter(timer(finish)); 10 | if (new Date(finish) > new Date()) { 11 | setTimeout(() => setCounter(timer(finish)), 1000); 12 | } else { 13 | setCounter("00:00:00"); 14 | } 15 | // !BUG 16 | // return () => { 17 | // clearTimeout(() => setCounter(timer(finish)), 1000); 18 | // }; 19 | }, [counter, finish]); 20 | 21 | function timer(finish) { 22 | const time = new Date( 23 | new Date(finish).getTime() - new Date().getTime() 24 | ).toLocaleTimeString("de-DE", { 25 | hour: "numeric", 26 | minute: "numeric", 27 | second: "numeric", 28 | timeZone: "UTC", 29 | }); 30 | return time; 31 | } 32 | 33 | return ( 34 | 35 |

{counter.substr(0, 5)}

36 |
37 | ); 38 | } 39 | 40 | Countdown.propTypes = { 41 | finish: PropType.string, 42 | }; 43 | 44 | const CountDown = styled.div` 45 | background: white; 46 | `; 47 | -------------------------------------------------------------------------------- /client/src/pages/CustomerInfo.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { useParams } from "react-router-dom"; 3 | import styled from "styled-components/macro"; 4 | import CardCustomer from "../components/CardCustomer"; 5 | import Header from "../components/Header"; 6 | import Wrapper, { ContentWrapper } from "../components/helpers/Wrapper"; 7 | import { getDataByID } from "../utils/api"; 8 | 9 | export default function CustomerInfo() { 10 | const { id } = useParams(); 11 | const [customer, setCustomer] = useState([]); 12 | 13 | useEffect(() => { 14 | const doFetch = async () => { 15 | const data = await getDataByID({ 16 | collectionName: "customers", 17 | id, 18 | }); 19 | setCustomer(data); 20 | }; 21 | 22 | doFetch(); 23 | }, [id]); 24 | return ( 25 | 26 |
27 | 28 | 35 | 36 | 37 | ); 38 | } 39 | 40 | const CustomerWrapper = styled(Wrapper)` 41 | background-color: var(--text-secondary); 42 | `; 43 | -------------------------------------------------------------------------------- /client/src/hooks/useBroadcastUpdate.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | 3 | const useBroadcastUpdate = (endpoint) => { 4 | const [latestUpdate, setLatestUpdate] = useState(null); 5 | 6 | useEffect(() => { 7 | if (!("serviceWorker" in navigator)) { 8 | console.warn("Service workers are not supported"); 9 | return; 10 | } 11 | 12 | const handleMessage = async (event) => { 13 | if (event.data.meta === "workbox-broadcast-update") { 14 | const { cacheName, updatedURL } = event.data.payload; 15 | if (!updatedURL.endsWith(endpoint)) { 16 | return; 17 | } 18 | const cache = await caches.open(cacheName); 19 | const updatedResponse = await cache.match(updatedURL); 20 | if (updatedResponse) { 21 | const latestShorties = await updatedResponse.json(); 22 | setLatestUpdate(latestShorties); 23 | } 24 | } 25 | }; 26 | 27 | navigator.serviceWorker.addEventListener("message", handleMessage); 28 | 29 | return () => { 30 | navigator.serviceWorker.removeEventListener("message", handleMessage); 31 | }; 32 | }, [endpoint]); 33 | console.log({ latestUpdate }); 34 | return latestUpdate; 35 | }; 36 | 37 | export default useBroadcastUpdate; 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | *.pid.lock 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | # Compiled binary addons (http://nodejs.org/api/addons.html) 28 | build/Release 29 | 30 | # Dependency directories 31 | node_modules 32 | jspm_packages 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional REPL history 38 | .node_repl_history 39 | 40 | .env 41 | 42 | **/.eslintcache 43 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 44 | 45 | # dependencies 46 | /node_modules 47 | /.pnp 48 | .pnp.js 49 | 50 | # testing 51 | /coverage 52 | 53 | # production 54 | **/build 55 | **/storybook-static 56 | 57 | # misc 58 | .DS_Store 59 | .env.local 60 | .env.development.local 61 | .env.test.local 62 | .env.production.local 63 | 64 | npm-debug.log* 65 | yarn-debug.log* 66 | yarn-error.log* 67 | client/.eslintcache 68 | client/build/ 69 | client/storybook-static/ 70 | -------------------------------------------------------------------------------- /client/src/components/InfoInput.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import PropTypes from "prop-types"; 3 | import styled from "styled-components"; 4 | import TodoList from "./TodoList"; 5 | 6 | const InfoInput = ({ 7 | checkboxes, 8 | info, 9 | onCheckboxesChange, 10 | onInfoChange, 11 | task, 12 | }) => { 13 | return ( 14 |
15 | 16 |

Info & Todo-Liste

17 |
18 | 19 |

Infos

20 |