├── .prettierrc.json ├── nodemon.json ├── .eslintignore ├── .prettierignore ├── client ├── README.md ├── public │ ├── favicon.ico │ ├── robots.txt │ ├── manifest.json │ └── index.html ├── src │ ├── utils │ │ ├── api │ │ │ ├── index.js │ │ │ └── dataFetch.js │ │ ├── helpers │ │ │ ├── index.js │ │ │ └── date.js │ │ ├── router │ │ │ ├── LoginRoute.js │ │ │ └── ProtectedRoute.js │ │ ├── config │ │ │ └── constants.js │ │ ├── hook │ │ │ ├── useAsync.js │ │ │ └── useAuth.js │ │ └── context │ │ │ └── Context.js │ ├── assets │ │ ├── logo │ │ │ ├── logo-bg.png │ │ │ ├── logo-artemis.jpg │ │ │ ├── logo-artemis.png │ │ │ ├── logo-artemis_bw.png │ │ │ └── logo-Artemis.svg │ │ ├── fonts │ │ │ ├── Poppins │ │ │ │ ├── poppins-v15-latin-100.eot │ │ │ │ ├── poppins-v15-latin-100.ttf │ │ │ │ ├── poppins-v15-latin-200.eot │ │ │ │ ├── poppins-v15-latin-200.ttf │ │ │ │ ├── poppins-v15-latin-300.eot │ │ │ │ ├── poppins-v15-latin-300.ttf │ │ │ │ ├── poppins-v15-latin-500.eot │ │ │ │ ├── poppins-v15-latin-500.ttf │ │ │ │ ├── poppins-v15-latin-600.eot │ │ │ │ ├── poppins-v15-latin-600.ttf │ │ │ │ ├── poppins-v15-latin-700.eot │ │ │ │ ├── poppins-v15-latin-700.ttf │ │ │ │ ├── poppins-v15-latin-800.eot │ │ │ │ ├── poppins-v15-latin-800.ttf │ │ │ │ ├── poppins-v15-latin-900.eot │ │ │ │ ├── poppins-v15-latin-900.ttf │ │ │ │ ├── poppins-v15-latin-100.woff │ │ │ │ ├── poppins-v15-latin-100.woff2 │ │ │ │ ├── poppins-v15-latin-200.woff │ │ │ │ ├── poppins-v15-latin-200.woff2 │ │ │ │ ├── poppins-v15-latin-300.woff │ │ │ │ ├── poppins-v15-latin-300.woff2 │ │ │ │ ├── poppins-v15-latin-500.woff │ │ │ │ ├── poppins-v15-latin-500.woff2 │ │ │ │ ├── poppins-v15-latin-600.woff │ │ │ │ ├── poppins-v15-latin-600.woff2 │ │ │ │ ├── poppins-v15-latin-700.woff │ │ │ │ ├── poppins-v15-latin-700.woff2 │ │ │ │ ├── poppins-v15-latin-800.woff │ │ │ │ ├── poppins-v15-latin-800.woff2 │ │ │ │ ├── poppins-v15-latin-900.woff │ │ │ │ ├── poppins-v15-latin-900.woff2 │ │ │ │ ├── poppins-v15-latin-italic.eot │ │ │ │ ├── poppins-v15-latin-italic.ttf │ │ │ │ ├── poppins-v15-latin-italic.woff │ │ │ │ ├── poppins-v15-latin-italic.woff2 │ │ │ │ ├── poppins-v15-latin-regular.eot │ │ │ │ ├── poppins-v15-latin-regular.ttf │ │ │ │ ├── poppins-v15-latin-regular.woff │ │ │ │ ├── poppins-v15-latin-100italic.eot │ │ │ │ ├── poppins-v15-latin-100italic.ttf │ │ │ │ ├── poppins-v15-latin-100italic.woff │ │ │ │ ├── poppins-v15-latin-100italic.woff2 │ │ │ │ ├── poppins-v15-latin-200italic.eot │ │ │ │ ├── poppins-v15-latin-200italic.ttf │ │ │ │ ├── poppins-v15-latin-200italic.woff │ │ │ │ ├── poppins-v15-latin-200italic.woff2 │ │ │ │ ├── poppins-v15-latin-300italic.eot │ │ │ │ ├── poppins-v15-latin-300italic.ttf │ │ │ │ ├── poppins-v15-latin-300italic.woff │ │ │ │ ├── poppins-v15-latin-300italic.woff2 │ │ │ │ ├── poppins-v15-latin-500italic.eot │ │ │ │ ├── poppins-v15-latin-500italic.ttf │ │ │ │ ├── poppins-v15-latin-500italic.woff │ │ │ │ ├── poppins-v15-latin-500italic.woff2 │ │ │ │ ├── poppins-v15-latin-600italic.eot │ │ │ │ ├── poppins-v15-latin-600italic.ttf │ │ │ │ ├── poppins-v15-latin-600italic.woff │ │ │ │ ├── poppins-v15-latin-600italic.woff2 │ │ │ │ ├── poppins-v15-latin-700italic.eot │ │ │ │ ├── poppins-v15-latin-700italic.ttf │ │ │ │ ├── poppins-v15-latin-700italic.woff │ │ │ │ ├── poppins-v15-latin-700italic.woff2 │ │ │ │ ├── poppins-v15-latin-800italic.eot │ │ │ │ ├── poppins-v15-latin-800italic.ttf │ │ │ │ ├── poppins-v15-latin-800italic.woff │ │ │ │ ├── poppins-v15-latin-800italic.woff2 │ │ │ │ ├── poppins-v15-latin-900italic.eot │ │ │ │ ├── poppins-v15-latin-900italic.ttf │ │ │ │ ├── poppins-v15-latin-900italic.woff │ │ │ │ ├── poppins-v15-latin-900italic.woff2 │ │ │ │ └── poppins-v15-latin-regular.woff2 │ │ │ └── Open_Sans │ │ │ │ ├── open-sans-v18-latin-300.eot │ │ │ │ ├── open-sans-v18-latin-300.ttf │ │ │ │ ├── open-sans-v18-latin-300.woff │ │ │ │ ├── open-sans-v18-latin-600.eot │ │ │ │ ├── open-sans-v18-latin-600.ttf │ │ │ │ ├── open-sans-v18-latin-600.woff │ │ │ │ ├── open-sans-v18-latin-700.eot │ │ │ │ ├── open-sans-v18-latin-700.ttf │ │ │ │ ├── open-sans-v18-latin-700.woff │ │ │ │ ├── open-sans-v18-latin-800.eot │ │ │ │ ├── open-sans-v18-latin-800.ttf │ │ │ │ ├── open-sans-v18-latin-800.woff │ │ │ │ ├── open-sans-v18-latin-300.woff2 │ │ │ │ ├── open-sans-v18-latin-600.woff2 │ │ │ │ ├── open-sans-v18-latin-700.woff2 │ │ │ │ ├── open-sans-v18-latin-800.woff2 │ │ │ │ ├── open-sans-v18-latin-italic.eot │ │ │ │ ├── open-sans-v18-latin-italic.ttf │ │ │ │ ├── open-sans-v18-latin-italic.woff │ │ │ │ ├── open-sans-v18-latin-regular.eot │ │ │ │ ├── open-sans-v18-latin-regular.ttf │ │ │ │ ├── open-sans-v18-latin-300italic.eot │ │ │ │ ├── open-sans-v18-latin-300italic.ttf │ │ │ │ ├── open-sans-v18-latin-600italic.eot │ │ │ │ ├── open-sans-v18-latin-600italic.ttf │ │ │ │ ├── open-sans-v18-latin-700italic.eot │ │ │ │ ├── open-sans-v18-latin-700italic.ttf │ │ │ │ ├── open-sans-v18-latin-800italic.eot │ │ │ │ ├── open-sans-v18-latin-800italic.ttf │ │ │ │ ├── open-sans-v18-latin-italic.woff2 │ │ │ │ ├── open-sans-v18-latin-regular.woff │ │ │ │ ├── open-sans-v18-latin-regular.woff2 │ │ │ │ ├── open-sans-v18-latin-300italic.woff │ │ │ │ ├── open-sans-v18-latin-300italic.woff2 │ │ │ │ ├── open-sans-v18-latin-600italic.woff │ │ │ │ ├── open-sans-v18-latin-600italic.woff2 │ │ │ │ ├── open-sans-v18-latin-700italic.woff │ │ │ │ ├── open-sans-v18-latin-700italic.woff2 │ │ │ │ ├── open-sans-v18-latin-800italic.woff │ │ │ │ └── open-sans-v18-latin-800italic.woff2 │ │ ├── icons │ │ │ ├── icon-general-pax.svg │ │ │ ├── icon-setup-pinnwand.svg │ │ │ ├── icon-arrow-toLeft.svg │ │ │ ├── icon-arrow-toRight.svg │ │ │ ├── icon-setup-Flipchart.svg │ │ │ ├── icon-setup-chairs.svg │ │ │ ├── icon-setup-Bankett.svg │ │ │ ├── icon-setup-tafel.svg │ │ │ ├── icon-setup-Klassen.svg │ │ │ ├── icon-setup-uForm.svg │ │ │ └── icons-general-rooms.svg │ │ └── data_samples │ │ │ ├── foodAndBeverages.js │ │ │ ├── dailyOverview.js │ │ │ └── eventData.js │ ├── components │ │ ├── Login │ │ │ ├── index.js │ │ │ ├── LoginButton.js │ │ │ ├── Login.stories.js │ │ │ ├── LoginForm.js │ │ │ └── LoginInput.js │ │ ├── helper │ │ │ ├── Main.js │ │ │ └── missingData.js │ │ ├── datasheets │ │ │ ├── DataHeader.js │ │ │ ├── SectionContainer.js │ │ │ ├── DataList.js │ │ │ ├── index.js │ │ │ ├── DataSamples.js │ │ │ ├── Documents.stories.js │ │ │ ├── DataListItem.js │ │ │ ├── DataSheetItem.js │ │ │ └── EventListItem.js │ │ └── Header │ │ │ ├── Notification.stories.js │ │ │ ├── Notification.js │ │ │ ├── MainMenu.stories.js │ │ │ ├── HeaderBar.stories.js │ │ │ ├── MainMenu.js │ │ │ └── index.js │ ├── setupTests.js │ ├── index.js │ ├── reportWebVitals.js │ ├── App.css │ ├── App.js │ ├── Pages │ │ ├── Error.js │ │ ├── Login.stories.js │ │ ├── Today.stories.js │ │ ├── Login.js │ │ ├── Event.js │ │ └── Today.js │ └── GlobalStyle.js ├── .storybook │ ├── main.js │ └── preview.js ├── .gitignore └── package.json ├── documentation ├── artemis_menu.png ├── logo-artemis.jpg ├── artemis_event.png ├── artemis_login.png ├── artemis_nextUp.png ├── Prototyp-Design-Artemis.xd ├── artemis_daily_overview.png ├── Alexis_Roehrling_Artemis.gif └── template │ ├── template.json │ ├── event.json │ └── database.json ├── lib ├── authentication │ ├── jwtTokenSecretGenerator.js │ ├── extractIDandPassword.js │ ├── crypto.js │ ├── isValidHeader.js │ └── isValidToken.js ├── cli │ ├── generateRandomUUID.js │ ├── tokenGenerator.js │ ├── hashingMaster.js │ └── compareHashes.js ├── routes │ ├── documents.js │ ├── user.js │ ├── user │ │ ├── logout.js │ │ ├── refresh.js │ │ └── login.js │ └── documents │ │ ├── eventID.js │ │ └── overviewDay.js ├── utils │ ├── index.js │ ├── sortEventsByTime.js │ ├── createPeriodOfTime.js │ ├── convertTimestamp.js │ ├── convertToDailyOverview.js │ └── convertToFunctionSheet.js └── api │ ├── errorCodes.js │ └── database.js ├── .eslintrc.json ├── .github └── workflows │ └── node.js.yml ├── server.js ├── LICENSE ├── .gitignore ├── package.json └── README.md /.prettierrc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { "ignore": ["client/*"] } 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | **/build/ 3 | **/storybook-static/ -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore artifacts: 2 | build/ 3 | coverage/ 4 | storybook-static/ -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # React client --> looking for Readme.md on root level 2 | 3 | # thanks 4 | -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/src/utils/api/index.js: -------------------------------------------------------------------------------- 1 | import { daily, event } from "./dataFetch"; 2 | 3 | export { daily, event }; 4 | -------------------------------------------------------------------------------- /documentation/artemis_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/documentation/artemis_menu.png -------------------------------------------------------------------------------- /documentation/logo-artemis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/documentation/logo-artemis.jpg -------------------------------------------------------------------------------- /documentation/artemis_event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/documentation/artemis_event.png -------------------------------------------------------------------------------- /documentation/artemis_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/documentation/artemis_login.png -------------------------------------------------------------------------------- /documentation/artemis_nextUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/documentation/artemis_nextUp.png -------------------------------------------------------------------------------- /client/src/assets/logo/logo-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/logo/logo-bg.png -------------------------------------------------------------------------------- /client/src/assets/logo/logo-artemis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/logo/logo-artemis.jpg -------------------------------------------------------------------------------- /client/src/assets/logo/logo-artemis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/logo/logo-artemis.png -------------------------------------------------------------------------------- /documentation/Prototyp-Design-Artemis.xd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/documentation/Prototyp-Design-Artemis.xd -------------------------------------------------------------------------------- /documentation/artemis_daily_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/documentation/artemis_daily_overview.png -------------------------------------------------------------------------------- /client/src/assets/logo/logo-artemis_bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/logo/logo-artemis_bw.png -------------------------------------------------------------------------------- /documentation/Alexis_Roehrling_Artemis.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/documentation/Alexis_Roehrling_Artemis.gif -------------------------------------------------------------------------------- /client/src/utils/helpers/index.js: -------------------------------------------------------------------------------- 1 | import { mockTimestamp, toDataStringFromUnix } from "./date"; 2 | 3 | export { mockTimestamp, toDataStringFromUnix }; 4 | -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-100.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-100.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-100.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-100.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-200.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-200.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-200.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-200.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-300.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-300.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-300.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-300.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-500.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-500.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-500.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-500.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-600.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-600.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-600.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-600.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-700.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-700.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-800.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-800.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-800.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-800.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-900.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-900.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-100.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-100.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-100.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-100.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-200.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-200.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-200.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-200.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-300.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-300.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-500.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-500.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-500.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-600.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-600.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-600.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-700.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-700.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-800.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-800.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-800.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-800.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-900.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-900.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-italic.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-italic.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-300.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-300.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-300.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-300.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-300.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-600.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-600.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-600.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-600.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-600.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-600.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-700.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-700.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-700.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-700.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-700.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-800.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-800.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-800.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-800.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-800.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-800.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-italic.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-italic.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-regular.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-regular.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-regular.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-300.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-600.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-700.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-800.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-800.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-italic.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-italic.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-italic.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-regular.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-regular.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-100italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-100italic.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-100italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-100italic.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-100italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-100italic.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-100italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-100italic.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-200italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-200italic.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-200italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-200italic.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-200italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-200italic.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-200italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-200italic.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-300italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-300italic.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-300italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-300italic.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-300italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-300italic.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-300italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-300italic.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-500italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-500italic.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-500italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-500italic.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-500italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-500italic.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-500italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-500italic.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-600italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-600italic.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-600italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-600italic.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-600italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-600italic.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-600italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-600italic.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-700italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-700italic.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-700italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-700italic.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-700italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-700italic.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-700italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-700italic.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-800italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-800italic.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-800italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-800italic.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-800italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-800italic.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-800italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-800italic.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-900italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-900italic.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-900italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-900italic.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-900italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-900italic.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-900italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-900italic.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Poppins/poppins-v15-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Poppins/poppins-v15-latin-regular.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-300italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-300italic.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-300italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-300italic.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-600italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-600italic.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-600italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-600italic.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-700italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-700italic.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-700italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-700italic.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-800italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-800italic.eot -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-800italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-800italic.ttf -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-italic.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-regular.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-regular.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-300italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-300italic.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-300italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-300italic.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-600italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-600italic.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-600italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-600italic.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-700italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-700italic.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-700italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-700italic.woff2 -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-800italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-800italic.woff -------------------------------------------------------------------------------- /client/src/assets/fonts/Open_Sans/open-sans-v18-latin-800italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexisRoe/Artemis/HEAD/client/src/assets/fonts/Open_Sans/open-sans-v18-latin-800italic.woff2 -------------------------------------------------------------------------------- /lib/authentication/jwtTokenSecretGenerator.js: -------------------------------------------------------------------------------- 1 | function generateTokenSecret(bytes = 64) { 2 | return require("crypto").randomBytes(bytes).toString("hex"); 3 | } 4 | 5 | module.exports = generateTokenSecret; 6 | -------------------------------------------------------------------------------- /client/src/components/Login/index.js: -------------------------------------------------------------------------------- 1 | import { LoginButton } from "./LoginButton"; 2 | import { LoginInput } from "./LoginInput"; 3 | import { LoginForm } from "./LoginForm"; 4 | export { LoginButton, LoginInput, LoginForm }; 5 | -------------------------------------------------------------------------------- /lib/cli/generateRandomUUID.js: -------------------------------------------------------------------------------- 1 | const { v4: uuidv4 } = require("uuid"); 2 | 3 | async function generateRandomUUID() { 4 | const uuid = uuidv4(); 5 | console.log(`your UUID:\n${uuid}`); 6 | } 7 | 8 | generateRandomUUID(); 9 | -------------------------------------------------------------------------------- /client/src/assets/icons/icon-general-pax.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/helper/Main.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | 3 | export const Main = styled.main` 4 | width: 100%; 5 | padding-top: 100px; 6 | 7 | > * { 8 | width: 100%; 9 | } 10 | `; 11 | -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Artemis", 3 | "name": "Artemis - Function Sheets 2.0", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "theme_color": "#000000", 7 | "background_color": "#ffffff" 8 | } 9 | -------------------------------------------------------------------------------- /client/src/assets/icons/icon-setup-pinnwand.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/datasheets/DataHeader.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components/macro"; 2 | 3 | export const DataHeader = styled.h3` 4 | color: var(--color-golden); 5 | padding: 0px 2rem; 6 | margin-bottom: 1.5rem; 7 | margin-top: 1rem; 8 | `; 9 | -------------------------------------------------------------------------------- /client/src/components/datasheets/SectionContainer.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components/macro"; 2 | 3 | export const SectionContainer = styled.article` 4 | width: 100%; 5 | padding: 2rem 0px; 6 | border-bottom: 1px solid var(--color-golden-dark); 7 | `; 8 | -------------------------------------------------------------------------------- /client/src/assets/icons/icon-arrow-toLeft.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/assets/icons/icon-arrow-toRight.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], 3 | addons: [ 4 | "@storybook/addon-links", 5 | "@storybook/addon-essentials", 6 | "@storybook/preset-create-react-app", 7 | ], 8 | }; 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /client/src/assets/icons/icon-setup-Flipchart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/authentication/extractIDandPassword.js: -------------------------------------------------------------------------------- 1 | function extractIDandPasswort(header) { 2 | const buff = Buffer.from(header.split(" ")[1], "base64"); 3 | const [id, password] = buff.toString("utf-8").split(":"); 4 | return { id, password }; 5 | } 6 | 7 | module.exports = extractIDandPasswort; 8 | -------------------------------------------------------------------------------- /client/src/assets/icons/icon-setup-chairs.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/routes/documents.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const event = require("./documents/eventID"); 3 | const overview = require("./documents/overviewDay"); 4 | 5 | const router = Router(); 6 | 7 | router.use("/event", event); 8 | router.use("/date", overview); 9 | 10 | module.exports = router; 11 | -------------------------------------------------------------------------------- /client/src/assets/icons/icon-setup-Bankett.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/datasheets/DataList.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components/macro"; 2 | 3 | export const DataListContainer = styled.ul` 4 | padding: 0; 5 | margin: 0; 6 | list-style: none; 7 | a, 8 | a:hover, 9 | a:focus, 10 | a:visited { 11 | text-decoration: none; 12 | color: var(--font-body-copy); 13 | } 14 | `; 15 | -------------------------------------------------------------------------------- /client/src/assets/icons/icon-setup-tafel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/routes/user.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const login = require("./user/login"); 3 | const logout = require("./user/logout"); 4 | const refresh = require("./user/refresh"); 5 | 6 | const router = Router(); 7 | 8 | router.use("/login", login); 9 | router.use("/logout", logout); 10 | router.use("/refresh", refresh); 11 | 12 | module.exports = router; 13 | -------------------------------------------------------------------------------- /client/src/utils/router/LoginRoute.js: -------------------------------------------------------------------------------- 1 | import { Redirect, Route } from "react-router-dom"; 2 | import { useUserContext } from "../context/Context"; 3 | 4 | export default function LoginRoute(props) { 5 | const { user } = useUserContext(); 6 | 7 | if (user?.auth_token) { 8 | return ; 9 | } else { 10 | return ; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/utils/index.js: -------------------------------------------------------------------------------- 1 | const { createPeriodOfTime } = require("./createPeriodOfTime"); 2 | const { buildDailyOverview } = require("./convertToDailyOverview"); 3 | const { createFunctionSheet } = require("./convertToFunctionSheet"); 4 | 5 | exports.createPeriodOfTime = createPeriodOfTime; 6 | exports.buildDailyOverview = buildDailyOverview; 7 | exports.createFunctionSheet = createFunctionSheet; 8 | -------------------------------------------------------------------------------- /client/src/utils/router/ProtectedRoute.js: -------------------------------------------------------------------------------- 1 | import { Redirect, Route } from "react-router-dom"; 2 | import { useUserContext } from "../context/Context"; 3 | 4 | export default function ProtectedRoute(props) { 5 | const { user } = useUserContext(); 6 | 7 | if (user?.auth_token) { 8 | return ; 9 | } else { 10 | return ; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/utils/sortEventsByTime.js: -------------------------------------------------------------------------------- 1 | function sortEventsByTime(array) { 2 | array.sort((firstElement, secondElement) => { 3 | if (firstElement.time > secondElement.time) { 4 | return 1; 5 | } 6 | if (firstElement.time < secondElement.time) { 7 | return -1; 8 | } 9 | 10 | return 0; 11 | }); 12 | return array; 13 | } 14 | 15 | exports.sortEventsByTime = sortEventsByTime; 16 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./App"; 4 | 5 | setTimeout(() => { 6 | document 7 | .querySelector(".loader") 8 | .parentNode.removeChild(document.querySelector(".loader")); 9 | 10 | ReactDOM.render( 11 | 12 | 13 | , 14 | document.getElementById("root") 15 | ); 16 | }, 1500); 17 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /client/.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 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | /storybook-static 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /client/src/assets/data_samples/foodAndBeverages.js: -------------------------------------------------------------------------------- 1 | const fbSamples = [ 2 | { 3 | time: "10:00 - 16:00", 4 | title: "Meetingroom", 5 | room: "Raum 123", 6 | notes: ["1 gr. Flasche Wasser + 2 kl. Flaschen Saft"], 7 | }, 8 | { 9 | time: "11:00 - 11:30", 10 | title: "Kaffeepause", 11 | room: "Lounge", 12 | notes: ["Kaffee/Tee", "Orangensaft", "Belegte Brezeln"], 13 | }, 14 | ]; 15 | 16 | console.log(fbSamples); 17 | -------------------------------------------------------------------------------- /client/.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import GlobalStyle from "../src/GlobalStyle"; 3 | import { BrowserRouter as Router } from "react-router-dom"; 4 | 5 | export const decorators = [ 6 | (Story) => ( 7 | <> 8 | 9 | 10 | 11 | 12 | 13 | ), 14 | ]; 15 | 16 | export const parameters = { 17 | actions: { argTypesRegex: "^on[A-Z].*" }, 18 | layout: "fullscreen", 19 | }; 20 | -------------------------------------------------------------------------------- /lib/authentication/crypto.js: -------------------------------------------------------------------------------- 1 | const bcrypt = require("bcrypt"); 2 | const saltRounds = 10; 3 | 4 | async function comparePasswords(storedPassword, plainPassword) { 5 | const result = await bcrypt.compare(plainPassword, storedPassword); 6 | return result; 7 | } 8 | 9 | async function hashPassword(pwdInput) { 10 | const pwd = await bcrypt.hash(pwdInput, saltRounds); 11 | return pwd; 12 | } 13 | 14 | module.exports = comparePasswords; 15 | module.exports = hashPassword; 16 | -------------------------------------------------------------------------------- /client/src/components/datasheets/index.js: -------------------------------------------------------------------------------- 1 | import { SectionContainer } from "./SectionContainer"; 2 | import { DataHeader } from "./DataHeader"; 3 | import { DataListContainer } from "./DataList"; 4 | import { DataListItem } from "./DataListItem"; 5 | import { EventListItem } from "./EventListItem"; 6 | import { DataSheetItem } from "./DataSheetItem"; 7 | 8 | export { 9 | SectionContainer, 10 | DataListItem, 11 | DataHeader, 12 | DataListContainer, 13 | EventListItem, 14 | DataSheetItem, 15 | }; 16 | -------------------------------------------------------------------------------- /client/src/assets/icons/icon-setup-Klassen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/utils/api/dataFetch.js: -------------------------------------------------------------------------------- 1 | const fetchData = (route) => async (id, token) => { 2 | try { 3 | const response = await fetch(`/api/documents/${route}/${id}`, { 4 | method: "GET", 5 | headers: { 6 | authorization: token, 7 | }, 8 | }); 9 | const data = await response.json(); 10 | return data; 11 | } catch (error) { 12 | throw new Error(error.message); 13 | } 14 | }; 15 | 16 | export const daily = fetchData("date"); 17 | export const event = fetchData("event"); 18 | -------------------------------------------------------------------------------- /client/src/utils/config/constants.js: -------------------------------------------------------------------------------- 1 | export const USER = "currentUser"; 2 | export const TITLE_DAY = "Daily Overview"; 3 | export const COOKIE_NAME = "auth_token"; 4 | export const ANIMATION_SLIDE_IN = "var(--animation-slide-in)"; 5 | export const ANIMATION_SLIDE_OUT = "var(--animation-slide-out)"; 6 | export const ANIMATION_ERROR = "var(--animation-error)"; 7 | export const STANDARD_404 = 8 | "no data for specific date/event found, please try again later"; 9 | export const STANDARD_500 = 10 | "Error: Try again or contact the system administrator"; 11 | -------------------------------------------------------------------------------- /client/src/assets/icons/icon-setup-uForm.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/cli/tokenGenerator.js: -------------------------------------------------------------------------------- 1 | const inquirer = require("inquirer"); 2 | 3 | async function askUser(question) { 4 | const { answer } = await inquirer.prompt([ 5 | { 6 | type: "string", 7 | name: "answer", 8 | message: question, 9 | }, 10 | ]); 11 | 12 | return answer; 13 | } 14 | 15 | async function tokenGenerator() { 16 | let bytes = await askUser( 17 | "How many bytes should be used for creating the token?\n default: 64byte >" 18 | ); 19 | if (!bytes) { 20 | bytes = 64; 21 | } 22 | const token = require("crypto").randomBytes(bytes).toString("hex"); 23 | console.log(`token_secret:\n${token}`); 24 | } 25 | 26 | tokenGenerator(); 27 | -------------------------------------------------------------------------------- /client/src/assets/icons/icons-general-rooms.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/api/errorCodes.js: -------------------------------------------------------------------------------- 1 | const errorMessages = { 2 | authorization: { 3 | 400: { code: 400, message: "transmitted data corrupted" }, 4 | 401: { code: 401, message: "invalid authorization token" }, 5 | 404: { code: 404, message: "User not found for the submitted id" }, 6 | }, 7 | timestamp: { 8 | 400: { code: 400, message: "invalid timestamp submitted" }, 9 | 404: { code: 404, message: "no events found today" }, 10 | }, 11 | event: { 12 | 400: { code: 400, message: "no valid objectId submitted" }, 13 | 404: { code: 404, message: "no events found today" }, 14 | }, 15 | server: { 16 | code: 500, 17 | message: "an internal server error accured", 18 | }, 19 | }; 20 | 21 | exports.errorMessages = errorMessages; 22 | -------------------------------------------------------------------------------- /lib/cli/hashingMaster.js: -------------------------------------------------------------------------------- 1 | const bcrypt = require("bcrypt"); 2 | const inquirer = require("inquirer"); 3 | const saltRounds = 10; 4 | 5 | async function hash(pwdInput) { 6 | const pwd = await bcrypt.hash(pwdInput, saltRounds); 7 | return pwd; 8 | } 9 | 10 | async function askUser(question) { 11 | const { answer } = await inquirer.prompt([ 12 | { 13 | type: "string", 14 | name: "answer", 15 | message: question, 16 | }, 17 | ]); 18 | 19 | return answer; 20 | } 21 | 22 | async function hashingPassword() { 23 | const passwordToHash = await askUser("Password to hash:"); 24 | const hashedPassword = await hash(passwordToHash); 25 | console.log(`Passwordhash:\n${hashedPassword}`); 26 | } 27 | 28 | hashingPassword(); 29 | -------------------------------------------------------------------------------- /lib/utils/createPeriodOfTime.js: -------------------------------------------------------------------------------- 1 | const toUnixTime = (date) => Math.floor(date / 1000); 2 | 3 | function createPeriodOfTime(timestamp) { 4 | const givenDate = new Date(timestamp * 1000); 5 | const start = toUnixTime( 6 | new Date( 7 | givenDate.getFullYear(), 8 | givenDate.getMonth(), 9 | givenDate.getDate(), 10 | 0, 11 | 0, 12 | 0, 13 | 1 14 | ) 15 | ); 16 | const end = toUnixTime( 17 | new Date( 18 | givenDate.getFullYear(), 19 | givenDate.getMonth(), 20 | givenDate.getDate(), 21 | 23, 22 | 59, 23 | 59, 24 | 999 25 | ) 26 | ); 27 | return { startPeriod: start, endPeriod: end, timeNow: timestamp }; 28 | } 29 | 30 | exports.createPeriodOfTime = createPeriodOfTime; 31 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true 7 | }, 8 | "extends": [ 9 | "react-app", 10 | "eslint:recommended", 11 | "plugin:react/recommended", 12 | "prettier" 13 | ], 14 | "parserOptions": { 15 | "ecmaFeatures": { 16 | "jsx": true 17 | }, 18 | "ecmaVersion": 12, 19 | "sourceType": "module" 20 | }, 21 | "settings": { 22 | "react": { 23 | "version": "latest" 24 | } 25 | }, 26 | "rules": { 27 | "react/react-in-jsx-scope": "off", 28 | "no-unused-vars": [ 29 | "warn", 30 | { "vars": "all", "args": "after-used", "ignoreRestSiblings": true } 31 | ], 32 | "import/no-anonymous-default-export": "off" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/utils/convertTimestamp.js: -------------------------------------------------------------------------------- 1 | function createTime(document) { 2 | return `${convertTime(document.start)} - ${convertTime(document.end)}`; 3 | } 4 | 5 | function convertTime(unixTimestamp) { 6 | const date = new Date((unixTimestamp - 7200) * 1000); 7 | return new Intl.DateTimeFormat("de-DE", { 8 | hour12: false, 9 | timeStyle: "short", 10 | }).format(date); 11 | } 12 | 13 | function convertDate(unixTimestamp) { 14 | const date = new Date((unixTimestamp - 7200) * 1000); 15 | const dateString = new Intl.DateTimeFormat("de-DE", { 16 | year: "numeric", 17 | month: "numeric", 18 | day: "numeric", 19 | }).format(date); 20 | return dateString.replace(/\D/g, "."); 21 | } 22 | 23 | exports.createTime = createTime; 24 | exports.convertDate = convertDate; 25 | -------------------------------------------------------------------------------- /client/src/components/Login/LoginButton.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components/macro"; 2 | import PropTypes from "prop-types"; 3 | 4 | const Button = styled.button` 5 | width: 100%; 6 | height: 50px; 7 | margin-top: 2rem; 8 | font-family: var(---font-family-header); 9 | text-transform: uppercase; 10 | font-weight: 600; 11 | font-size: 1.5rem; 12 | color: var(--color-font-white); 13 | background-color: ${(props) => 14 | props.state ? "var(--color-state-error)" : "var(--color-golden)"}; 15 | border: none; 16 | cursor: pointer; 17 | `; 18 | 19 | export function LoginButton({ error }) { 20 | return ( 21 | <> 22 | 23 | 24 | ); 25 | } 26 | 27 | LoginButton.propTypes = { 28 | error: PropTypes.bool, 29 | }; 30 | -------------------------------------------------------------------------------- /client/src/components/helper/missingData.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import logo from "../../assets/logo/logo-bg.svg"; 3 | 4 | const Div = styled.div` 5 | width: 100%; 6 | min-height: 250px; 7 | display: grid; 8 | place-items: center; 9 | background-image: url(${logo}); 10 | background-repeat: no-repeat; 11 | background-size: 25%; 12 | background-position: center center; 13 | 14 | img { 15 | z-index: -1; 16 | width: 130px; 17 | height: auto; 18 | } 19 | 20 | h4 { 21 | width: 200px; 22 | text-align: center; 23 | } 24 | `; 25 | 26 | export default function MissingData() { 27 | return ( 28 |
29 |

30 | nothing found to do
31 | in the next 6 hours 32 |

33 |
34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /lib/cli/compareHashes.js: -------------------------------------------------------------------------------- 1 | const bcrypt = require("bcrypt"); 2 | const inquirer = require("inquirer"); 3 | 4 | async function compare(storedPassword, plainPassword) { 5 | const result = await bcrypt.compare(plainPassword, storedPassword); 6 | return result; 7 | } 8 | 9 | async function askUser(question) { 10 | const { answer } = await inquirer.prompt([ 11 | { 12 | type: "string", 13 | name: "answer", 14 | message: question, 15 | }, 16 | ]); 17 | 18 | return answer; 19 | } 20 | 21 | async function comparePasswords() { 22 | const storedPassword = await askUser("the stored hash:"); 23 | const plainPassword = await askUser("the plain password:"); 24 | const answer = await compare(storedPassword, plainPassword); 25 | console.log(`The two passwords are equal: ${answer}`); 26 | } 27 | 28 | comparePasswords(); 29 | -------------------------------------------------------------------------------- /client/src/utils/helpers/date.js: -------------------------------------------------------------------------------- 1 | export const mockTimestamp = () => { 2 | const today = new Date(); 3 | const mockedDate = new Date( 4 | 2020, 5 | 9, 6 | today.getDay() + 1, 7 | today.getHours(), 8 | today.getMinutes(), 9 | 0, 10 | 0 11 | ); 12 | return toUnixTime(mockedDate); 13 | }; 14 | 15 | export const toDataStringFromUnix = (unixTimestamp) => 16 | toTimeFormat(new Date(unixTimestamp * 1000)); 17 | 18 | const toUnixTime = (date = Date.now()) => Math.floor(date / 1000); 19 | 20 | const toTimeFormat = (time = new Date()) => 21 | `${("0" + time.getHours()).slice(-2)}.${("0" + time.getMinutes()).slice(-2)}`; 22 | 23 | export const toDateFormat = (today = new Date()) => 24 | new Intl.DateTimeFormat("de-DE", { 25 | year: "numeric", 26 | month: "numeric", 27 | day: "numeric", 28 | }).format(today); 29 | -------------------------------------------------------------------------------- /.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: [master] 9 | pull_request: 10 | branches: [master] 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | strategy: 17 | matrix: 18 | node-version: [10.x, 12.x, 14.x] 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Use Node.js ${{ matrix.node-version }} 23 | uses: actions/setup-node@v1 24 | with: 25 | node-version: ${{ matrix.node-version }} 26 | - run: npm ci 27 | - run: npm run build --if-present 28 | - run: npm test 29 | -------------------------------------------------------------------------------- /client/src/components/Login/Login.stories.js: -------------------------------------------------------------------------------- 1 | import { LoginButton } from "./LoginButton"; 2 | import { LoginInput } from "./LoginInput"; 3 | import { LoginForm } from "./LoginForm"; 4 | 5 | export default { 6 | title: "Components/Login", 7 | component: LoginButton, 8 | LoginInput, 9 | }; 10 | 11 | export const InputField = (args) => ; 12 | 13 | InputField.args = { 14 | title: "Mitarbeiter-ID", 15 | error: true, 16 | }; 17 | 18 | export const Button = (args) => ; 19 | 20 | Button.args = { 21 | error: true, 22 | }; 23 | 24 | export const Login = (args) => ( 25 | 26 | 27 | 28 | 29 | Login 30 | 31 | 32 | ); 33 | 34 | Login.args = { 35 | error: true, 36 | }; 37 | -------------------------------------------------------------------------------- /client/src/components/Login/LoginForm.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components/macro"; 2 | import logo from "../../assets/logo/logo-bg.svg"; 3 | import PropTypes from "prop-types"; 4 | 5 | export const Form = styled.form` 6 | width: 100%; 7 | height: calc(100vh - 100px); 8 | padding: 5rem; 9 | padding-top: 20vh; 10 | display: flex; 11 | flex-direction: column; 12 | position: relative; 13 | background-image: url(${logo}); 14 | background-repeat: no-repeat; 15 | background-size: cover; 16 | background-position: -25vw 30vh; 17 | `; 18 | 19 | export const Img = styled.img` 20 | z-index: -1; 21 | width: 200%; 22 | height: auto; 23 | transform: translate(-10vw, -15vh); 24 | `; 25 | 26 | export function LoginForm({ children, onSubmit }) { 27 | return
{children}
; 28 | } 29 | 30 | LoginForm.propTypes = { 31 | children: PropTypes.node, 32 | onSubmit: PropTypes.func, 33 | }; 34 | -------------------------------------------------------------------------------- /client/src/components/Header/Notification.stories.js: -------------------------------------------------------------------------------- 1 | import NotificationHeader from "./Notification"; 2 | 3 | export const NotificationBar = (args) => { 4 | const NotificationMessage = () => { 5 | if (args.isError) { 6 | return "An Error accured"; 7 | } else { 8 | return args.message; 9 | } 10 | }; 11 | 12 | return ( 13 | 14 | ); 15 | }; 16 | 17 | export default { 18 | title: "Components/Header", 19 | components: NotificationBar, 20 | argTypes: { 21 | animation: { 22 | table: { 23 | disable: true, 24 | }, 25 | }, 26 | date: { 27 | table: { 28 | disable: true, 29 | }, 30 | }, 31 | title: { 32 | table: { 33 | disable: true, 34 | }, 35 | }, 36 | }, 37 | }; 38 | 39 | NotificationBar.args = { 40 | isError: true, 41 | message: "loading ...", 42 | }; 43 | -------------------------------------------------------------------------------- /lib/routes/user/logout.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | const { Router } = require("express"); 3 | const { modifyUser } = require("../../api/database"); 4 | const { errorMessages } = require("../../api/errorCodes"); 5 | const { ObjectID } = require("mongodb"); 6 | const router = Router(); 7 | 8 | router.delete("/", async (request, response) => { 9 | console.log("logout ...."); 10 | try { 11 | const { auth_token } = request.cookies; 12 | const { id } = jwt.verify(auth_token, process.env.TOKEN_SECRET); 13 | 14 | await modifyUser( 15 | process.env.DB_COLLECTION_USER, 16 | new ObjectID.createFromHexString(id), 17 | "" 18 | ); 19 | 20 | response.clearCookie("auth_token"); 21 | response.status(200).json({ code: 200, message: "logout successful" }); 22 | } catch (error) { 23 | response.status(500).json({ ...errorMessages.server, desc: error.message }); 24 | } 25 | }); 26 | 27 | module.exports = router; 28 | -------------------------------------------------------------------------------- /client/src/components/datasheets/DataSamples.js: -------------------------------------------------------------------------------- 1 | export const DataListSamples = [ 2 | { 3 | time: "10:00", 4 | title: "Kaffeepause", 5 | description: "Telekom Team 1", 6 | room: "Lounge", 7 | }, 8 | { 9 | time: "10:15", 10 | title: "Snacks", 11 | description: "Telekom Team 2", 12 | room: "Raum 123", 13 | }, 14 | ]; 15 | 16 | export const DataSheetSamples = [ 17 | { 18 | time: "10:00 - 16:00", 19 | title: "Meetingroom", 20 | room: "Raum 123", 21 | notes: ["3 Flipcharts", "1 Pinnwand", "1 Beamer"], 22 | }, 23 | ]; 24 | 25 | export const EventSamples = [ 26 | { 27 | time: "10:00 - 16:00", 28 | title: "Telekom Team 1", 29 | room: "120", 30 | setup: 0, 31 | pax: 120, 32 | pinboard: 2, 33 | flipchart: 1, 34 | }, 35 | { 36 | time: "10:00 - 16:00", 37 | title: "Telekom Team 2", 38 | room: "120", 39 | setup: 3, 40 | pax: 12, 41 | pinboard: 1, 42 | flipchart: 2, 43 | }, 44 | ]; 45 | -------------------------------------------------------------------------------- /client/src/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import GlobalStyle from "./GlobalStyle"; 3 | import { BrowserRouter as Router, Switch, Redirect } from "react-router-dom"; 4 | import { UserData } from "./utils/context/Context"; 5 | import ProtectedRoute from "./utils/router/ProtectedRoute"; 6 | import LoginRoute from "./utils/router/LoginRoute"; 7 | import Today from "./Pages/Today"; 8 | import Event from "./Pages/Event"; 9 | import Login from "./Pages/Login"; 10 | 11 | function App() { 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | } 27 | 28 | export default App; 29 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | const express = require("express"); 3 | const path = require("path"); 4 | const cookieParser = require("cookie-parser"); 5 | 6 | const { connect } = require("./lib/api/database"); 7 | 8 | const documents = require("./lib/routes/documents"); 9 | const user = require("./lib/routes/user"); 10 | 11 | const app = express(); 12 | 13 | app.use(express.json()); 14 | app.use(cookieParser()); 15 | app.use("/api/documents", documents); 16 | app.use("/api/user", user); 17 | 18 | // Heroku Deployment 19 | app.use(express.static(path.join(__dirname, "client/build"))); 20 | app.use( 21 | "/storybook", 22 | express.static(path.join(__dirname, "client/storybook-static")) 23 | ); 24 | app.get("*", (request, response) => { 25 | response.sendFile(path.join(__dirname, "client/build", "index.html")); 26 | }); 27 | 28 | // server 29 | async function run() { 30 | console.log("Connecting to database ..."); 31 | await connect(process.env.DB_URL); 32 | app.listen(process.env.PORT || 6000); 33 | } 34 | 35 | run(); 36 | -------------------------------------------------------------------------------- /client/src/assets/data_samples/dailyOverview.js: -------------------------------------------------------------------------------- 1 | const args = { 2 | arrowleft: true, 3 | arrowright: true, 4 | filter: true, 5 | }; 6 | 7 | const nextSamples = [ 8 | { 9 | time: "10:00", 10 | title: "Kaffeepause", 11 | description: "Telekom Team 1", 12 | room: "Lounge", 13 | }, 14 | { 15 | time: "10:15", 16 | title: "Snacks", 17 | description: "Telekom Team 2", 18 | room: "Raum 123", 19 | }, 20 | { 21 | time: "10:30", 22 | title: "Kaffeepause", 23 | description: "Telekom Team 3", 24 | room: "Lounge", 25 | }, 26 | ]; 27 | 28 | const eventSamples = [ 29 | { 30 | time: "10:00 - 16:00", 31 | title: "Telekom Team 1", 32 | room: "120", 33 | setup: 0, 34 | pax: 120, 35 | pinboard: 2, 36 | flipchart: 1, 37 | }, 38 | { 39 | time: "10:00 - 16:00", 40 | title: "Telekom Team 2", 41 | room: "120", 42 | setup: 3, 43 | pax: 12, 44 | pinboard: 1, 45 | flipchart: 2, 46 | }, 47 | ]; 48 | 49 | const mockup = { 50 | eventSamples, 51 | nextSamples, 52 | args, 53 | }; 54 | -------------------------------------------------------------------------------- /lib/authentication/isValidHeader.js: -------------------------------------------------------------------------------- 1 | const { errorMessages } = require("../api/errorCodes"); 2 | 3 | function isValidHeader(request, response, next) { 4 | try { 5 | const { id, password } = request.body; 6 | 7 | if (!id) { 8 | response.status(400).json({ 9 | ...errorMessages.authorization[400], 10 | desc: "no id included in the message, denied", 11 | }); 12 | return; 13 | } 14 | 15 | if (!password) { 16 | response.status(400).json({ 17 | ...errorMessages.authorization[400], 18 | desc: "no password included in the message, denied", 19 | }); 20 | return; 21 | } 22 | 23 | if (typeof id != "string" || typeof password != "string") { 24 | response.status(400).json({ 25 | ...errorMessages.authorization[400], 26 | desc: "id/passwort value have the wrong data type", 27 | }); 28 | return; 29 | } 30 | 31 | next(); 32 | } catch (error) { 33 | response.status(500).json({ ...errorMessages.server, desc: error.message }); 34 | } 35 | } 36 | 37 | module.exports = isValidHeader; 38 | -------------------------------------------------------------------------------- /lib/api/database.js: -------------------------------------------------------------------------------- 1 | const { MongoClient } = require("mongodb"); 2 | 3 | let client; 4 | let db; 5 | 6 | async function connect(url) { 7 | client = await MongoClient.connect(url, { useUnifiedTopology: true }); 8 | db = client.db(process.env.DB_NAME); 9 | } 10 | 11 | async function findOne(name, query) { 12 | return await db.collection(name).findOne(query); 13 | } 14 | 15 | async function find(name, query) { 16 | const results = await db.collection(name).find(query).toArray(); 17 | return results; 18 | } 19 | 20 | async function modifyUser(name, id, newValue) { 21 | try { 22 | await db.collection(name).updateOne( 23 | { _id: id }, 24 | { 25 | $currentDate: { 26 | lastModified: true, 27 | }, 28 | $set: { 29 | auth_token: newValue, 30 | }, 31 | } 32 | ); 33 | } catch (error) { 34 | throw new Error(error.message); 35 | } 36 | } 37 | 38 | function close() { 39 | return client.close(); 40 | } 41 | 42 | exports.connect = connect; 43 | exports.findOne = findOne; 44 | exports.find = find; 45 | exports.modifyUser = modifyUser; 46 | exports.close = close; 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Alexis Roehrling 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/Pages/Error.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import logo from "../assets/logo/logo-bg.svg"; 3 | import { useHistory } from "react-router-dom"; 4 | import { STANDARD_500 } from "../utils/config/constants"; 5 | 6 | const Article = styled.article` 7 | padding: 25vh 25%; 8 | width: 100%; 9 | height: calc(100vh - 100px); 10 | background-image: url(${logo}); 11 | background-repeat: no-repeat; 12 | background-size: cover; 13 | background-position: -25vw 30vh; 14 | 15 | h2 { 16 | text-align: center; 17 | margin-bottom: 1rem; 18 | } 19 | button { 20 | width: 100%; 21 | height: 50px; 22 | font-family: var(---font-family-header); 23 | text-transform: uppercase; 24 | font-weight: 600; 25 | font-size: 1.5rem; 26 | color: var(--color-font-white); 27 | background-color: var(--color-golden); 28 | border: none; 29 | margin-top: 2rem; 30 | cursor: pointer; 31 | } 32 | `; 33 | 34 | export default function ErrorHandler() { 35 | const history = useHistory(); 36 | 37 | return ( 38 |
39 |

{STANDARD_500}

40 | 41 |
42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /lib/routes/documents/eventID.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const { ObjectID } = require("mongodb"); 3 | const { findOne } = require("../../api/database"); 4 | const { errorMessages } = require("../../api/errorCodes"); 5 | const isValidToken = require("../../authentication/isValidToken"); 6 | const { createFunctionSheet } = require("../../utils/convertToFunctionSheet"); 7 | 8 | const router = Router(); 9 | 10 | router.get("/:eventID", isValidToken, async (request, response) => { 11 | try { 12 | if (!ObjectID.isValid(request.params.eventID)) { 13 | response.status(400).json(errorMessages.event[400]); 14 | return; 15 | } 16 | 17 | const result = await findOne(process.env.DB_COLLECTION_EVENTS, { 18 | _id: new ObjectID.createFromHexString(request.params.eventID), 19 | }); 20 | 21 | if (!result) { 22 | response.status(404).json(errorMessages.event[404]); 23 | return; 24 | } 25 | 26 | const functionSheet = createFunctionSheet(result); 27 | 28 | response.json({ code: 200, ...functionSheet }); 29 | } catch (error) { 30 | response.status(500).json({ ...errorMessages.server, desc: error.message }); 31 | } 32 | }); 33 | 34 | module.exports = router; 35 | -------------------------------------------------------------------------------- /client/src/components/Login/LoginInput.js: -------------------------------------------------------------------------------- 1 | import PropTypes from "prop-types"; 2 | import styled from "styled-components/macro"; 3 | 4 | const InputLabel = styled.label` 5 | display: flex; 6 | flex-direction: column; 7 | gap: 0.3rem; 8 | color: ${(props) => 9 | props.state ? "var(--color-state-error)" : "var(--color-golden)"}; 10 | `; 11 | 12 | const InputField = styled.input` 13 | min-height: 50px; 14 | background: rgba(255, 255, 255, 0.4); 15 | border: 1px solid 16 | ${(props) => 17 | props.state ? "var(--color-state-error)" : "var(--color-golden)"}; 18 | padding: 0 1rem; 19 | `; 20 | 21 | export const LoginInput = ({ title, value, type, onChange, error = false }) => { 22 | return ( 23 | 24 | {title} 25 | 34 | 35 | ); 36 | }; 37 | 38 | LoginInput.propTypes = { 39 | title: PropTypes.string.isRequired, 40 | value: PropTypes.string, 41 | type: PropTypes.string, 42 | onChange: PropTypes.func, 43 | error: PropTypes.bool, 44 | }; 45 | -------------------------------------------------------------------------------- /client/src/components/datasheets/Documents.stories.js: -------------------------------------------------------------------------------- 1 | import { DataListContainer } from "./DataList"; 2 | import { EventListItem } from "./EventListItem"; 3 | import { DataSheetItem } from "./DataSheetItem"; 4 | import { DataListItem } from "./DataListItem"; 5 | 6 | import { DataListSamples, DataSheetSamples, EventSamples } from "./DataSamples"; 7 | 8 | export const EventItem = () => { 9 | return ( 10 | 11 | {EventSamples.map((sample) => { 12 | return ; 13 | })} 14 | 15 | ); 16 | }; 17 | 18 | export const DataSheetSimple = () => { 19 | return ( 20 | 21 | {DataSheetSamples.map((sample) => { 22 | return ; 23 | })} 24 | 25 | ); 26 | }; 27 | 28 | export const NextUp = () => { 29 | return ( 30 | 31 | {DataListSamples.map((sample) => { 32 | return ; 33 | })} 34 | 35 | ); 36 | }; 37 | 38 | export default { 39 | title: "Components/Documents", 40 | component: EventItem, 41 | DataSheetSimple, 42 | NextUp, 43 | }; 44 | -------------------------------------------------------------------------------- /documentation/template/template.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "string", 4 | "sign": "string", 5 | "Customer-ID": 1, 6 | "event-ID": 1, 7 | "start": "2020-10-01T10:00:00", 8 | "end": "2020-10-01T16:00:00", 9 | "pax": 1, 10 | "rooms": [ 11 | { 12 | "location": "string", 13 | "title": "string", 14 | "start": "2020-10-01T10:00:00", 15 | "end": "2020-10-01T10:00:00", 16 | "default": true, 17 | "role": "string", 18 | "pax": 1, 19 | "setup": "string icons", 20 | "equipment": { 21 | "flipchart": 1, 22 | "pinnboard": 2, 23 | "beamer": 1 24 | }, 25 | "notes": [""] 26 | } 27 | ], 28 | "event": [ 29 | { 30 | "title": "string", 31 | "location": "string", 32 | "pax": 0, 33 | "start": "2020-10-01T10:00:00", 34 | "end": "2020-10-01T10:00:00", 35 | "foods": ["string", "string", "string"], 36 | "beverages": ["string", "string", "string"] 37 | } 38 | ] 39 | }, 40 | { 41 | "icons": { 42 | "iconClassRoom": "iconClassRoom", 43 | "iconUForm": "iconUForm", 44 | "iconBoard": "iconBoard", 45 | "iconBanquet": "iconBanquet", 46 | "iconChairCircle": "iconChairCircle" 47 | } 48 | } 49 | ] 50 | -------------------------------------------------------------------------------- /client/src/Pages/Login.stories.js: -------------------------------------------------------------------------------- 1 | import logoSrc from "../assets/logo/logo-artemis.png"; 2 | import { Article, Button, InformationContainer } from "../components/Header"; 3 | import { LoginButton } from "../components/Login/LoginButton"; 4 | import { LoginInput } from "../components/Login/LoginInput"; 5 | import { LoginForm } from "../components/Login/LoginForm"; 6 | import styled from "styled-components/macro"; 7 | 8 | const Main = styled.main` 9 | width: 100%; 10 | padding-top: 100px; 11 | 12 | > * { 13 | width: 100%; 14 | } 15 | `; 16 | 17 | export const Login = () => { 18 | return ( 19 | <> 20 |
21 |
22 | 23 |

01.01.2021

24 |

Login

25 |
26 | 29 |
30 |
31 |
32 | 33 | 34 | 35 | Login 36 | 37 |
38 | 39 | ); 40 | }; 41 | 42 | export default { 43 | title: "Pages", 44 | components: Login, 45 | }; 46 | -------------------------------------------------------------------------------- /client/src/components/Header/Notification.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components/macro"; 2 | import PropTypes from "prop-types"; 3 | 4 | const Aside = styled.aside` 5 | padding: 0.8rem; 6 | z-index: 10; 7 | color: var(--color-font-white); 8 | background-color: ${(props) => 9 | props.state ? "var(--color-state-error)" : "var(--color-state-default)"}; 10 | position: relative; 11 | display: grid; 12 | grid-template-columns: calc(2.4rem + 100px) 1fr; 13 | span { 14 | grid-column: 2/3; 15 | } 16 | animation: 10s ${(props) => (props.state ? "fadeInOut" : "none")} ease-in-out; 17 | animation-fill-mode: forward; 18 | opacity: ${(props) => (props.state ? "0" : "1")}; 19 | 20 | @keyframes fadeInOut { 21 | 0% { 22 | opacity: 1; 23 | transform: translateY(-100%); 24 | } 25 | 10% { 26 | transform: translateY(0); 27 | } 28 | 90% { 29 | transform: translateY(0); 30 | } 31 | 100% { 32 | transform: translateY(-100%); 33 | opacity: 1; 34 | } 35 | } 36 | `; 37 | 38 | export default function NotificationHeader({ 39 | error = false, 40 | message = "loading ...", 41 | }) { 42 | return ( 43 | 46 | ); 47 | } 48 | 49 | NotificationHeader.propTypes = { 50 | error: PropTypes.bool, 51 | message: PropTypes.string, 52 | }; 53 | -------------------------------------------------------------------------------- /lib/routes/documents/overviewDay.js: -------------------------------------------------------------------------------- 1 | const { Router } = require("express"); 2 | const { find } = require("../../api/database"); 3 | const { errorMessages } = require("../../api/errorCodes"); 4 | const { buildDailyOverview } = require("../../utils/convertToDailyOverview"); 5 | const { createPeriodOfTime } = require("../../utils/createPeriodOfTime"); 6 | const isValidToken = require("../../authentication/isValidToken"); 7 | 8 | const router = Router(); 9 | 10 | router.get("/:timestamp", isValidToken, async (request, response) => { 11 | try { 12 | const timestamp = Number(request.params.timestamp); 13 | 14 | if (!Number.isInteger(timestamp)) { 15 | response.status(400).json(errorMessages.timestamp[400]); 16 | return; 17 | } 18 | 19 | const { startPeriod, endPeriod, timeNow } = createPeriodOfTime(timestamp); 20 | 21 | const result = await find(process.env.DB_COLLECTION_EVENTS, { 22 | $and: [{ start: { $gte: startPeriod } }, { end: { $lte: endPeriod } }], 23 | }); 24 | 25 | if (!result.length) { 26 | response.status(404).json(errorMessages.timestamp[404]); 27 | return; 28 | } 29 | 30 | response.json({ code: 200, content: buildDailyOverview(result, timeNow) }); 31 | } catch (error) { 32 | response.status(500).json({ ...errorMessages.server, desc: error.message }); 33 | } 34 | }); 35 | 36 | module.exports = router; 37 | -------------------------------------------------------------------------------- /client/src/components/Header/MainMenu.stories.js: -------------------------------------------------------------------------------- 1 | import { Button, Ul, Nav } from "./MainMenu"; 2 | import styled from "styled-components/macro"; 3 | 4 | const PlaceHolder = styled.div` 5 | margin-bottom: 100px; 6 | `; 7 | 8 | export const Menu = (args) => { 9 | function animationState() { 10 | if (args.animation === "fade In") { 11 | return true; 12 | } 13 | if (args.animation === "fade Out") { 14 | return false; 15 | } 16 | } 17 | 18 | return ( 19 | <> 20 | 21 | 32 | 33 | ); 34 | }; 35 | 36 | export default { 37 | title: "Components/Header", 38 | components: Menu, 39 | argTypes: { 40 | animation: { 41 | control: { 42 | type: "radio", 43 | options: ["fade In", "fade Out"], 44 | }, 45 | table: { 46 | disable: false, 47 | }, 48 | }, 49 | date: { 50 | table: { 51 | disable: true, 52 | }, 53 | }, 54 | title: { 55 | table: { 56 | disable: true, 57 | }, 58 | }, 59 | }, 60 | }; 61 | -------------------------------------------------------------------------------- /client/src/components/datasheets/DataListItem.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import PropTypes from "prop-types"; 3 | 4 | const ListItem = styled.li` 5 | width: 100%; 6 | padding: 0.6rem 1rem 0.6rem 2rem; 7 | display: grid; 8 | grid-template-columns: 1.5fr 2fr; 9 | grid-template-rows: auto auto auto; 10 | grid-template-areas: 11 | "time title" 12 | "time description" 13 | "time room"; 14 | 15 | :not(:only-child):nth-child(2n + 1) { 16 | background-color: var(--color-bg-sub); 17 | } 18 | `; 19 | 20 | const Time = styled.h4` 21 | grid-area: time; 22 | 23 | &:after { 24 | content: " Uhr"; 25 | } 26 | `; 27 | 28 | const Titel = styled.h4` 29 | grid-area: title; 30 | margin: 0; 31 | `; 32 | 33 | const Description = styled.span` 34 | grid-area: description; 35 | `; 36 | 37 | const Room = styled.span` 38 | grid-area: room; 39 | `; 40 | 41 | export const DataListItem = ({ time, title, description, room, onClick }) => { 42 | return ( 43 | 44 | 45 | {title} 46 | {description} 47 | {room} 48 | 49 | ); 50 | }; 51 | 52 | DataListItem.propTypes = { 53 | time: PropTypes.string.isRequired, 54 | title: PropTypes.string.isRequired, 55 | description: PropTypes.string, 56 | room: PropTypes.string, 57 | onClick: PropTypes.func, 58 | }; 59 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "proxy": "http://localhost:6000", 6 | "dependencies": { 7 | "@storybook/addon-actions": "^6.1.1", 8 | "@storybook/addon-essentials": "^6.1.1", 9 | "@storybook/addon-links": "^6.1.1", 10 | "@storybook/node-logger": "^6.1.1", 11 | "@storybook/preset-create-react-app": "^3.1.5", 12 | "@storybook/react": "^6.1.1", 13 | "@testing-library/jest-dom": "^5.11.6", 14 | "@testing-library/react": "^11.2.1", 15 | "@testing-library/user-event": "^12.2.2", 16 | "babel-loader": "^8.1.0", 17 | "react": "^17.0.1", 18 | "react-dom": "^17.0.1", 19 | "react-is": "^17.0.1", 20 | "react-router-dom": "^5.2.0", 21 | "react-scripts": "4.0.0", 22 | "styled-components": "^5.2.1" 23 | }, 24 | "scripts": { 25 | "start": "react-scripts start", 26 | "build": "react-scripts build", 27 | "test:watch": "react-scripts test", 28 | "test": "react-scripts test --watchAll=false --passWithNoTests", 29 | "eject": "react-scripts eject", 30 | "storybook": "start-storybook -p 6006 -s public", 31 | "build-storybook": "build-storybook -s public" 32 | }, 33 | "browserslist": { 34 | "production": [ 35 | ">0.2%", 36 | "not dead", 37 | "not op_mini all" 38 | ], 39 | "development": [ 40 | "last 1 chrome version", 41 | "last 1 firefox version", 42 | "last 1 safari version" 43 | ] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /client/src/components/Header/HeaderBar.stories.js: -------------------------------------------------------------------------------- 1 | import logoSrc from "../../assets/logo/logo-artemis.png"; 2 | import { Article, Button, InformationContainer } from "."; 3 | 4 | export const HeaderBar = (args) => { 5 | const HeaderTitle = () => { 6 | if (!args.title) { 7 | return "Artemis"; 8 | } else { 9 | return args.title; 10 | } 11 | }; 12 | 13 | const ConvertDate = () => { 14 | if (!args.date) { 15 | return "01.01.2021"; 16 | } 17 | 18 | console.log(args.date); 19 | 20 | const date = new Date(args.date); 21 | return new Intl.DateTimeFormat("de-DE", { 22 | year: "numeric", 23 | month: "2-digit", 24 | day: "numeric", 25 | }).format(date); 26 | }; 27 | 28 | return ( 29 |
30 |
31 | 32 |

{ConvertDate()}

33 |

{HeaderTitle()}

34 |
35 | 38 |
39 |
40 | ); 41 | }; 42 | 43 | export default { 44 | title: "Components/Header", 45 | components: HeaderBar, 46 | argTypes: { 47 | animation: { 48 | table: { 49 | disable: true, 50 | }, 51 | }, 52 | date: { 53 | control: { 54 | type: "date", 55 | }, 56 | table: { 57 | disable: false, 58 | }, 59 | }, 60 | title: { 61 | control: { 62 | type: "text", 63 | }, 64 | table: { 65 | disable: false, 66 | }, 67 | }, 68 | }, 69 | }; 70 | -------------------------------------------------------------------------------- /client/src/utils/hook/useAsync.js: -------------------------------------------------------------------------------- 1 | import { useState, useCallback } from "react"; 2 | import { useUserContext } from "../context/Context"; 3 | import useAuth from "./useAuth"; 4 | 5 | export default function useAsync(action, params) { 6 | const [data, setData] = useState(null); 7 | const [metaData, setMetaData] = useState(null); 8 | const [isError, setIsError] = useState(false); 9 | const [loading, setLoading] = useState(false); 10 | const [message, setMessage] = useState("loading ..."); 11 | const { user } = useUserContext(); 12 | const { signOut } = useAuth(); 13 | 14 | const doFetch = useCallback(async () => { 15 | const errorHandler = (response) => { 16 | console.error(response.message); 17 | setIsError(true); 18 | !response.message 19 | ? setMessage("unknown Error") 20 | : setMessage(response.message); 21 | setTimeout(() => { 22 | setLoading(false); 23 | }, 6000); 24 | }; 25 | 26 | try { 27 | setLoading(true); 28 | const response = await action(params, user.auth_token); 29 | switch (response.code) { 30 | case 200: 31 | setData(response.content); 32 | setMetaData({ title: response.title, date: response.date }); 33 | setLoading(false); 34 | break; 35 | case 401: 36 | signOut(); 37 | break; 38 | case 400: 39 | case 404: 40 | case 500: 41 | default: 42 | errorHandler(response); 43 | } 44 | } catch (error) { 45 | errorHandler({ message: error.message }); 46 | } 47 | }, [user.auth_token, action, params, signOut]); 48 | 49 | return { data, loading, isError, message, metaData, doFetch }; 50 | } 51 | -------------------------------------------------------------------------------- /lib/authentication/isValidToken.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | const { errorMessages } = require("../api/errorCodes"); 3 | const { findOne } = require("../api/database"); 4 | const { ObjectID } = require("mongodb"); 5 | 6 | async function isValidToken(request, response, next) { 7 | try { 8 | const auth_token = request.headers.authorization; 9 | 10 | if (!auth_token) { 11 | response.status(401).json({ 12 | ...errorMessages.authorization[401], 13 | description: "no auth token submitted", 14 | }); 15 | return; 16 | } 17 | 18 | const { id, name } = jwt.verify(auth_token, process.env.TOKEN_SECRET); 19 | 20 | if (!id || !name) { 21 | response.status(401).json({ 22 | ...errorMessages.authorization[401], 23 | description: "id/name not included", 24 | }); 25 | return; 26 | } 27 | 28 | const result = await findOne(process.env.DB_COLLECTION_USER, { 29 | _id: new ObjectID.createFromHexString(id), 30 | }); 31 | 32 | if (!result) { 33 | response.status(404).json({ 34 | ...errorMessages.authorization[404], 35 | description: "no user identified", 36 | }); 37 | return; 38 | } 39 | 40 | if (result.auth_token !== auth_token) { 41 | response.status(401).json({ 42 | ...errorMessages.authorization[401], 43 | description: 44 | "authorization token can´t be verified with the stored userID", 45 | }); 46 | return; 47 | } 48 | 49 | next(); 50 | } catch (error) { 51 | response 52 | .status(500) 53 | .json({ ...errorMessages.server, decription: error.message }); 54 | } 55 | } 56 | 57 | module.exports = isValidToken; 58 | -------------------------------------------------------------------------------- /client/src/Pages/Today.stories.js: -------------------------------------------------------------------------------- 1 | import logoSrc from "../assets/logo/logo-artemis.png"; 2 | import { Article, Button, InformationContainer } from "../components/Header"; 3 | import styled from "styled-components/macro"; 4 | import { 5 | DataSheetSamples, 6 | EventSamples, 7 | } from "../components/datasheets/DataSamples"; 8 | import { DataListContainer } from "../components/datasheets/DataList"; 9 | import { EventListItem } from "../components/datasheets/EventListItem"; 10 | import { DataSheetItem } from "../components/datasheets/DataSheetItem"; 11 | import { DataHeader } from "../components/datasheets/DataHeader"; 12 | 13 | const Main = styled.main` 14 | width: 100%; 15 | padding-top: 200px; 16 | 17 | > * { 18 | width: 100%; 19 | } 20 | `; 21 | 22 | export const Today = () => { 23 | return ( 24 | <> 25 |
26 |
27 | 28 |

01.01.2021

29 |

Login

30 |
31 | 34 |
35 |
36 |
37 | 38 | Next to ToDo 39 | {DataSheetSamples.map((sample) => { 40 | return ; 41 | })} 42 | 43 | 44 | Events Today 45 | {EventSamples.map((sample) => { 46 | return ; 47 | })} 48 | 49 |
50 | 51 | ); 52 | }; 53 | 54 | export default { 55 | title: "Pages", 56 | components: Today, 57 | }; 58 | -------------------------------------------------------------------------------- /client/src/utils/context/Context.js: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useContext, useEffect, useState } from "react"; 2 | import PropTypes from "prop-types"; 3 | import { mockTimestamp } from "../helpers"; 4 | 5 | export const Context = React.createContext(null); 6 | 7 | const defaultUser = { 8 | id: null, 9 | name: null, 10 | auth_token: "", 11 | }; 12 | 13 | const defaultDate = { 14 | realDate: new Intl.DateTimeFormat("de-DE", { 15 | year: "numeric", 16 | month: "numeric", 17 | day: "numeric", 18 | }).format(new Date()), 19 | mockedDate: mockTimestamp(), 20 | }; 21 | 22 | export const UserData = ({ children }) => { 23 | const [user, setUser] = useState(null); 24 | 25 | useEffect(() => { 26 | const getUserCredentials = async () => { 27 | try { 28 | const response = await fetch(`/api/user/refresh`); 29 | if (response.ok) { 30 | const data = await response.json(); 31 | setUser({ ...data.user, ...defaultDate }); 32 | } 33 | if (!response.ok) { 34 | setUser({ ...defaultUser, ...defaultDate }); 35 | } 36 | } catch (error) { 37 | console.error(error.message); 38 | } 39 | }; 40 | getUserCredentials(); 41 | }, []); 42 | 43 | const logoutUser = () => setUser(defaultUser); 44 | 45 | const loginUser = useCallback((newUser) => { 46 | setUser({ ...defaultUser, ...newUser }); 47 | }, []); 48 | 49 | return ( 50 | 51 | {children} 52 | 53 | ); 54 | }; 55 | 56 | UserData.propTypes = { 57 | children: PropTypes.node.isRequired, 58 | }; 59 | 60 | export const useUserContext = () => useContext(Context); 61 | 62 | export const useAuth_Token = () => { 63 | const { user } = useUserContext(); 64 | return user.auth_token; 65 | }; 66 | -------------------------------------------------------------------------------- /client/src/Pages/Login.js: -------------------------------------------------------------------------------- 1 | import Header from "../components/Header"; 2 | import { Main } from "../components/helper/Main"; 3 | import { LoginButton, LoginInput, LoginForm } from "../components/Login"; 4 | import useAuth from "../utils/hook/useAuth"; 5 | 6 | const defaultHeader = { 7 | title: "Login", 8 | date: new Intl.DateTimeFormat("de-DE", { 9 | year: "numeric", 10 | month: "numeric", 11 | day: "numeric", 12 | }).format(new Date()), 13 | loading: false, 14 | isError: false, 15 | message: null, 16 | }; 17 | 18 | function Login() { 19 | const { 20 | signIn, 21 | loading, 22 | isError, 23 | message, 24 | formState, 25 | credentials, 26 | handleID, 27 | handlePassword, 28 | } = useAuth(); 29 | 30 | const handleSubmit = async (event) => { 31 | event.preventDefault(); 32 | signIn(); 33 | }; 34 | 35 | return ( 36 | <> 37 |
43 |
44 | 45 | handleID(event.target.value)} 51 | /> 52 | handlePassword(event.target.value)} 58 | /> 59 | 60 | Login 61 | 62 | 63 |
64 | 65 | ); 66 | } 67 | 68 | export default Login; 69 | -------------------------------------------------------------------------------- /lib/routes/user/refresh.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | const { Router } = require("express"); 3 | const { findOne } = require("../../api/database"); 4 | const { errorMessages } = require("../../api/errorCodes"); 5 | const { ObjectID } = require("mongodb"); 6 | const router = Router(); 7 | 8 | router.get("/", async (request, response) => { 9 | try { 10 | const { auth_token } = request.cookies; 11 | 12 | if (!auth_token) { 13 | response.status(401).json({ 14 | ...errorMessages.authorization[401], 15 | description: "no auth token submitted", 16 | }); 17 | return; 18 | } 19 | 20 | const { id, name } = jwt.verify(auth_token, process.env.TOKEN_SECRET); 21 | 22 | if (!id || !name) { 23 | response.status(401).json({ 24 | ...errorMessages.authorization[401], 25 | description: "id/name not included", 26 | }); 27 | return; 28 | } 29 | 30 | const result = await findOne(process.env.DB_COLLECTION_USER, { 31 | _id: new ObjectID.createFromHexString(id), 32 | }); 33 | 34 | if (!result) { 35 | response.status(404).json(errorMessages.authorization[404]); 36 | return; 37 | } 38 | 39 | if (result.auth_token !== auth_token) { 40 | response.status(401).json({ 41 | ...errorMessages.authorization[401], 42 | description: "submitted jwt doesnt fit the stored one", 43 | }); 44 | return; 45 | } 46 | 47 | const feedback = { 48 | code: 200, 49 | message: "refresh auth_token", 50 | user: { 51 | id: result._id, 52 | name: result.name, 53 | auth_token: result.auth_token, 54 | }, 55 | }; 56 | 57 | response.status(200).json(feedback); 58 | } catch (error) { 59 | response.status(500).json({ ...errorMessages.server, desc: error.message }); 60 | } 61 | }); 62 | 63 | module.exports = router; 64 | -------------------------------------------------------------------------------- /client/src/components/datasheets/DataSheetItem.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import PropTypes from "prop-types"; 3 | 4 | const ItemContainer = styled.li` 5 | width: 100%; 6 | padding: 0.6rem 1rem 0.6rem 2rem; 7 | display: grid; 8 | column-gap: 0.5rem; 9 | grid-template-columns: 2fr 3fr; 10 | grid-template-rows: repeat(5, auto); 11 | grid-template-areas: 12 | "time title" 13 | "time room" 14 | "time pax" 15 | "time setup" 16 | "notes notes"; 17 | 18 | &:not(:only-child):nth-child(2n + 1) { 19 | background-color: var(--color-bg-sub); 20 | } 21 | `; 22 | 23 | const Time = styled.h4` 24 | grid-area: time; 25 | 26 | &:after { 27 | content: " Uhr"; 28 | } 29 | `; 30 | 31 | const Title = styled.h4` 32 | grid-area: title; 33 | `; 34 | 35 | const Room = styled.span` 36 | grid-area: room; 37 | `; 38 | const Setup = styled.span` 39 | grid-area: setup; 40 | margin-bottom: 1rem; 41 | `; 42 | const Pax = styled.span` 43 | grid-area: pax; 44 | `; 45 | 46 | const NotesContainer = styled.ul` 47 | grid-area: notes; 48 | list-style: none; 49 | `; 50 | 51 | export const DataSheetItem = ({ time, title, room, notes, pax, setup }) => { 52 | return ( 53 | 54 | 55 | {title} 56 | {room && {room}} 57 | {setup && {setup}} 58 | {pax && {pax} pax} 59 | 60 | {notes && 61 | notes.map((note) => { 62 | return
  • {note}
  • ; 63 | })} 64 |
    65 |
    66 | ); 67 | }; 68 | 69 | DataSheetItem.propTypes = { 70 | time: PropTypes.string.isRequired, 71 | title: PropTypes.string.isRequired, 72 | room: PropTypes.string, 73 | notes: PropTypes.arrayOf(PropTypes.string), 74 | setup: PropTypes.string, 75 | pax: PropTypes.number, 76 | }; 77 | -------------------------------------------------------------------------------- /lib/routes/user/login.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | const { Router } = require("express"); 3 | const { findOne, modifyUser } = require("../../api/database"); 4 | const { errorMessages } = require("../../api/errorCodes"); 5 | const isValidHeader = require("../../authentication/isValidHeader"); 6 | const comparePasswords = require("../../authentication/crypto"); 7 | 8 | const router = Router(); 9 | const EXPIRATION_DATE = 1000 * 60 * 60 * 24; 10 | 11 | router.post("/", isValidHeader, async (request, response) => { 12 | try { 13 | const { id, password } = request.body; 14 | 15 | const result = await findOne(process.env.DB_COLLECTION_USER, { 16 | $or: [{ employeeID: id }, { name: id }, { email: id }], 17 | }); 18 | 19 | if (!result) { 20 | response.status(404).json(errorMessages.authorization[404]); 21 | return; 22 | } 23 | 24 | if (!(await comparePasswords(result.password, password))) { 25 | response.status(401).json({ 26 | ...errorMessages.authorization[401], 27 | desc: "submitted password is not correct", 28 | }); 29 | return; 30 | } 31 | 32 | const user = { 33 | id: result._id, 34 | name: result.name, 35 | }; 36 | 37 | const auth_token = jwt.sign(user, process.env.TOKEN_SECRET, { 38 | expiresIn: `${EXPIRATION_DATE}s`, 39 | }); 40 | 41 | await modifyUser(process.env.DB_COLLECTION_USER, result._id, auth_token); 42 | 43 | const auth_response = { 44 | code: 200, 45 | message: "validation successful", 46 | user: { ...user, auth_token }, 47 | }; 48 | 49 | // SET to unsecure to test it on localhost 50 | // response.cookie("auth_token", `${auth_token}`, { 51 | // path: "/", 52 | // maxAge: EXPIRATION_DATE, 53 | // httpOnly: true, 54 | // secure: true, 55 | // sameSite: true, 56 | // }); 57 | 58 | response.cookie("auth_token", `${auth_token}`, { 59 | path: "/", 60 | maxAge: EXPIRATION_DATE, 61 | httpOnly: true, 62 | sameSite: true, 63 | }); 64 | 65 | response.json(auth_response); 66 | } catch (error) { 67 | response.status(500).json({ ...errorMessages.server, desc: error.message }); 68 | } 69 | }); 70 | 71 | module.exports = router; 72 | -------------------------------------------------------------------------------- /lib/utils/convertToDailyOverview.js: -------------------------------------------------------------------------------- 1 | const { createTime } = require("./convertTimestamp"); 2 | const { sortEventsByTime } = require("./sortEventsByTime"); 3 | 4 | const DELAY = 21600; 5 | 6 | const createContentNextUp = (documents, timestamp) => { 7 | const content = documents.reduce((previous, document) => { 8 | const eventsInPeriodOfTime = [...document.rooms, ...document.event].filter( 9 | (event) => event.start < timestamp || event.start > timestamp + DELAY 10 | ); 11 | 12 | const events = eventsInPeriodOfTime.map((event) => { 13 | return { 14 | id: document._id, 15 | time: createTime(event), 16 | title: event.title, 17 | room: event.location, 18 | description: document.title, 19 | }; 20 | }); 21 | 22 | return [...previous, ...events]; 23 | }, []); 24 | 25 | return sortEventsByTime(content); 26 | }; 27 | 28 | const createContentEventsToday = (documents) => { 29 | const content = documents.reduce((previous, document) => { 30 | const defaultRoom = document.rooms.find((room) => room.default); 31 | const info = { 32 | id: document._id, 33 | time: createTime(document), 34 | title: document.title, 35 | room: defaultRoom.location, 36 | setup: defaultRoom.setup, 37 | pax: defaultRoom.pax, 38 | pinboard: defaultRoom.equipment.pinnboard, 39 | flipchart: defaultRoom.equipment.flipchart, 40 | }; 41 | 42 | return [...previous, info]; 43 | }, []); 44 | 45 | content.sort((firstElement, secondElement) => { 46 | if (firstElement.time > secondElement.time) { 47 | return 1; 48 | } 49 | if (firstElement.time < secondElement.time) { 50 | return -1; 51 | } 52 | 53 | return 0; 54 | }); 55 | 56 | return sortEventsByTime(content); 57 | }; 58 | 59 | const buildDailyOverview = (documents, timestamp) => { 60 | const nextUp = { 61 | title: "Next Up", 62 | list: true, 63 | content: createContentNextUp(documents, timestamp), 64 | }; 65 | 66 | const eventsToday = { 67 | title: "Events today", 68 | list: false, 69 | content: createContentEventsToday(documents), 70 | }; 71 | 72 | return [nextUp, eventsToday]; 73 | }; 74 | 75 | exports.buildDailyOverview = buildDailyOverview; 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | 107 | # own stuff 108 | 109 | client/build 110 | client/storybook-static 111 | cert/ 112 | -------------------------------------------------------------------------------- /client/src/assets/data_samples/eventData.js: -------------------------------------------------------------------------------- 1 | const args = { 2 | arrowleft: false, 3 | arrowright: true, 4 | filter: true, 5 | }; 6 | 7 | const sheduleSamples = [ 8 | { 9 | time: "10:00 - 16:00", 10 | title: "Meetingroom", 11 | room: "Raum 123", 12 | }, 13 | { 14 | time: "11:00 - 11:30", 15 | title: "Kaffeepause 1", 16 | room: "Lounge", 17 | }, 18 | { 19 | time: "13:00 - 14:00", 20 | title: "Mittagessen", 21 | room: "Restaurant", 22 | }, 23 | { 24 | time: "15:00 - 16:00", 25 | title: "Kaffeepause 2", 26 | room: "Lounge", 27 | }, 28 | ]; 29 | 30 | const setupSamples = [ 31 | { 32 | time: "10:00 - 16:00", 33 | title: "Meetingroom", 34 | room: "Raum 123", 35 | notes: ["Tafel 20 Personen", "Referententisch", "Seitenbufett"], 36 | }, 37 | ]; 38 | const technicSamples = [ 39 | { 40 | time: "10:00 - 16:00", 41 | title: "Meetingroom", 42 | room: "Raum 123", 43 | notes: ["3 Flipcharts", "1 Pinnwand", "1 Beamer"], 44 | }, 45 | ]; 46 | const houseSamples = [ 47 | { 48 | time: "10:00 - 16:00", 49 | title: "Meetingroom", 50 | room: "Raum 123", 51 | }, 52 | ]; 53 | const signSamples = [ 54 | { 55 | time: "10:00 - 16:00", 56 | title: `Telekom „Future Treff“`, 57 | }, 58 | ]; 59 | 60 | const fbSamples = [ 61 | { 62 | time: "10:00 - 16:00", 63 | title: "Meetingroom", 64 | room: "Raum 123", 65 | notes: ["1 gr. Flasche Wasser + 2 kl. Flaschen Saft"], 66 | }, 67 | { 68 | time: "11:00 - 11:30", 69 | title: "Kaffeepause", 70 | room: "Lounge", 71 | notes: ["Kaffee/Tee", "Orangensaft", "Belegte Brezeln"], 72 | }, 73 | { 74 | time: "13:00 - 14:00", 75 | title: "Mittagessen", 76 | room: "Restaurant", 77 | notes: [ 78 | "1 Glas Wein/ Bier pro Person", 79 | "1 Flasche Wasser / Person", 80 | "Tagesmenu", 81 | ], 82 | }, 83 | { 84 | time: "15:00 - 16:00", 85 | title: "Mittagessen", 86 | room: "Restaurant", 87 | notes: ["Kaffee/ Tee", "1 stück Kuchen pro Person"], 88 | }, 89 | ]; 90 | 91 | const mockup = { 92 | args, 93 | sheduleSamples, 94 | setupSamples, 95 | houseSamples, 96 | fbSamples, 97 | signSamples, 98 | technicSamples, 99 | }; 100 | -------------------------------------------------------------------------------- /client/src/Pages/Event.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useHistory, useParams } from "react-router-dom"; 3 | import { 4 | SectionContainer, 5 | DataHeader, 6 | DataListContainer, 7 | DataSheetItem, 8 | DataListItem, 9 | } from "../components/datasheets/"; 10 | import useAsync from "../utils/hook/useAsync"; 11 | import { event } from "../utils/api"; 12 | import { Main } from "../components/helper/Main"; 13 | import ErrorHandler from "./Error"; 14 | import Header from "../components/Header"; 15 | import MissingData from "../components/helper/missingData"; 16 | 17 | const defaultHeader = { 18 | title: "Event Overview", 19 | date: new Intl.DateTimeFormat("de-DE", { 20 | year: "numeric", 21 | month: "numeric", 22 | day: "numeric", 23 | }).format(new Date()), 24 | loading: false, 25 | isError: false, 26 | message: null, 27 | }; 28 | 29 | function Event() { 30 | const { eventID } = useParams(); 31 | const history = useHistory(); 32 | const { data, doFetch, loading, isError, message, metaData } = useAsync( 33 | event, 34 | eventID 35 | ); 36 | 37 | useEffect(() => { 38 | doFetch(); 39 | defaultHeader.title = metaData?.title; 40 | }, [doFetch, metaData?.title]); 41 | 42 | return ( 43 | <> 44 |
    50 |
    51 | {isError && } 52 | {data?.map((item) => { 53 | const ListItem = item.list ? DataListItem : DataSheetItem; 54 | return ( 55 | 56 | {item.title} 57 | {!item.content.length ? ( 58 | 59 | ) : ( 60 | 61 | {item.content.map((content, index) => ( 62 | history.push(`/event/${content.id}`)} 66 | /> 67 | ))} 68 | 69 | )} 70 | 71 | ); 72 | })} 73 |
    74 | 75 | ); 76 | } 77 | 78 | export default Event; 79 | -------------------------------------------------------------------------------- /client/src/Pages/Today.js: -------------------------------------------------------------------------------- 1 | import { 2 | SectionContainer, 3 | DataHeader, 4 | DataListContainer, 5 | DataListItem, 6 | EventListItem, 7 | } from "../components/datasheets/"; 8 | import { useEffect } from "react"; 9 | import { useHistory, useParams } from "react-router-dom"; 10 | import MissingData from "../components/helper/missingData"; 11 | import { daily } from "../utils/api"; 12 | import { mockTimestamp } from "../utils/helpers/date"; 13 | import useAsync from "../utils/hook/useAsync"; 14 | import Header from "../components/Header"; 15 | import { Main } from "../components/helper/Main"; 16 | import ErrorHandler from "./Error"; 17 | 18 | const dateToday = mockTimestamp(); 19 | const defaultHeader = { 20 | title: "Daily Overview", 21 | date: new Intl.DateTimeFormat("de-DE", { 22 | year: "numeric", 23 | month: "numeric", 24 | day: "numeric", 25 | }).format(new Date()), 26 | loading: false, 27 | isError: false, 28 | message: null, 29 | }; 30 | 31 | function Today() { 32 | const { timestamp } = useParams(); 33 | 34 | const params = timestamp ? timestamp : dateToday; 35 | 36 | const { data, doFetch, loading, isError, message } = useAsync(daily, params); 37 | const history = useHistory(); 38 | 39 | useEffect(() => { 40 | doFetch(); 41 | }, [doFetch]); 42 | 43 | return ( 44 | <> 45 |
    51 |
    52 | {isError && } 53 | {data?.map((item) => { 54 | const ListItem = item.list ? DataListItem : EventListItem; 55 | return ( 56 | 57 | {item.title} 58 | {!item.content.length ? ( 59 | 60 | ) : ( 61 | 62 | {item.content.map((content, index) => ( 63 | history.push(`/event/${content.id}`)} 67 | /> 68 | ))} 69 | 70 | )} 71 | 72 | ); 73 | })} 74 |
    75 | 76 | ); 77 | } 78 | 79 | export default Today; 80 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "artemis", 3 | "version": "0.0.1", 4 | "private": true, 5 | "description": "Dashboard & digital Function Sheet replacement", 6 | "main": "server.js", 7 | "scripts": { 8 | "postinstall": "cd client && npm install", 9 | "build": "cd client && npm run build && npm run build-storybook", 10 | "test:watch": "cd client && npm run test:watch", 11 | "test": "npm run lint && npm run prettier && cd client && npm test", 12 | "lint": "eslint . --ext .js", 13 | "prettify": "prettier --write .", 14 | "dev": "concurrently \"npm run server\" \"npm run client\"", 15 | "client": "cd client && npm start", 16 | "prettier": "prettier --check \"**/*.{js,jsx,ts,tsx,md,mdx,html,css,json}\"", 17 | "server": "nodemon server.js", 18 | "storybook": "cd client && npm run storybook", 19 | "start": "node server.js" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/AlexisRoe/Artemis.git" 24 | }, 25 | "keywords": [ 26 | "dashboard", 27 | "function", 28 | "sheet", 29 | "react", 30 | "mongodb" 31 | ], 32 | "author": "Alexis Roehrling", 33 | "license": "MIT", 34 | "bugs": { 35 | "url": "https://github.com/AlexisRoe/Artemis/issues" 36 | }, 37 | "homepage": "https://github.com/AlexisRoe/Artemis#readme", 38 | "devDependencies": { 39 | "@typescript-eslint/eslint-plugin": "^4.8.1", 40 | "@typescript-eslint/parser": "^4.8.1", 41 | "babel-eslint": "^10.1.0", 42 | "concurrently": "^5.3.0", 43 | "eslint": "^7.13.0", 44 | "eslint-config-prettier": "^6.15.0", 45 | "eslint-config-react-app": "^6.0.0", 46 | "eslint-plugin-flowtype": "^5.2.0", 47 | "eslint-plugin-import": "^2.22.1", 48 | "eslint-plugin-jsx-a11y": "^6.4.1", 49 | "eslint-plugin-react": "^7.21.5", 50 | "eslint-plugin-react-hooks": "^4.2.0", 51 | "husky": "^4.3.0", 52 | "inquirer": "^7.3.3", 53 | "lint-staged": "^10.5.1", 54 | "nodemon": "^2.0.6", 55 | "prettier": "2.1.2", 56 | "typescript": "^4.0.5" 57 | }, 58 | "husky": { 59 | "hooks": { 60 | "pre-commit": "lint-staged", 61 | "pre-push": "npm test" 62 | } 63 | }, 64 | "lint-staged": { 65 | "*.js": "eslint --cache --fix", 66 | "*.{js,css,md}": "prettier --write" 67 | }, 68 | "dependencies": { 69 | "bcrypt": "^5.0.0", 70 | "cookie-parser": "^1.4.5", 71 | "dotenv": "^8.2.0", 72 | "express": "^4.17.1", 73 | "jsonwebtoken": "^8.5.1", 74 | "mongodb": "^3.6.3" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /client/src/components/Header/MainMenu.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components/macro"; 2 | import PropTypes from "prop-types"; 3 | import { useHistory } from "react-router-dom"; 4 | 5 | export const Nav = styled.nav` 6 | background-color: var(--color-bg-dark); 7 | padding: 50px 0px; 8 | padding-left: 20vw; 9 | position: relative; 10 | z-index: 20; 11 | animation: 1s ${(props) => (props.state ? "fadeIn" : "fadeOut")} ease-in-out; 12 | animation-fill-mode: forwards; 13 | 14 | @keyframes fadeIn { 15 | 0% { 16 | opacity: 0; 17 | transform: translateY(-100%); 18 | } 19 | 100% { 20 | opacity: 1; 21 | } 22 | } 23 | 24 | @keyframes fadeOut { 25 | 0% { 26 | opacity: 1; 27 | } 28 | 100% { 29 | opacity: 0; 30 | transform: translateY(-100%); 31 | } 32 | } 33 | 34 | :before { 35 | content: ""; 36 | position: absolute; 37 | border-style: solid; 38 | border-width: 0 25px 25px; 39 | border-color: var(--color-bg-dark) transparent; 40 | width: 0; 41 | top: -22px; 42 | left: 45px; 43 | z-index: 1; 44 | } 45 | `; 46 | 47 | export const Ul = styled.ul` 48 | list-style-type: none; 49 | padding-left: 0; 50 | a, 51 | a:hover, 52 | a:focus, 53 | a:visited { 54 | text-decoration: none; 55 | color: var(--color-font-white); 56 | } 57 | 58 | li { 59 | margin-bottom: 1rem; 60 | color: var(--color-font-disabled); 61 | cursor: default; 62 | } 63 | 64 | .link { 65 | color: var(--color-font-white) !important; 66 | cursor: pointer; 67 | } 68 | `; 69 | 70 | export const Button = styled.button` 71 | margin-top: 2rem; 72 | cursor: pointer; 73 | background: none; 74 | border: none; 75 | outline: none; 76 | color: var(--color-golden); 77 | font-weight: 600; 78 | font-size: 20px; 79 | `; 80 | 81 | export default function MainMenu({ show, onClick, onLogout }) { 82 | const history = useHistory(); 83 | 84 | function handleLink(target) { 85 | onClick(); 86 | setTimeout(() => { 87 | history.push(target); 88 | }, 1000); 89 | } 90 | 91 | return ( 92 | 105 | ); 106 | } 107 | 108 | MainMenu.propTypes = { 109 | show: PropTypes.bool, 110 | onClick: PropTypes.func, 111 | onLogout: PropTypes.func, 112 | }; 113 | -------------------------------------------------------------------------------- /lib/utils/convertToFunctionSheet.js: -------------------------------------------------------------------------------- 1 | const { createTime, convertDate } = require("./convertTimestamp"); 2 | const { sortEventsByTime } = require("./sortEventsByTime"); 3 | 4 | const desc = { 5 | iconClassRoom: "Klassenraum", 6 | iconUForm: "U-Form", 7 | iconBoard: "Tafel", 8 | iconBanquet: "Bankett", 9 | iconChairCircle: "Stuhlkreis", 10 | }; 11 | 12 | const translateSetup = (iconName) => desc[iconName]; 13 | 14 | const createBasicInfo = (array, key) => { 15 | return array.map((element) => ({ 16 | time: createTime(element), 17 | title: element[key], 18 | room: element.location, 19 | })); 20 | }; 21 | 22 | const createSetup = (basicInfoRooms, rooms) => { 23 | return rooms.map((room, index) => ({ 24 | ...basicInfoRooms[index], 25 | notes: room.notes, 26 | setup: translateSetup(room.setup), 27 | pax: room.pax, 28 | })); 29 | }; 30 | 31 | const createFacilityManagement = (basicInfoRooms, rooms) => { 32 | return rooms.map((room, index) => ({ 33 | ...basicInfoRooms[index], 34 | notes: [ 35 | `${room.equipment.flipchart} Flipchart`, 36 | `${room.equipment.pinnboard} Pinnwände`, 37 | `${room.equipment.beamer} Beamer`, 38 | ], 39 | })); 40 | }; 41 | 42 | const createCatering = (basicInfoEvents, events) => { 43 | return events.map((event, index) => ({ 44 | ...basicInfoEvents[index], 45 | notes: [...event.foods, ...event.beverages], 46 | })); 47 | }; 48 | 49 | function createFunctionSheet(document) { 50 | const basicInfoRooms = createBasicInfo(document.rooms, "role"); 51 | const basicInfoEvents = createBasicInfo(document.event, "title"); 52 | 53 | const functionSheet = [ 54 | { 55 | title: "Shedule", 56 | list: true, 57 | content: sortEventsByTime([...basicInfoRooms, ...basicInfoEvents]), 58 | }, 59 | { 60 | title: "Setup", 61 | list: false, 62 | content: createSetup(basicInfoRooms, document.rooms), 63 | }, 64 | { 65 | title: "Haustechnik", 66 | list: false, 67 | content: createFacilityManagement(basicInfoRooms, document.rooms), 68 | }, 69 | { 70 | title: "Housekeeping", 71 | list: false, 72 | content: basicInfoRooms, 73 | }, 74 | { 75 | title: "Sign on Board", 76 | list: false, 77 | content: [ 78 | { 79 | time: createTime(document), 80 | title: document.sign, 81 | }, 82 | ], 83 | }, 84 | { 85 | title: "Food & Beverages", 86 | list: false, 87 | content: createCatering(basicInfoEvents, document.event), 88 | }, 89 | ]; 90 | 91 | return { 92 | title: document.title, 93 | date: convertDate(document.start), 94 | content: functionSheet, 95 | }; 96 | } 97 | 98 | exports.createFunctionSheet = createFunctionSheet; 99 | -------------------------------------------------------------------------------- /client/src/assets/logo/logo-Artemis.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/GlobalStyle.js: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from "styled-components"; 2 | import Poppins500 from "./assets/fonts/Poppins/poppins-v15-latin-500.woff2"; 3 | import Poppins600 from "./assets/fonts/Poppins/poppins-v15-latin-600.woff2"; 4 | import Poppins700 from "./assets/fonts/Poppins/poppins-v15-latin-700.woff2"; 5 | import OpenSans600 from "./assets/fonts/Open_Sans/open-sans-v18-latin-600.woff2"; 6 | import OpenSans700 from "./assets/fonts/Open_Sans/open-sans-v18-latin-700.woff2"; 7 | 8 | const GlobalStyle = createGlobalStyle` 9 | 10 | @font-face { 11 | font-family: "Poppins"; 12 | font-style: swap; 13 | font-weight: 500; 14 | src: url(${Poppins500}); 15 | } 16 | @font-face { 17 | font-family: "Poppins"; 18 | font-style: swap; 19 | font-weight: 600; 20 | src: url(${Poppins600}); 21 | } 22 | @font-face { 23 | font-family: "Poppins"; 24 | font-style: swap; 25 | font-weight: 700; 26 | src: url(${Poppins700}); 27 | } 28 | @font-face { 29 | font-family: "Open Sans"; 30 | font-style: swap; 31 | font-weight: 600; 32 | src: url(${OpenSans600}); 33 | } 34 | @font-face { 35 | font-family: "Open Sans"; 36 | font-style: swap; 37 | font-weight: 700; 38 | src: url(${OpenSans700}); 39 | } 40 | 41 | :root { 42 | --color-bg-white: #fff; 43 | --color-bg-dark: #2d2c2a; 44 | --color-bg-sub: rgba(182, 158, 79, .24); 45 | --color-golden: #b69e4f; 46 | --color-alarm: #c32020; 47 | --color-golden-dark: #9f8641; 48 | 49 | --color-state-default: rgb(208, 179, 86); 50 | --color-state-error: rgb(182, 99,79); 51 | --color-highlight-bg: rgba(182,158, 79, 13); 52 | 53 | --animation-curve: cubic-bezier(0.6, 0.04, 0.98, 0.335); 54 | 55 | --color-font-white: #fff; 56 | --color-font-sub: #8b8585; 57 | --color-font-disabled: #737373; 58 | --font-body-copy: #54534a; 59 | --font-family-header: "Poppins", sans-serif; 60 | --font-family-body: "Open Sans", sans-serif; 61 | } 62 | 63 | html { 64 | height: 100%; 65 | } 66 | 67 | body { 68 | height: 100%; 69 | font-size: 16px; 70 | font-family: var(--font-family-body); 71 | font-weight: 500; 72 | color: var(--font-body-copy); 73 | display: grid; 74 | grid-template-rows: 150px 1fr; 75 | } 76 | 77 | header { 78 | position: fixed; 79 | width: 100%; 80 | z-index: 100; 81 | grid-row: 1/2; 82 | } 83 | 84 | main { 85 | grid-row: 2/3; 86 | } 87 | 88 | h1, h2 { 89 | font-family: var(--font-family-header); 90 | text-transform: uppercase; 91 | } 92 | 93 | h1 { 94 | font-weight:600; 95 | font-size: 24px; 96 | letter-spacing: 1rem; 97 | } 98 | 99 | h2 { 100 | font-weight:600; 101 | font-size:20px; 102 | } 103 | 104 | 105 | h3 { 106 | font-weight:700; 107 | font-size:20px; 108 | } 109 | 110 | h4 { 111 | font-weight:700; 112 | font-size:16px; 113 | } 114 | 115 | button, button:hover, button:focus { 116 | outline:none; 117 | } 118 | 119 | `; 120 | 121 | export default GlobalStyle; 122 | -------------------------------------------------------------------------------- /client/src/components/Header/index.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components/macro"; 2 | import PropTypes from "prop-types"; 3 | import { useState } from "react"; 4 | import NotificationHeader from "./Notification"; 5 | import MainMenu from "./MainMenu"; 6 | import logoSrc from "../../assets/logo/logo-artemis.png"; 7 | import useAuth from "../../utils/hook/useAuth"; 8 | 9 | export const Article = styled.article` 10 | z-index: 30; 11 | min-width: 375px; 12 | min-height: 100px; 13 | padding: 1.2rem; 14 | background-color: var(--color-bg-dark); 15 | color: var(--color-font-white); 16 | display: grid; 17 | grid-template-columns: 100px 1fr; 18 | gap: 2rem; 19 | position: relative; 20 | 21 | :not(aside) + nav { 22 | transform: translateY(3rem); 23 | } 24 | `; 25 | 26 | export const Button = styled.button` 27 | border: 0.5rem solid var(--color-bg-dark); 28 | outline: none; 29 | z-index: 10; 30 | border-radius: 50%; 31 | background-color: var(--color-bg-dark); 32 | width: 100px; 33 | height: 100px; 34 | grid-column: 1/2; 35 | position: absolute; 36 | bottom: -25%; 37 | 38 | img { 39 | width: 100%; 40 | height: auto; 41 | cursor: pointer; 42 | } 43 | `; 44 | 45 | export const InformationContainer = styled.div` 46 | grid-column: 2/3; 47 | display: flex; 48 | flex-direction: column; 49 | align-items: start; 50 | justify-content: space-between; 51 | 52 | h2 { 53 | font-family: var(--font-family-body); 54 | text-transform: none; 55 | margin: 0; 56 | } 57 | `; 58 | 59 | export default function Header({ 60 | settings, 61 | showNotification, 62 | isError, 63 | message, 64 | }) { 65 | const [loadingMenu, setLoadingMenu] = useState(false); 66 | const [toggleAnimationMenu, setToggleAnimationMenu] = useState(false); 67 | const { signOut } = useAuth(); 68 | 69 | function hideMainMenu() { 70 | setToggleAnimationMenu(!toggleAnimationMenu); 71 | setTimeout(() => { 72 | setLoadingMenu(!loadingMenu); 73 | }, 1000); 74 | } 75 | 76 | function menuSwitch() { 77 | if (!loadingMenu) { 78 | setToggleAnimationMenu(!toggleAnimationMenu); 79 | setLoadingMenu(!loadingMenu); 80 | } else { 81 | hideMainMenu(); 82 | } 83 | } 84 | 85 | function handleLogout() { 86 | hideMainMenu(); 87 | setTimeout(() => { 88 | signOut(); 89 | }, 1100); 90 | } 91 | 92 | return ( 93 |
    94 |
    95 | 96 |

    {settings?.date}

    97 |

    {settings?.title}

    98 |
    99 | 102 |
    103 | {showNotification && ( 104 | 105 | )} 106 | {loadingMenu && ( 107 | 112 | )} 113 |
    114 | ); 115 | } 116 | 117 | Header.propTypes = { 118 | settings: PropTypes.object, 119 | showNotification: PropTypes.bool, 120 | isError: PropTypes.bool, 121 | message: PropTypes.string, 122 | }; 123 | -------------------------------------------------------------------------------- /client/src/utils/hook/useAuth.js: -------------------------------------------------------------------------------- 1 | import { useCallback, useState } from "react"; 2 | import { useHistory } from "react-router-dom"; 3 | import { useUserContext } from "../context/Context"; 4 | 5 | export default function useAuth() { 6 | const history = useHistory(); 7 | const { logoutUser, loginUser } = useUserContext(); 8 | const [formState, setFormState] = useState(false); 9 | const [credentials, setCredentials] = useState({ id: "", password: "" }); 10 | const [isError, setIsError] = useState(false); 11 | const [loading, setLoading] = useState(false); 12 | const [message, setMessage] = useState("loading ..."); 13 | 14 | const changeUserCredentials = (key) => (value) => { 15 | setCredentials({ ...credentials, [key]: value }); 16 | }; 17 | const handleID = changeUserCredentials("id"); 18 | const handlePassword = changeUserCredentials("password"); 19 | 20 | const resetCredentials = useCallback(() => { 21 | handleID(""); 22 | handlePassword(""); 23 | }, [handleID, handlePassword]); 24 | 25 | const errorHandler = useCallback( 26 | (data) => { 27 | console.error(data.message); 28 | setIsError(true); 29 | switch (data.code) { 30 | case 401: 31 | setMessage(data.desc); 32 | setFormState(true); 33 | break; 34 | case 404: 35 | setMessage(data.message); 36 | setFormState(true); 37 | break; 38 | default: 39 | setMessage(data.message); 40 | setFormState(false); 41 | } 42 | setTimeout(() => { 43 | setLoading(false); 44 | setIsError(false); 45 | setLoading(false); 46 | setMessage("loading ..."); 47 | resetCredentials(); 48 | }, 6000); 49 | }, 50 | [resetCredentials] 51 | ); 52 | 53 | // TODO: remove credentials, to avoid javascript access to cookie 54 | 55 | const signIn = useCallback(async () => { 56 | const options = { 57 | method: "POST", 58 | body: JSON.stringify({ 59 | id: credentials.id, 60 | password: credentials.password, 61 | }), 62 | headers: { 63 | "Content-Type": "application/json", 64 | }, 65 | }; 66 | try { 67 | setLoading(true); 68 | const response = await fetch(`/api/user/login`, options); 69 | const data = await response.json(); 70 | switch (data.code) { 71 | case 200: 72 | loginUser(data.user); 73 | history.push("/"); 74 | break; 75 | default: 76 | errorHandler(data); 77 | } 78 | } catch (error) { 79 | errorHandler({ message: error.message }); 80 | } 81 | }, [errorHandler, history, credentials.id, credentials.password, loginUser]); 82 | 83 | const signOut = useCallback(async () => { 84 | try { 85 | const response = await fetch("/api/user/logout", { 86 | method: "DELETE", 87 | }); 88 | if (response.ok) { 89 | logoutUser(); 90 | history.push("/login"); 91 | } 92 | } catch (error) { 93 | console.error(error.message); 94 | } 95 | }, [history, logoutUser]); 96 | 97 | return { 98 | loading, 99 | message, 100 | isError, 101 | formState, 102 | signIn, 103 | signOut, 104 | credentials, 105 | handleID, 106 | handlePassword, 107 | }; 108 | } 109 | -------------------------------------------------------------------------------- /client/src/components/datasheets/EventListItem.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components/macro"; 2 | import PropTypes from "prop-types"; 3 | 4 | import iconPax from "../../assets/icons/icon-general-pax.svg"; 5 | import iconBanquet from "../../assets/icons/icon-setup-Bankett.svg"; 6 | import iconChairCircle from "../../assets/icons/icon-setup-chairs.svg"; 7 | import iconFlipChart from "../../assets/icons/icon-setup-Flipchart.svg"; 8 | import iconClassRoom from "../../assets/icons/icon-setup-Klassen.svg"; 9 | import iconBoard from "../../assets/icons/icon-setup-tafel.svg"; 10 | import iconPinBoard from "../../assets/icons/icon-setup-pinnwand.svg"; 11 | import iconUForm from "../../assets/icons/icon-setup-uForm.svg"; 12 | 13 | const ListItem = styled.li` 14 | width: 100%; 15 | margin-bottom: 1rem; 16 | padding: 1rem 2rem; 17 | display: grid; 18 | gap: 0.5rem; 19 | grid-template-columns: 3rem 1fr 1fr 1fr; 20 | grid-template-rows: repeat(4, auto); 21 | grid-template-areas: 22 | "time time time time" 23 | "title title title title" 24 | "room room room room" 25 | "setup pax pinboard flipchart"; 26 | 27 | &:not(:only-child):nth-child(2n + 1) { 28 | background-color: var(--color-bg-sub); 29 | } 30 | `; 31 | 32 | const Time = styled.h4` 33 | grid-area: time; 34 | 35 | &:after { 36 | content: " Uhr"; 37 | } 38 | `; 39 | 40 | const Title = styled.h4` 41 | grid-area: title; 42 | `; 43 | 44 | const Room = styled.span` 45 | grid-area: room; 46 | margin-bottom: 1rem; 47 | 48 | &:before { 49 | content: "Raum: "; 50 | } 51 | `; 52 | 53 | const IconTag = styled.span` 54 | display: flex; 55 | align-items: center; 56 | margin: 0 auto; 57 | `; 58 | 59 | const Pax = styled(IconTag)` 60 | grid-area: pax; 61 | `; 62 | 63 | const PinBoard = styled(IconTag)` 64 | grid-area: pinboard; 65 | `; 66 | 67 | const FlipChart = styled(IconTag)` 68 | grid-area: flipchart; 69 | `; 70 | 71 | const Icon = styled.img` 72 | width: auto; 73 | height: 1.5rem; 74 | margin-right: 0.3rem; 75 | `; 76 | 77 | const IconSetup = styled(Icon)` 78 | grid-area: setup; 79 | `; 80 | 81 | export const EventListItem = ({ 82 | time, 83 | title, 84 | room, 85 | setup, 86 | pax = 0, 87 | pinboard = 0, 88 | flipchart = 0, 89 | onClick, 90 | }) => { 91 | const icons = { 92 | iconClassRoom, 93 | iconUForm, 94 | iconBoard, 95 | iconBanquet, 96 | iconChairCircle, 97 | }; 98 | 99 | const desc = { 100 | iconClassRoom: "Setup Klassenraum", 101 | iconUForm: "Setup U-Form", 102 | iconBoard: "Setup Tafel", 103 | iconBanquet: "Setup Bankett", 104 | iconChairCircle: "Setup Stuhlkreis", 105 | }; 106 | 107 | return ( 108 | 109 | 110 | {title} 111 | {room} 112 | 113 | 114 | 115 | {pax} 116 | 117 | 118 | 119 | {pinboard} 120 | 121 | 122 | 123 | {flipchart} 124 | 125 | 126 | ); 127 | }; 128 | 129 | EventListItem.propTypes = { 130 | time: PropTypes.string.isRequired, 131 | title: PropTypes.string.isRequired, 132 | room: PropTypes.string.isRequired, 133 | setup: PropTypes.string, 134 | pax: PropTypes.number, 135 | pinboard: PropTypes.number, 136 | flipchart: PropTypes.number, 137 | onClick: PropTypes.func, 138 | }; 139 | -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 43 | Artemis 44 | 45 | 46 | 47 |
    48 |
    49 | 107 |

    ARTEMIS

    108 |
    109 | 110 | 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Logo of the project 2 | 3 | # Artemis · [![Build Status](https://img.shields.io/travis/npm/npm/latest.svg?style=flat-square)](https://travis-ci.org/npm/npm) [![npm](https://img.shields.io/npm/v/npm.svg?style=flat-square)](https://www.npmjs.com/package/npm) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://github.com/AlexisRoe/Artemis/blob/master/LICENSE) 4 | 5 | Function Sheets 2.0 - Dashboard like tool to use for hotels/ event staff. 6 | 7 | **You can find the project here**: [Deployment on Heroku](https://dashboard-artemis.herokuapp.com/) 8 | 9 | 10 | 11 | Artemis is a dashboard like approach for replacing classic "Function Sheets". A function sheet is just a sheet of paper, which describes an event at a hotel for one day. It holdes information about the amount of people expected, the booked rooms, the setup, the ordered food & beverages and more. It is usually a big mess to spread the newest information to the colleagues. Why not using a tool, everyone have now. Your Phone. 12 | 13 | --- 14 | 15 | Artemis is my **capstone project** for the Webdeveloper Bootcamp at Neue Fische Cologne in late 2020. The capstone project is meant to learn and recaping concepts, which you are interested the most. You can log in and see some live data. But of course all data are mocked. 16 | 17 | What topics I covered with that project? 18 | 19 | - hashing and storing passwords 20 | - protected routing in the client & server 21 | - building middleware 22 | - Authentication with json web token and middleware 23 | - building API´s 24 | 25 | --- 26 | 27 | 28 | 29 | --- 30 | 31 | ## How to use 32 | 33 | ### 1. Login 34 | 35 | use the following credentials:
    36 | **User**: Martin McFly
    37 | **Password**: DeLorian 38 | 39 | ### 2. Daily Overview 40 | 41 | The homescreen is actually the daily overview about all events happening on that day, highlighting the next todos on the shedule. Please be aware, that everything is mocked, including the date. 42 | 43 | ### 3. Choose a specific event 44 | 45 | The event is displayed in more details, like it would be on regular Function Sheet. Why? Because users are used to it. It´s a fast and convenient way to display the information. The advancement is to get rid of some clutter like metadata, which is not usefull for the operational departments. 46 | 47 | ### 4. The logo is the menu 😉 48 | 49 | Here you can find, some more details, information about changed documents, helpful tools like mise-en-place (preparation) lists and more. Be aware, its still under construction. 50 | 51 | ## The design 52 | 53 | The design is meant to look luxurious, simple and concentrated on the information itself. I used two main colors and a sans-serif font. You can find all informations in the file below. 54 | 55 | [XD Artemis Prototyp](https://xd.adobe.com/view/a7884a1f-4aa5-4b74-b2b1-1d4a6eed2c98-f2db) (created in 2020/10)or use the uploaded file [XD file](./documentation/Prototyp-Design-Artemis.xd) 56 | 57 | ## Installing / Getting started 58 | 59 | For this project you need a MongoDB instance. For more information go to the database section 60 | 61 | 1. Create the two databases and fill it with data
    62 | [the mocked database as a json file](./lib/documentation/template/database.json) 63 | 64 | 2. Createa a **.env** file with the following credentials 65 | 66 | ```ènv 67 | DB_URL=mongodb+srv://:@?retryWrites=true&w=majority 68 | DB_NAME= 69 | DB_COLLECTION_USER=users 70 | DB_COLLECTION_EVENTS= events 71 | AUTH_TOKEN=eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxfQ.5b0YUvXu9IFCI4kqzNAfrnuA2lSMp8XtezIZTfQYH4k 72 | TOKEN_SECRET= 73 | ``` 74 | 75 | 3. Install node.js and use the following commands 76 | 77 | ```shell 78 | git clone git@github.com:AlexisRoe/Artemis.git 79 | cd artemis/ 80 | npm install 81 | ``` 82 | 83 | 4. standard commands 84 | 85 | for development 86 | 87 | ```shell 88 | npm run dev 89 | ``` 90 | 91 | for styling components 92 | 93 | ```shell 94 | npm run storybook 95 | ``` 96 | 97 | 4. Create a token_secret for the JSON web token. You can use the cli tools in the ./lib/cli folder for creating the token and hashing passwords for the user database. 98 | 99 | - [cli tool for hashing password using crypto.js](./lib/cli/hashingMaster.js) 100 | - [cli tool for creating secret tokens, used with jwt](./lib/cli/tokenGenerator.js) 101 | 102 | ### Build with 103 | 104 | [React](https://reactjs.org/), [styled-components](https://styled-components.com/), [express.js](https://expressjs.com/), [eslint](https://eslint.org/), [storybook](https://storybook.js.org/), [React Router](https://reactrouter.com/), [crypto-js](https://www.npmjs.com/package/crypto-js), [MongoDB](https://www.mongodb.com/) 105 | 106 | ## Tests 107 | 108 | ...in progress 109 | 110 | ## Database 111 | 112 | For this project, I used a Atlas Cluster of MongoDB. I created two collections, which you can find here. The collection events with the data I used can be found in the [./lib/documentation/template/database.json](./lib/documentation/template/database.json) file. 113 | 114 | 1. users 115 | 116 | ```json 117 | { 118 | "name": "Martin McFly", 119 | "password": "DeLorian", 120 | "lastModified": "2020-12-21T09:10:29.164+00:00", 121 | "auth_token": "jwt_token", 122 | "personalnr": "2015" 123 | } 124 | ``` 125 | 126 | 2. events 127 | 128 | ```json 129 | [ 130 | { 131 | "title": "string", 132 | "sign": "string", 133 | "Customer-ID": 1, 134 | "event-ID": 1, 135 | "start": "2020-10-01T10:00:00", 136 | "end": "2020-10-01T16:00:00", 137 | "pax": 1, 138 | "rooms": [ 139 | { 140 | "location": "string", 141 | "title": "string", 142 | "start": "2020-10-01T10:00:00", 143 | "end": "2020-10-01T10:00:00", 144 | "default": true, 145 | "role": "string", 146 | "pax": 1, 147 | "setup": "string icons", 148 | "equipment": { 149 | "flipchart": 1, 150 | "pinnboard": 2, 151 | "beamer": 1 152 | }, 153 | "notes": [""] 154 | } 155 | ], 156 | "event": [ 157 | { 158 | "title": "string", 159 | "location": "string", 160 | "pax": 0, 161 | "start": "2020-10-01T10:00:00", 162 | "end": "2020-10-01T10:00:00", 163 | "foods": ["string", "string", "string"], 164 | "beverages": ["string", "string", "string"] 165 | } 166 | ] 167 | }, 168 | { 169 | "icons": { 170 | "iconClassRoom": "iconClassRoom", 171 | "iconUForm": "iconUForm", 172 | "iconBoard": "iconBoard", 173 | "iconBanquet": "iconBanquet", 174 | "iconChairCircle": "iconChairCircle" 175 | } 176 | } 177 | ] 178 | ``` 179 | -------------------------------------------------------------------------------- /documentation/template/event.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Telekom Board Meeting", 4 | "sign": "Telekom Board 120", 5 | "Customer-ID": 1000, 6 | "event-ID": 54567, 7 | "start": "2020-10-01T10:00:00", 8 | "end": "2020-10-01T16:00:00", 9 | "pax": 10, 10 | "rooms": [ 11 | { 12 | "location": "Boardroom", 13 | "start": "2020-10-01T10:00:00", 14 | "end": "2020-10-01T16:00:00", 15 | "default": true, 16 | "role": "Hauptraum", 17 | "pax": 10, 18 | "setup": "iconBoard", 19 | "equipment": { 20 | "flipchart": 1, 21 | "pinnboard": 0, 22 | "beamer": 0 23 | }, 24 | "notes": ["Extra Stifte und Marker"] 25 | } 26 | ], 27 | "event": [ 28 | { 29 | "title": "Getränke im Raum", 30 | "location": "Boardroom", 31 | "pax": 10, 32 | "start": "2020-10-01T10:00:00", 33 | "end": "2020-10-01T16:00:00", 34 | "foods": ["Sandwiches", "HandObst", "Müsliriegel"], 35 | "beverages": ["1 Flasche großes Wasser", "frische Säfte"] 36 | }, 37 | { 38 | "title": "Mittagspause", 39 | "location": "Restaurant", 40 | "pax": 10, 41 | "start": "2020-10-01T12:00:00", 42 | "end": "2020-10-01T12:30:00", 43 | "foods": ["Auswahl aus dem Tagesmenu"], 44 | "beverages": ["Mineralwasser unbegrenzt"] 45 | } 46 | ] 47 | }, 48 | { 49 | "title": "Firma Meier", 50 | "sign": "Firma Meier", 51 | "Customer-ID": 10200, 52 | "event-ID": 78567, 53 | "start": "2020-10-01T08:00:00", 54 | "end": "2020-10-01T17:30:00", 55 | "pax": 25, 56 | "rooms": [ 57 | { 58 | "location": "Luise", 59 | "start": "2020-10-01T08:00:00", 60 | "end": "2020-10-01T17:30:00", 61 | "default": true, 62 | "role": "Hauptraum", 63 | "pax": 25, 64 | "setup": "iconClassRoom", 65 | "equipment": { 66 | "flipchart": 1, 67 | "pinnboard": 2, 68 | "beamer": 1 69 | }, 70 | "notes": ["Referententisch", "Moderatorenkoffer"] 71 | } 72 | ], 73 | "event": [ 74 | { 75 | "title": "Getränke im Raum", 76 | "location": "Luise", 77 | "pax": 25, 78 | "start": "2020-10-01T08:00:00", 79 | "end": "2020-10-01T17:30:00", 80 | "foods": [], 81 | "beverages": ["1 Flasche großes Wasser", "frische Säfte"] 82 | }, 83 | { 84 | "title": "Kaffeepause 1", 85 | "location": "Salon", 86 | "pax": 25, 87 | "start": "2020-10-01T10:00:00", 88 | "end": "2020-10-01T10:30:00", 89 | "foods": ["Kekse", "Joghurt", "frisches Obst"], 90 | "beverages": ["Kaffee/ Tee", "Mineralwasser"] 91 | }, 92 | { 93 | "title": "Mittagspause", 94 | "location": "Restaurant", 95 | "pax": 25, 96 | "start": "2020-10-01T12:00:00", 97 | "end": "2020-10-01T12:30:00", 98 | "foods": ["Auswahl aus dem Tagesmenu"], 99 | "beverages": ["Mineralwasser unbegrenzt"] 100 | }, 101 | { 102 | "title": "Kaffeepause 2", 103 | "location": "Salon", 104 | "pax": 25, 105 | "start": "2020-10-01T15:00:00", 106 | "end": "2020-10-01T15:30:00", 107 | "foods": ["1 Stück Kuchen pro Person"], 108 | "beverages": ["Kaffee/ Tee", "Mineralwasser"] 109 | } 110 | ] 111 | }, 112 | { 113 | "title": "ZahnärzteKongress 2020", 114 | "sign": "Let´s teeth 2020", 115 | "Customer-ID": 10220, 116 | "event-ID": 78557, 117 | "start": "2020-10-02T09:00:00", 118 | "end": "2020-10-02T16:30:00", 119 | "pax": 120, 120 | "rooms": [ 121 | { 122 | "location": "Goldsaal", 123 | "start": "2020-10-02T09:00:00", 124 | "end": "2020-10-02T16:30:00", 125 | "default": true, 126 | "role": "Hauptraum", 127 | "pax": 120, 128 | "setup": "iconClassRoom", 129 | "equipment": { 130 | "flipchart": 2, 131 | "pinnboard": 1, 132 | "beamer": 1 133 | }, 134 | "notes": ["Referententisch", "Moderatorenkoffer"] 135 | }, 136 | { 137 | "location": "Raum 10", 138 | "start": "2020-10-02T14:00:00", 139 | "end": "2020-10-02T16:30:00", 140 | "default": false, 141 | "role": "Gruppenraum 1", 142 | "pax": 30, 143 | "setup": "iconClassRoom", 144 | "equipment": { 145 | "flipchart": 5, 146 | "pinnboard": 2, 147 | "beamer": 0 148 | }, 149 | "notes": ["Moderatorenkoffer"] 150 | }, 151 | { 152 | "location": "Raum 20", 153 | "start": "2020-10-02T14:00:00", 154 | "end": "2020-10-02T16:30:00", 155 | "default": false, 156 | "role": "Gruppenraum 2", 157 | "pax": 30, 158 | "setup": "iconClassRoom", 159 | "equipment": { 160 | "flipchart": 5, 161 | "pinnboard": 2, 162 | "beamer": 0 163 | }, 164 | "notes": ["Moderatorenkoffer"] 165 | }, 166 | { 167 | "location": "Raum 30", 168 | "start": "2020-10-02T14:00:00", 169 | "end": "2020-10-02T16:30:00", 170 | "default": false, 171 | "role": "Gruppenraum 3", 172 | "pax": 30, 173 | "setup": "iconClassRoom", 174 | "equipment": { 175 | "flipchart": 5, 176 | "pinnboard": 2, 177 | "beamer": 0 178 | }, 179 | "notes": ["Moderatorenkoffer"] 180 | } 181 | ], 182 | "event": [ 183 | { 184 | "title": "Getränkebufett", 185 | "location": "Goldsaal", 186 | "pax": 150, 187 | "start": "2020-10-02T14:00:00", 188 | "end": "2020-10-02T16:30:00", 189 | "foods": [], 190 | "beverages": ["1 Flasche großes Wasser"] 191 | }, 192 | { 193 | "title": "Kaffeepause 1", 194 | "location": "Foyer", 195 | "pax": 120, 196 | "start": "2020-10-01T10:00:00", 197 | "end": "2020-10-01T10:30:00", 198 | "foods": ["Kekse", "Joghurt", "frisches Obst"], 199 | "beverages": ["Kaffee/ Tee", "Mineralwasser"] 200 | }, 201 | { 202 | "title": "Mittagspause", 203 | "location": "Foyer", 204 | "pax": 120, 205 | "start": "2020-10-01T12:00:00", 206 | "end": "2020-10-01T12:30:00", 207 | "foods": ["Fingerfood", "Tagessuppe", "belegte Brote"], 208 | "beverages": ["Mineralwasser unbegrenzt", "Kaffee/Tee Station"] 209 | }, 210 | { 211 | "title": "Kaffeepause 2", 212 | "location": "Foyer", 213 | "pax": 120, 214 | "start": "2020-10-01T15:00:00", 215 | "end": "2020-10-01T15:30:00", 216 | "foods": ["1 Stück Kuchen pro Person"], 217 | "beverages": ["Kaffee/ Tee", "Mineralwasser"] 218 | } 219 | ] 220 | }, 221 | { 222 | "title": "Telekom 122", 223 | "sign": "Team Zukunft", 224 | "Customer-ID": 1000, 225 | "event-ID": 785675, 226 | "start": "2020-10-03T08:30:00", 227 | "end": "2020-10-03T12:30:00", 228 | "pax": 25, 229 | "rooms": [ 230 | { 231 | "location": "Martin", 232 | "start": "2020-10-03T08:30:00", 233 | "end": "2020-10-03T12:30:00", 234 | "default": true, 235 | "role": "Hauptraum", 236 | "pax": 25, 237 | "setup": "iconUForm", 238 | "equipment": { 239 | "flipchart": 1, 240 | "pinnboard": 1, 241 | "beamer": 1 242 | }, 243 | "notes": ["Referententisch", "Moderatorenkoffer", "Seitenbuffett"] 244 | } 245 | ], 246 | "event": [ 247 | { 248 | "title": "Getränke im Raum", 249 | "location": "Martin", 250 | "pax": 25, 251 | "start": "2020-10-03T08:30:00", 252 | "end": "2020-10-03T12:30:00", 253 | "foods": ["belegte Brote"], 254 | "beverages": ["1 Flasche großes Wasser", "frische Säfte"] 255 | }, 256 | { 257 | "title": "Mittagspause", 258 | "location": "Restaurant", 259 | "pax": 25, 260 | "start": "2020-10-01T12:30:00", 261 | "end": "2020-10-01T13:00:00", 262 | "foods": ["Auswahl aus dem Tagesmenu"], 263 | "beverages": ["Mineralwasser unbegrenzt"] 264 | } 265 | ] 266 | }, 267 | { 268 | "title": "Telekom 128", 269 | "sign": "Personalteam #3", 270 | "Customer-ID": 1000, 271 | "event-ID": 7856753, 272 | "start": "2020-10-03T12:30:00", 273 | "end": "2020-10-03T17:00:00", 274 | "pax": 10, 275 | "rooms": [ 276 | { 277 | "location": "Oliver", 278 | "start": "2020-10-03T13:30:00", 279 | "end": "2020-10-03T11:00:00", 280 | "default": true, 281 | "role": "Hauptraum", 282 | "pax": 10, 283 | "setup": "iconChairCircle", 284 | "equipment": { 285 | "flipchart": 5, 286 | "pinnboard": 5, 287 | "beamer": 0 288 | }, 289 | "notes": ["Referententisch", "5 Moderatorenkoffer", "Seitenbuffett"] 290 | } 291 | ], 292 | "event": [ 293 | { 294 | "title": "Mittagspause", 295 | "location": "Restaurant", 296 | "pax": 10, 297 | "start": "2020-10-03T12:30:00", 298 | "end": "2020-10-03T13:30:00", 299 | "foods": ["Auswahl aus dem Tagesmenu"], 300 | "beverages": ["Mineralwasser unbegrenzt"] 301 | }, 302 | { 303 | "title": "Getränke im Raum", 304 | "location": "Oliver", 305 | "pax": 25, 306 | "start": "2020-10-03T08:30:00", 307 | "end": "2020-10-03T12:30:00", 308 | "foods": ["1 Stück Kuchen pro Person", "Handobst", "Schaumküsse"], 309 | "beverages": [ 310 | "1 Flasche großes Wasser", 311 | "frische Säfte", 312 | "Kaffee/Teestation" 313 | ] 314 | } 315 | ] 316 | } 317 | ] 318 | -------------------------------------------------------------------------------- /documentation/template/database.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Telekom Board Meeting", 4 | "sign": "Telekom Board 120", 5 | "Customer-ID": 1000, 6 | "event-ID": 54567, 7 | "start": 1601546400, 8 | "end": 1601568000, 9 | "pax": 10, 10 | "rooms": [ 11 | { 12 | "location": "Boardroom", 13 | "title": "Setup Boardroom", 14 | "start": 1601546400, 15 | "end": 1601568000, 16 | "default": true, 17 | "role": "Hauptraum", 18 | "pax": 10, 19 | "setup": "iconBoard", 20 | "equipment": { 21 | "flipchart": 1, 22 | "pinnboard": 0, 23 | "beamer": 0 24 | }, 25 | "notes": ["Extra Stifte und Marker"] 26 | } 27 | ], 28 | "event": [ 29 | { 30 | "title": "Getränke im Raum", 31 | "location": "Boardroom", 32 | "pax": 10, 33 | "start": 1601546400, 34 | "end": 1601568000, 35 | "foods": ["Sandwiches", "HandObst", "Müsliriegel"], 36 | "beverages": ["1 Flasche großes Wasser", "frische Säfte"] 37 | }, 38 | { 39 | "title": "Mittagspause", 40 | "location": "Restaurant", 41 | "pax": 10, 42 | "start": 1601553600, 43 | "end": 1601555400, 44 | "foods": ["Auswahl aus dem Tagesmenu"], 45 | "beverages": ["Mineralwasser unbegrenzt"] 46 | } 47 | ] 48 | }, 49 | { 50 | "title": "Firma Meier", 51 | "sign": "Firma Meier", 52 | "Customer-ID": 10200, 53 | "event-ID": 78567, 54 | "start": 1601539200, 55 | "end": 1601573400, 56 | "pax": 25, 57 | "rooms": [ 58 | { 59 | "location": "Luise", 60 | "title": "Setup Luise", 61 | "start": 1601539200, 62 | "end": 1601573400, 63 | "default": true, 64 | "role": "Hauptraum", 65 | "pax": 25, 66 | "setup": "iconClassRoom", 67 | "equipment": { 68 | "flipchart": 1, 69 | "pinnboard": 2, 70 | "beamer": 1 71 | }, 72 | "notes": ["Referententisch", "Moderatorenkoffer"] 73 | } 74 | ], 75 | "event": [ 76 | { 77 | "title": "Getränke im Raum", 78 | "location": "Luise", 79 | "pax": 25, 80 | "start": 1601539200, 81 | "end": 1601573400, 82 | "foods": [], 83 | "beverages": ["1 Flasche großes Wasser", "frische Säfte"] 84 | }, 85 | { 86 | "title": "Kaffeepause 1", 87 | "location": "Salon", 88 | "pax": 25, 89 | "start": 1601546400, 90 | "end": 1601548200, 91 | "foods": ["Kekse", "Joghurt", "frisches Obst"], 92 | "beverages": ["Kaffee/ Tee", "Mineralwasser"] 93 | }, 94 | { 95 | "title": "Mittagspause", 96 | "location": "Restaurant", 97 | "pax": 25, 98 | "start": 1601553600, 99 | "end": 1601555400, 100 | "foods": ["Auswahl aus dem Tagesmenu"], 101 | "beverages": ["Mineralwasser unbegrenzt"] 102 | }, 103 | { 104 | "title": "Kaffeepause 2", 105 | "location": "Salon", 106 | "pax": 25, 107 | "start": 1601564400, 108 | "end": 1601566200, 109 | "foods": ["1 Stück Kuchen pro Person"], 110 | "beverages": ["Kaffee/ Tee", "Mineralwasser"] 111 | } 112 | ] 113 | }, 114 | { 115 | "title": "ZahnärzteKongress 2020", 116 | "sign": "Let´s teeth 2020", 117 | "Customer-ID": 10220, 118 | "event-ID": 78557, 119 | "start": 1601629200, 120 | "end": 1601656200, 121 | "pax": 120, 122 | "rooms": [ 123 | { 124 | "location": "Goldsaal", 125 | "title": "Setup Goldsaal", 126 | "start": 1601629200, 127 | "end": 1601656200, 128 | "default": true, 129 | "role": "Hauptraum", 130 | "pax": 120, 131 | "setup": "iconClassRoom", 132 | "equipment": { 133 | "flipchart": 2, 134 | "pinnboard": 1, 135 | "beamer": 1 136 | }, 137 | "notes": ["Referententisch", "Moderatorenkoffer"] 138 | }, 139 | { 140 | "location": "Raum 10", 141 | "title": "Setup Raum 10", 142 | "start": 1601647200, 143 | "end": 1601656200, 144 | "default": false, 145 | "role": "Gruppenraum 1", 146 | "pax": 30, 147 | "setup": "iconClassRoom", 148 | "equipment": { 149 | "flipchart": 5, 150 | "pinnboard": 2, 151 | "beamer": 0 152 | }, 153 | "notes": ["Moderatorenkoffer"] 154 | }, 155 | { 156 | "location": "Raum 20", 157 | "title": "Setup Raum 20", 158 | "start": 1601647200, 159 | "end": 1601656200, 160 | "default": false, 161 | "role": "Gruppenraum 2", 162 | "pax": 30, 163 | "setup": "iconClassRoom", 164 | "equipment": { 165 | "flipchart": 5, 166 | "pinnboard": 2, 167 | "beamer": 0 168 | }, 169 | "notes": ["Moderatorenkoffer"] 170 | }, 171 | { 172 | "location": "Raum 30", 173 | "title": "Setup Raum 30", 174 | "start": 1601647200, 175 | "end": 1601656200, 176 | "default": false, 177 | "role": "Gruppenraum 3", 178 | "pax": 30, 179 | "setup": "iconClassRoom", 180 | "equipment": { 181 | "flipchart": 5, 182 | "pinnboard": 2, 183 | "beamer": 0 184 | }, 185 | "notes": ["Moderatorenkoffer"] 186 | } 187 | ], 188 | "event": [ 189 | { 190 | "title": "Getränkebufett", 191 | "location": "Goldsaal", 192 | "pax": 150, 193 | "start": 1601629200, 194 | "end": 1601656200, 195 | "foods": [], 196 | "beverages": ["1 Flasche großes Wasser"] 197 | }, 198 | { 199 | "title": "Kaffeepause 1", 200 | "location": "Foyer", 201 | "pax": 120, 202 | "start": 1601632800, 203 | "end": 1601634600, 204 | "foods": ["Kekse", "Joghurt", "frisches Obst"], 205 | "beverages": ["Kaffee/ Tee", "Mineralwasser"] 206 | }, 207 | { 208 | "title": "Mittagspause", 209 | "location": "Foyer", 210 | "pax": 120, 211 | "start": 1601640000, 212 | "end": 1601641800, 213 | "foods": ["Fingerfood", "Tagessuppe", "belegte Brote"], 214 | "beverages": ["Mineralwasser unbegrenzt", "Kaffee/Tee Station"] 215 | }, 216 | { 217 | "title": "Kaffeepause 2", 218 | "location": "Foyer", 219 | "pax": 120, 220 | "start": 1601650800, 221 | "end": 1601652600, 222 | "foods": ["1 Stück Kuchen pro Person"], 223 | "beverages": ["Kaffee/ Tee", "Mineralwasser"] 224 | } 225 | ] 226 | }, 227 | { 228 | "title": "Telekom 122", 229 | "sign": "Team Zukunft", 230 | "Customer-ID": 1000, 231 | "event-ID": 785675, 232 | "start": 1601713800, 233 | "end": 1601728200, 234 | "pax": 25, 235 | "rooms": [ 236 | { 237 | "location": "Martin", 238 | "title": "Setup Martin", 239 | "start": 1601713800, 240 | "end": 1601728200, 241 | "default": true, 242 | "role": "Hauptraum", 243 | "pax": 25, 244 | "setup": "iconUForm", 245 | "equipment": { 246 | "flipchart": 1, 247 | "pinnboard": 1, 248 | "beamer": 1 249 | }, 250 | "notes": ["Referententisch", "Moderatorenkoffer", "Seitenbuffett"] 251 | } 252 | ], 253 | "event": [ 254 | { 255 | "title": "Getränke im Raum", 256 | "location": "Martin", 257 | "pax": 25, 258 | "start": 1601713800, 259 | "end": 1601728200, 260 | "foods": ["belegte Brote"], 261 | "beverages": ["1 Flasche großes Wasser", "frische Säfte"] 262 | }, 263 | { 264 | "title": "Mittagspause", 265 | "location": "Restaurant", 266 | "pax": 25, 267 | "start": 1601728200, 268 | "end": 1601731800, 269 | "foods": ["Auswahl aus dem Tagesmenu"], 270 | "beverages": ["Mineralwasser unbegrenzt"] 271 | } 272 | ] 273 | }, 274 | { 275 | "title": "Telekom 128", 276 | "sign": "Personalteam #3", 277 | "Customer-ID": 1000, 278 | "event-ID": 7856753, 279 | "start": 1601728200, 280 | "end": 1601744400, 281 | "pax": 10, 282 | "rooms": [ 283 | { 284 | "location": "Oliver", 285 | "title": "Setup Oliver", 286 | "start": 1601731800, 287 | "end": 1601744400, 288 | "default": true, 289 | "role": "Hauptraum", 290 | "pax": 10, 291 | "setup": "iconChairCircle", 292 | "equipment": { 293 | "flipchart": 5, 294 | "pinnboard": 5, 295 | "beamer": 0 296 | }, 297 | "notes": ["Referententisch", "5 Moderatorenkoffer", "Seitenbuffett"] 298 | } 299 | ], 300 | "event": [ 301 | { 302 | "title": "Mittagspause", 303 | "location": "Restaurant", 304 | "pax": 10, 305 | "start": 1601728200, 306 | "end": 1601731800, 307 | "foods": ["Auswahl aus dem Tagesmenu"], 308 | "beverages": ["Mineralwasser unbegrenzt"] 309 | }, 310 | { 311 | "title": "Getränke im Raum", 312 | "location": "Oliver", 313 | "pax": 25, 314 | "start": 1601731800, 315 | "end": 1601744400, 316 | "foods": ["1 Stück Kuchen pro Person", "Handobst", "Schaumküsse"], 317 | "beverages": [ 318 | "1 Flasche großes Wasser", 319 | "frische Säfte", 320 | "Kaffee/Teestation" 321 | ] 322 | } 323 | ] 324 | }, 325 | { 326 | "title": "Melissa Meier", 327 | "sign": "Melissa kriegt die Kurve", 328 | "Customer-ID": 1123, 329 | "event-ID": 545367, 330 | "start": 1601803800, 331 | "end": 1601816400, 332 | "pax": 60, 333 | "rooms": [ 334 | { 335 | "location": "Goldsaal", 336 | "title": "Setup Goldsaal", 337 | "start": 1601803800, 338 | "end": 1601816400, 339 | "default": true, 340 | "role": "Hauptraum", 341 | "pax": 60, 342 | "setup": "iconBanquet", 343 | "equipment": { 344 | "flipchart": 0, 345 | "pinnboard": 0, 346 | "beamer": 0 347 | }, 348 | "notes": [ 349 | "Dekoration: rote Liese, Blumengestecke, weiße Tischwäsche, 8 pro Tisch" 350 | ] 351 | } 352 | ], 353 | "event": [ 354 | { 355 | "title": "Brunchbufett", 356 | "location": "Goldsaal", 357 | "pax": 60, 358 | "start": 1601803800, 359 | "end": 1601816400, 360 | "foods": [ 361 | "großer Brunch", 362 | "frische Waffeln", 363 | "Lachsriegel", 364 | "Kartoffelpfanne", 365 | "Frühstücksauswahl" 366 | ], 367 | "beverages": [ 368 | "Kaffee/Tee", 369 | "Saftstation mit 3 Säften", 370 | "heiße Schokolade", 371 | "Sekt", 372 | "Hauswein Rot/Weiß", 373 | "Bier nach Wahl" 374 | ] 375 | } 376 | ] 377 | }, 378 | { 379 | "title": "Unilever", 380 | "sign": "Impfen leicht gemacht", 381 | "Customer-ID": 112344, 382 | "event-ID": 5453637, 383 | "start": 1601920800, 384 | "end": 1601841600, 385 | "pax": 25, 386 | "rooms": [ 387 | { 388 | "location": "Martin", 389 | "title": "Setup Martin", 390 | "start": 1601807400, 391 | "end": 1601841600, 392 | "default": true, 393 | "role": "Hauptraum", 394 | "pax": 25, 395 | "setup": "iconUForm", 396 | "equipment": { 397 | "flipchart": 1, 398 | "pinnboard": 6, 399 | "beamer": 2 400 | }, 401 | "notes": [ 402 | "Gäste richten den Raum selber ein", 403 | "Bühne 3x4", 404 | "Pinnwände weiß bespannen" 405 | ] 406 | } 407 | ], 408 | "event": [ 409 | { 410 | "title": "Vorbereitung/ Setup", 411 | "location": "Martin", 412 | "pax": 5, 413 | "start": 1601805600, 414 | "end": 1601812800, 415 | "foods": [], 416 | "beverages": [] 417 | }, 418 | { 419 | "title": "Getränke im Raum", 420 | "location": "Martin", 421 | "pax": 100, 422 | "start": 1601805600, 423 | "end": 1601812800, 424 | "foods": [], 425 | "beverages": ["Wasser", "Säfte", "Softdrinks"] 426 | }, 427 | { 428 | "title": "Kaffeestation", 429 | "location": "Salon/Säule", 430 | "pax": 100, 431 | "start": 1601812800, 432 | "end": 1601834400, 433 | "foods": [ 434 | "Sandwiches", 435 | "Schokobrunnen", 436 | "frisches Obst", 437 | "Laugengebäck", 438 | "Tagessuppe" 439 | ], 440 | "beverages": ["Kaffee/Tee", "Wasser"] 441 | }, 442 | { 443 | "title": "Abbau", 444 | "location": "Martin", 445 | "pax": 5, 446 | "start": 1601834400, 447 | "end": 1601841600, 448 | "foods": [], 449 | "beverages": ["Sekt", "Bier"] 450 | }, 451 | { 452 | "title": "Abendessen", 453 | "location": "Restaurant", 454 | "pax": 5, 455 | "start": 1601841600, 456 | "end": 1601848800, 457 | "foods": ["A-la-Carte auf Rechnung"], 458 | "beverages": ["A-la-Carte auf Rechnung"] 459 | } 460 | ] 461 | }, 462 | { 463 | "title": "Werkstatt Meier", 464 | "sign": "Reperaturen leicht gemacht", 465 | "Customer-ID": 81773, 466 | "event-ID": 422437, 467 | "start": 1601886600, 468 | "end": 1601917200, 469 | "pax": 12, 470 | "rooms": [ 471 | { 472 | "location": "Luise", 473 | "title": "Setup Luise", 474 | "start": 1601886600, 475 | "end": 1601917200, 476 | "default": true, 477 | "role": "Hauptraum", 478 | "pax": 12, 479 | "setup": "iconUForm", 480 | "equipment": { 481 | "flipchart": 1, 482 | "pinnboard": 2, 483 | "beamer": 1 484 | }, 485 | "notes": ["Extra Stifte und Marker"] 486 | } 487 | ], 488 | "event": [ 489 | { 490 | "title": "Getränke im Raum", 491 | "location": "Luise", 492 | "pax": 12, 493 | "start": 1601886600, 494 | "end": 1601917200, 495 | "foods": [], 496 | "beverages": ["Tagungsgetränke"] 497 | }, 498 | { 499 | "title": "Kaffeepause", 500 | "location": "Salon", 501 | "pax": 12, 502 | "start": 1601892000, 503 | "end": 1601893800, 504 | "foods": ["Handobst", "belegte Brote", "Joghurt (kein Erdbeer)"], 505 | "beverages": ["Kaffee/Tee", "Wasser"] 506 | }, 507 | { 508 | "title": "Mittagspause", 509 | "location": "Restaurant", 510 | "pax": 12, 511 | "start": 1601899200, 512 | "end": 1601901000, 513 | "foods": ["Auswahl aus dem Tagesmenu"], 514 | "beverages": ["Mineralwasser unbegrenzt"] 515 | }, 516 | { 517 | "title": "Kaffeepause", 518 | "location": "Salon", 519 | "pax": 12, 520 | "start": 1601910000, 521 | "end": 1601911800, 522 | "foods": ["Kuchen"], 523 | "beverages": ["Kaffee/Tee", "Wasser"] 524 | } 525 | ] 526 | }, 527 | { 528 | "title": "Agentur Pop", 529 | "sign": "Dinner at 8", 530 | "Customer-ID": 281773, 531 | "event-ID": 4222437, 532 | "start": 1601920800, 533 | "end": 1601942340, 534 | "pax": 100, 535 | "rooms": [ 536 | { 537 | "location": "Goldsaal", 538 | "title": "Setup Goldsaal", 539 | "start": 1601920800, 540 | "end": 1601942400, 541 | "default": true, 542 | "role": "Hauptraum", 543 | "pax": 100, 544 | "setup": "iconBanquet", 545 | "equipment": { 546 | "flipchart": 0, 547 | "pinnboard": 0, 548 | "beamer": 0 549 | }, 550 | "notes": ["weiße Deko", "Seitentische", "Bühne 4x6"] 551 | }, 552 | { 553 | "location": "Fritz", 554 | "title": "Umkleide/ Abstellraum", 555 | "start": 1601920800, 556 | "end": 1601942400, 557 | "default": false, 558 | "role": "Umkleide", 559 | "pax": 10, 560 | "setup": "iconClassRoom", 561 | "equipment": { 562 | "flipchart": 0, 563 | "pinnboard": 0, 564 | "beamer": 0 565 | }, 566 | "notes": ["Seitentische", "Garderobenständer", "Aschenbecher"] 567 | } 568 | ], 569 | "event": [ 570 | { 571 | "title": "Dinner", 572 | "location": "Goldsaal", 573 | "pax": 100, 574 | "start": 1601920800, 575 | "end": 1601942400, 576 | "foods": ["Büfett Goldgans", "Rinderfilet"], 577 | "beverages": [ 578 | "Hausweine Rot/Weiß", 579 | "Bier", 580 | "Sekt", 581 | "Softs", 582 | "Spitituosen", 583 | "Getränke bis 1200€" 584 | ] 585 | }, 586 | { 587 | "title": "Umkleide", 588 | "location": "Goldsaal", 589 | "pax": 100, 590 | "start": 1601928000, 591 | "end": 1601935200, 592 | "foods": [], 593 | "beverages": ["Wasser", "Softs"] 594 | } 595 | ] 596 | }, 597 | { 598 | "title": "Liese GmbH", 599 | "sign": "Liese GmbH", 600 | "Customer-ID": 4481773, 601 | "event-ID": 498437, 602 | "start": 1601973000, 603 | "end": 1602001800, 604 | "pax": 12, 605 | "rooms": [ 606 | { 607 | "location": "Luise", 608 | "title": "Setup Luise", 609 | "start": 1601973000, 610 | "end": 1602001800, 611 | "default": true, 612 | "role": "Hauptraum", 613 | "pax": 12, 614 | "setup": "iconUForm", 615 | "equipment": { 616 | "flipchart": 2, 617 | "pinnboard": 3, 618 | "beamer": 1 619 | }, 620 | "notes": [] 621 | } 622 | ], 623 | "event": [ 624 | { 625 | "title": "Getränke im Raum", 626 | "location": "Luise", 627 | "pax": 12, 628 | "start": 1601973000, 629 | "end": 1602001800, 630 | "foods": [], 631 | "beverages": ["Tagungsgetränke"] 632 | }, 633 | { 634 | "title": "Kaffeepause", 635 | "location": "Salon", 636 | "pax": 12, 637 | "start": 1601978400, 638 | "end": 1601980200, 639 | "foods": ["Handobst", "belegte Brote", "Schokobrunnen"], 640 | "beverages": ["Kaffee/Tee", "Wasser"] 641 | }, 642 | { 643 | "title": "Mittagspause", 644 | "location": "Restaurant", 645 | "pax": 12, 646 | "start": 1601985600, 647 | "end": 1601987400, 648 | "foods": ["Auswahl aus dem Tagesmenu", "Fischallergie 1x"], 649 | "beverages": ["Mineralwasser unbegrenzt"] 650 | }, 651 | { 652 | "title": "Kaffeepause", 653 | "location": "Salon", 654 | "pax": 12, 655 | "start": 1601996400, 656 | "end": 1601998200, 657 | "foods": ["1 Stück Kuchen pro Person"], 658 | "beverages": ["Kaffee/Tee", "Wasser"] 659 | } 660 | ] 661 | }, 662 | { 663 | "title": "Meier GmbH", 664 | "sign": "Meier GmbH", 665 | "Customer-ID": 5681773, 666 | "event-ID": 49866437, 667 | "start": 1601978400, 668 | "end": 1601998200, 669 | "pax": 20, 670 | "rooms": [ 671 | { 672 | "location": "Werner", 673 | "title": "Setup Werner", 674 | "start": 1601978400, 675 | "end": 1601998200, 676 | "default": true, 677 | "role": "Hauptraum", 678 | "pax": 20, 679 | "setup": "iconUForm", 680 | "equipment": { 681 | "flipchart": 1, 682 | "pinnboard": 1, 683 | "beamer": 1 684 | }, 685 | "notes": ["extra Flipchart Papier"] 686 | } 687 | ], 688 | "event": [ 689 | { 690 | "title": "Getränke im Raum", 691 | "location": "Werner", 692 | "pax": 20, 693 | "start": 1601978400, 694 | "end": 1601998200, 695 | "foods": [], 696 | "beverages": ["Tagungsgetränke"] 697 | }, 698 | { 699 | "title": "Kaffeepause", 700 | "location": "Salon", 701 | "pax": 20, 702 | "start": 1601978400, 703 | "end": 1601980200, 704 | "foods": ["Handobst", "belegte Brote", "Joghurt"], 705 | "beverages": ["Kaffee/Tee", "Wasser"] 706 | }, 707 | { 708 | "title": "Mittagspause", 709 | "location": "Restaurant", 710 | "pax": 20, 711 | "start": 1601989200, 712 | "end": 1601991000, 713 | "foods": ["Auswahl aus dem Tagesmenu", "keine Nüsse"], 714 | "beverages": ["Mineralwasser unbegrenzt"] 715 | }, 716 | { 717 | "title": "Kaffeepause", 718 | "location": "Salon", 719 | "pax": 20, 720 | "start": 1601996400, 721 | "end": 1601998200, 722 | "foods": ["1 Stück Kuchen pro Person"], 723 | "beverages": ["Kaffee/Tee", "Wasser"] 724 | } 725 | ] 726 | }, 727 | { 728 | "title": "Schachverein Lupe", 729 | "sign": "Schachturnier 2020", 730 | "Customer-ID": 561773, 731 | "event-ID": 49869937, 732 | "start": 1602057600, 733 | "end": 1602100800, 734 | "pax": 75, 735 | "rooms": [ 736 | { 737 | "location": "Goldsaal", 738 | "title": "Setup Goldsaal", 739 | "start": 1602057600, 740 | "end": 1602100800, 741 | "default": true, 742 | "role": "Hauptraum", 743 | "pax": 75, 744 | "setup": "iconClassRoom", 745 | "equipment": { 746 | "flipchart": 10, 747 | "pinnboard": 10, 748 | "beamer": 2 749 | }, 750 | "notes": [ 751 | "extra Flipchart Papier", 752 | "extra Stifte", 753 | "3 Moderatorenkoffen", 754 | "Bühne 8x4 40cm" 755 | ] 756 | }, 757 | { 758 | "location": "Kiez", 759 | "title": "Setup Kiez", 760 | "start": 1602057600, 761 | "end": 1602100800, 762 | "default": false, 763 | "role": "Umkleide/Lager", 764 | "pax": 10, 765 | "setup": "iconChairCircle", 766 | "equipment": { 767 | "flipchart": 0, 768 | "pinnboard": 0, 769 | "beamer": 0 770 | }, 771 | "notes": ["Kleiderständer", "extra Mülleimer", "3 seitentische"] 772 | }, 773 | { 774 | "location": "Martin", 775 | "title": "Setup Martin", 776 | "start": 1602064800, 777 | "end": 1602079200, 778 | "default": false, 779 | "role": "Arena", 780 | "pax": 20, 781 | "setup": "iconClassRoom", 782 | "equipment": { 783 | "flipchart": 1, 784 | "pinnboard": 1, 785 | "beamer": 0 786 | }, 787 | "notes": ["nicht eindecken"] 788 | }, 789 | { 790 | "location": "Oliver", 791 | "title": "Setup Oliver", 792 | "start": 1602064800, 793 | "end": 1602079200, 794 | "default": false, 795 | "role": "Arena", 796 | "pax": 20, 797 | "setup": "iconClassRoom", 798 | "equipment": { 799 | "flipchart": 1, 800 | "pinnboard": 1, 801 | "beamer": 0 802 | }, 803 | "notes": ["nicht eindecken"] 804 | }, 805 | { 806 | "location": "Martin", 807 | "title": "Setup Martin", 808 | "start": 1602072000, 809 | "end": 1602100800, 810 | "default": false, 811 | "role": "Besprechungsraum", 812 | "pax": 12, 813 | "setup": "iconUForm", 814 | "equipment": { 815 | "flipchart": 0, 816 | "pinnboard": 0, 817 | "beamer": 0 818 | }, 819 | "notes": ["jede Stunde auffrischen"] 820 | } 821 | ], 822 | "event": [ 823 | { 824 | "title": "Getränke im Raum", 825 | "location": "Goldsaal", 826 | "pax": 75, 827 | "start": 1602057600, 828 | "end": 1602100800, 829 | "foods": ["Sandwiches", "Handobst"], 830 | "beverages": ["Tagungsgetränke"] 831 | }, 832 | { 833 | "title": "Kaffeepause", 834 | "location": "Salon", 835 | "pax": 75, 836 | "start": 1602064800, 837 | "end": 1602066600, 838 | "foods": ["Handobst", "belegte Brote", "Joghurt"], 839 | "beverages": ["Kaffee/Tee", "Wasser"] 840 | }, 841 | { 842 | "title": "Mittagspause", 843 | "location": "Restaurant", 844 | "pax": 75, 845 | "start": 1602075600, 846 | "end": 1602077400, 847 | "foods": ["Auswahl aus dem Tagesmenu"], 848 | "beverages": ["Mineralwasser unbegrenzt"] 849 | }, 850 | { 851 | "title": "Kaffeepause", 852 | "location": "Salon", 853 | "pax": 75, 854 | "start": 1602082800, 855 | "end": 1602084600, 856 | "foods": ["1 Stück Kuchen pro Person"], 857 | "beverages": ["Kaffee/Tee", "Wasser"] 858 | }, 859 | { 860 | "title": "Getränke im Raum", 861 | "location": "Martin", 862 | "pax": 12, 863 | "start": 1602072000, 864 | "end": 1602100800, 865 | "foods": ["Müsliriegel", "Gummibärchen"], 866 | "beverages": ["Kaffeebar", "Tagungsgetränke"] 867 | }, 868 | { 869 | "title": "Getränke im Raum", 870 | "location": "Oliver", 871 | "pax": 20, 872 | "start": 1602064800, 873 | "end": 1602079200, 874 | "foods": [], 875 | "beverages": ["Kaffeebar", "Tagungsgetränke"] 876 | } 877 | ] 878 | } 879 | ] 880 | --------------------------------------------------------------------------------