├── .prettierrc.json
├── .prettierignore
├── .eslintignore
├── nodemon.json
├── client
├── src
│ ├── pages
│ │ ├── index.js
│ │ ├── SignUpPage.stories.js
│ │ ├── Budget.js
│ │ ├── CreateToDo.stories.js
│ │ ├── ToDoPage.stories.js
│ │ ├── SignUpPage.js
│ │ ├── AddListItem.js
│ │ ├── CreateToDo.js
│ │ ├── Home.js
│ │ ├── GuestsPage.js
│ │ └── ToDoPage.js
│ ├── components
│ │ ├── ToDoListItem
│ │ │ ├── index.js
│ │ │ ├── ToDoListItem.stories.js
│ │ │ └── ToDoListItem.js
│ │ ├── form
│ │ │ ├── Button.stories.js
│ │ │ ├── DropDown.stories.js
│ │ │ ├── Form.stories.js
│ │ │ ├── Input.stories.js
│ │ │ ├── Button.js
│ │ │ ├── DropDown.js
│ │ │ ├── Input.js
│ │ │ └── Form.js
│ │ ├── listItem
│ │ │ ├── ListItem.stories.js
│ │ │ └── ListItem.js
│ │ ├── signUpForm
│ │ │ ├── SignUpForm.stories.js
│ │ │ └── SignUpForm.js
│ │ ├── DeleteButton.stories.js
│ │ ├── signUpButton
│ │ │ ├── SignUpButton.stories.js
│ │ │ └── SignUpButton.js
│ │ ├── addNewItemForm
│ │ │ ├── AddNewItemForm.stories.js
│ │ │ └── AddNewItemForm.js
│ │ ├── menu
│ │ │ ├── Menu.stories.js
│ │ │ └── Menu.js
│ │ ├── Header
│ │ │ ├── Header.stories.js
│ │ │ └── Header.js
│ │ ├── headerSignUp
│ │ │ ├── HeaderSignUp.stories.js
│ │ │ └── HeaderSignUp.js
│ │ ├── IitemsList
│ │ │ └── ItemsList.js
│ │ ├── userData
│ │ │ ├── UserDataDate.js
│ │ │ └── UserDataNames.js
│ │ ├── countDown
│ │ │ └── CountDown.js
│ │ ├── Modal.js
│ │ ├── MenuList
│ │ │ └── MenuList.js
│ │ ├── DeleteButton.js
│ │ ├── menuListItem
│ │ │ └── MenuListItem.js
│ │ └── List
│ │ │ ├── List.js
│ │ │ └── List.stories.js
│ ├── assets
│ │ ├── signUpBg.png
│ │ ├── homeBgImage.png
│ │ ├── menu.svg
│ │ ├── delete.svg
│ │ ├── uncompleted.svg
│ │ ├── close.svg
│ │ ├── filter.svg
│ │ ├── time.svg
│ │ ├── bubble.svg
│ │ ├── bubble1.svg
│ │ ├── euro.svg
│ │ ├── completed.svg
│ │ ├── people.svg
│ │ ├── heart.svg
│ │ ├── addNew.svg
│ │ ├── tasks.svg
│ │ ├── logo.svg
│ │ └── logo_Readme.svg
│ ├── fonts
│ │ ├── roboto-regular.ttf
│ │ └── lora-mediumItalic.ttf
│ ├── App.test.js
│ ├── api
│ │ ├── fetchGuests.js
│ │ ├── deleteToDo.js
│ │ ├── deleteGuest.js
│ │ ├── postToDo.js
│ │ ├── postGuest.js
│ │ └── fetchToDos.js
│ ├── setupTests.js
│ ├── apiAccount
│ │ ├── fetchAccount.js
│ │ └── postAccount.js
│ ├── stories
│ │ ├── Header.stories.js
│ │ ├── Page.stories.js
│ │ ├── header.css
│ │ ├── button.css
│ │ ├── Button.stories.js
│ │ ├── assets
│ │ │ ├── direction.svg
│ │ │ ├── flow.svg
│ │ │ ├── code-brackets.svg
│ │ │ ├── comments.svg
│ │ │ ├── repo.svg
│ │ │ ├── plugin.svg
│ │ │ ├── stackalt.svg
│ │ │ └── colors.svg
│ │ ├── page.css
│ │ ├── Button.js
│ │ ├── Header.js
│ │ ├── Page.js
│ │ └── Introduction.stories.mdx
│ ├── index.js
│ ├── hooks
│ │ └── useAsync.js
│ ├── App.js
│ ├── GlobalStyles.js
│ └── serviceWorker.js
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
├── .storybook
│ ├── main.js
│ └── preview.js
├── .gitignore
├── package.json
└── README.md
├── .github
├── ISSUE_TEMPLATE
│ └── user-story.md
└── workflows
│ └── node.js.yml
├── .eslintrc.json
├── server.js
├── README.md
├── LICENSE
├── db.json
├── package.json
└── .gitignore
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | build
2 | coverage
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | build/
3 | storybook-static/
--------------------------------------------------------------------------------
/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "ignore": ["client/*", "db.json"]
3 | }
4 |
--------------------------------------------------------------------------------
/client/src/pages/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./CreateToDo";
2 |
--------------------------------------------------------------------------------
/client/src/components/ToDoListItem/index.js:
--------------------------------------------------------------------------------
1 | export { default } from "./ToDoList";
2 |
--------------------------------------------------------------------------------
/client/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/client/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexRyzhkova/MarryMe_App/HEAD/client/public/favicon.ico
--------------------------------------------------------------------------------
/client/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexRyzhkova/MarryMe_App/HEAD/client/public/logo192.png
--------------------------------------------------------------------------------
/client/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexRyzhkova/MarryMe_App/HEAD/client/public/logo512.png
--------------------------------------------------------------------------------
/client/src/assets/signUpBg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexRyzhkova/MarryMe_App/HEAD/client/src/assets/signUpBg.png
--------------------------------------------------------------------------------
/client/src/assets/homeBgImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexRyzhkova/MarryMe_App/HEAD/client/src/assets/homeBgImage.png
--------------------------------------------------------------------------------
/client/src/fonts/roboto-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexRyzhkova/MarryMe_App/HEAD/client/src/fonts/roboto-regular.ttf
--------------------------------------------------------------------------------
/client/src/fonts/lora-mediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexRyzhkova/MarryMe_App/HEAD/client/src/fonts/lora-mediumItalic.ttf
--------------------------------------------------------------------------------
/client/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { render } from "@testing-library/react";
3 | import App from "./App";
4 |
5 | test("renders App", () => {
6 | render( );
7 | });
8 |
--------------------------------------------------------------------------------
/client/src/components/form/Button.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Button from "./Button";
3 |
4 | export default {
5 | title: "Button",
6 | component: Button,
7 | };
8 |
9 | export const SubmitButton = () => ;
10 |
--------------------------------------------------------------------------------
/client/src/assets/menu.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/components/listItem/ListItem.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ListItem from "./ListItem";
3 |
4 | export default {
5 | title: "ListItem",
6 | component: ListItem,
7 | };
8 |
9 | export const Item = () => ;
10 |
--------------------------------------------------------------------------------
/client/src/pages/SignUpPage.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import SignUpPage from "./SignUpPage";
3 |
4 | export default {
5 | title: "SignUpPage",
6 | component: SignUpPage,
7 | };
8 |
9 | export const CreateAccount = () => ;
10 |
--------------------------------------------------------------------------------
/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/components/signUpForm/SignUpForm.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import SignUpForm from "./SignUpForm";
3 |
4 | export default {
5 | title: "SignUpForm",
6 | component: SignUpForm,
7 | };
8 |
9 | export const MainInfos = () => ;
10 |
--------------------------------------------------------------------------------
/client/src/pages/Budget.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Header from "../components/Header/Header";
3 |
4 | export default function Budget() {
5 | return (
6 |
7 |
8 |
Work in progress
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/client/src/pages/CreateToDo.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import CreateToDo from "./CreateToDo";
3 |
4 | export default {
5 | title: "CreateToDo",
6 | component: CreateToDo,
7 | };
8 |
9 | export const Template = (args) => ;
10 |
--------------------------------------------------------------------------------
/client/src/components/DeleteButton.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import DeleteButton from "./DeleteButton";
3 |
4 | export default {
5 | title: "DeleteButton",
6 | component: DeleteButton,
7 | };
8 |
9 | export const DeleteItem = () => ;
10 |
--------------------------------------------------------------------------------
/client/src/components/form/DropDown.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import DropDown from "./DropDown";
3 |
4 | export default {
5 | title: "DropDown",
6 | component: DropDown,
7 | };
8 |
9 | export const Category = () => ;
10 |
--------------------------------------------------------------------------------
/client/src/components/signUpButton/SignUpButton.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import SignUpButton from "./SignUpButton";
3 |
4 | export default {
5 | title: "SignUpButton",
6 | component: SignUpButton,
7 | };
8 |
9 | export const SignUp = () => ;
10 |
--------------------------------------------------------------------------------
/client/src/api/fetchGuests.js:
--------------------------------------------------------------------------------
1 | export const getGuests = async () => {
2 | const response = await fetch("/api/guests");
3 |
4 | if (!response.ok) {
5 | throw new Error(response);
6 | }
7 |
8 | const fetchedGuests = await response.json();
9 | return fetchedGuests;
10 | };
11 |
--------------------------------------------------------------------------------
/client/src/components/addNewItemForm/AddNewItemForm.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import AddNewItemForm from "./AddNewItemForm";
3 |
4 | export default {
5 | title: "AddNewItem",
6 | component: AddNewItemForm,
7 | };
8 |
9 | export const AddGuest = () => ;
10 |
--------------------------------------------------------------------------------
/client/src/components/form/Form.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Form from "./Form";
3 |
4 | export default {
5 | title: "Form",
6 | component: Form,
7 | };
8 |
9 | export const CreateToDo = () => ;
10 |
11 | // type="text" value="Titel" placeholder="neues ToDo"
12 |
--------------------------------------------------------------------------------
/client/src/pages/ToDoPage.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { ToDoPage } from "./ToDoPage";
4 |
5 | export default {
6 | title: "ToDoPage",
7 | component: ToDoPage,
8 | };
9 |
10 | const Template = (args) => ;
11 |
12 | export const ToDoList = Template.bind({});
13 |
--------------------------------------------------------------------------------
/client/src/assets/delete.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/assets/uncompleted.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/components/menu/Menu.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import Menu from "./Menu";
4 |
5 | export default {
6 | title: "Menu",
7 | component: Menu,
8 | };
9 |
10 | const Template = (args) => ;
11 |
12 | export const ChoosePage = Template.bind({});
13 | ChoosePage.args = {};
14 |
--------------------------------------------------------------------------------
/client/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import "@testing-library/jest-dom/extend-expect";
6 | import "mutationobserver-shim";
7 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/user-story.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: User story
3 | about: agile feature description
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | # User story
11 | As a
12 | I want to
13 | so I can
14 |
15 | #Description
16 |
17 | #Material
18 |
19 | #Done?
20 | - [ ]
21 | - [ ]
22 | - [ ]
23 | - [ ]
24 |
--------------------------------------------------------------------------------
/client/src/apiAccount/fetchAccount.js:
--------------------------------------------------------------------------------
1 | export const getAccount = async () => {
2 | const response = await fetch("/api/account");
3 |
4 | if (!response.ok) {
5 | throw new Error(response);
6 | }
7 |
8 | const fetchedAccount = await response.json();
9 | console.log(fetchedAccount);
10 | return fetchedAccount;
11 | };
12 |
--------------------------------------------------------------------------------
/client/src/components/Header/Header.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import Header from "./Header";
4 |
5 | export default {
6 | title: "Header",
7 | component: Header,
8 | };
9 |
10 | const Template = (args) => ;
11 |
12 | export const WithMenu = Template.bind({});
13 | WithMenu.args = {};
14 |
--------------------------------------------------------------------------------
/client/src/api/deleteToDo.js:
--------------------------------------------------------------------------------
1 | export async function deleteTodo(toDo) {
2 | const response = await fetch(`/api/toDos/${toDo}`, {
3 | method: "DELETE",
4 | headers: { Accept: "application/json", "Content-Type": "application/json" },
5 | });
6 | if (!response.ok) {
7 | throw response;
8 | }
9 | return response;
10 | }
11 |
--------------------------------------------------------------------------------
/client/src/assets/close.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/assets/filter.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/assets/time.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/api/deleteGuest.js:
--------------------------------------------------------------------------------
1 | const deleteGuest = async (guest) => {
2 | const response = await fetch(`/api/guests/${guest}`, {
3 | method: "DELETE",
4 | headers: { Accept: "application/json", "Content-Type": "application/json" },
5 | });
6 | if (!response.ok) {
7 | throw response;
8 | }
9 | return response;
10 | };
11 |
12 | export default deleteGuest;
13 |
--------------------------------------------------------------------------------
/client/src/api/postToDo.js:
--------------------------------------------------------------------------------
1 | export const postTodo = async (toDo) => {
2 | const response = await fetch("/api/toDos", {
3 | method: "POST",
4 | headers: { Accept: "application/json", "Content-Type": "application/json" },
5 | body: JSON.stringify(toDo),
6 | });
7 |
8 | if (!response.ok) {
9 | throw new Error(response);
10 | }
11 |
12 | return response;
13 | };
14 |
--------------------------------------------------------------------------------
/client/src/api/postGuest.js:
--------------------------------------------------------------------------------
1 | export const postGuest = async (guest) => {
2 | const response = await fetch("/api/guests", {
3 | method: "POST",
4 | headers: { Accept: "application/json", "Content-Type": "application/json" },
5 | body: JSON.stringify(guest),
6 | });
7 |
8 | if (!response.ok) {
9 | throw new Error(response);
10 | }
11 |
12 | return response;
13 | };
14 |
--------------------------------------------------------------------------------
/client/src/components/headerSignUp/HeaderSignUp.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import HeaderSignUp from "./HeaderSignUp";
4 |
5 | export default {
6 | title: "HeaderSignUp",
7 | component: HeaderSignUp,
8 | };
9 |
10 | const Template = (args) => ;
11 |
12 | export const HeaderWithoutMenu = Template.bind({});
13 | HeaderWithoutMenu.args = {};
14 |
--------------------------------------------------------------------------------
/client/src/assets/bubble.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/client/src/stories/Header.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { Header } from "./Header";
4 |
5 | export default {
6 | title: "Example/Header",
7 | component: Header,
8 | };
9 |
10 | const Template = (args) => ;
11 |
12 | export const LoggedIn = Template.bind({});
13 | LoggedIn.args = {
14 | user: {},
15 | };
16 |
17 | export const LoggedOut = Template.bind({});
18 | LoggedOut.args = {};
19 |
--------------------------------------------------------------------------------
/client/src/apiAccount/postAccount.js:
--------------------------------------------------------------------------------
1 | const postAccount = async (account) => {
2 | const response = await fetch("/api/account", {
3 | method: "POST",
4 | headers: { Accept: "application/json", "Content-Type": "application/json" },
5 | body: JSON.stringify(account),
6 | });
7 |
8 | if (!response.ok) {
9 | throw new Error(response);
10 | }
11 |
12 | return response;
13 | };
14 |
15 | export default postAccount;
16 |
--------------------------------------------------------------------------------
/client/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import GlobalStyles from "../src/GlobalStyles";
3 | export const parameters = {
4 | actions: { argTypesRegex: "^on[A-Z].*" },
5 | layout: "fullscreen",
6 | };
7 | const withGlobalStyles = (Story, context) => {
8 | return (
9 | <>
10 |
11 |
12 | >
13 | );
14 | };
15 |
16 | export const decorators = [withGlobalStyles];
17 |
--------------------------------------------------------------------------------
/client/src/assets/bubble1.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/assets/euro.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/components/IitemsList/ItemsList.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styled from "@emotion/styled";
4 |
5 | function ItemsList({ children }) {
6 | return {children} ;
7 | }
8 |
9 | export default ItemsList;
10 |
11 | //styling
12 | const ListContainer = styled.ul`
13 | padding: 0;
14 | height: 100vh;
15 | `;
16 |
17 | ItemsList.propTypes = {
18 | children: PropTypes.node.isRequired,
19 | };
20 |
--------------------------------------------------------------------------------
/client/src/components/form/Input.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Input from "./Input";
3 |
4 | export default {
5 | title: "Input",
6 | component: Input,
7 | };
8 |
9 | export const TitleInput = () => (
10 |
11 | );
12 |
13 | export const InfosInput = () => (
14 |
20 | );
21 |
--------------------------------------------------------------------------------
/client/src/components/form/Button.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "@emotion/styled";
3 |
4 | const Button = () => {
5 | return Speichern ;
6 | };
7 |
8 | export default Button;
9 |
10 | const ButtonLight = styled.button`
11 | text-align: center;
12 | border-radius: 30px;
13 | padding: 5px 20px;
14 | outline: none;
15 | border: none;
16 | background-image: var(--base-bg-color);
17 | box-shadow: 2px 2px 6px 0px #141313;
18 | cursor: pointer;
19 | `;
20 |
--------------------------------------------------------------------------------
/client/src/components/ToDoListItem/ToDoListItem.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ToDoListItem from "./ToDoListItem";
3 |
4 | export default {
5 | title: "ToDoListItem",
6 | component: ToDoListItem,
7 | };
8 |
9 | export const Completed = () => (
10 |
11 | );
12 |
13 | export const Uncompleted = () => (
14 |
19 | );
20 |
--------------------------------------------------------------------------------
/client/src/api/fetchToDos.js:
--------------------------------------------------------------------------------
1 | export const getTodos = async () => {
2 | const response = await fetch("/api/toDos");
3 |
4 | if (!response.ok) {
5 | throw new Error(response);
6 | }
7 |
8 | const fetchedTodos = await response.json();
9 | return fetchedTodos;
10 | };
11 |
12 | export const getToDo = async (id) => {
13 | const response = await fetch(`/api/toDos/${id}`);
14 |
15 | if (!response.ok) {
16 | throw new Error(response);
17 | }
18 | const fetchedToDo = await response.json();
19 | return fetchedToDo;
20 | };
21 |
--------------------------------------------------------------------------------
/client/src/stories/Page.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { Page } from "./Page";
4 | import * as HeaderStories from "./Header.stories";
5 |
6 | export default {
7 | title: "Example/Page",
8 | component: Page,
9 | };
10 |
11 | const Template = (args) => ;
12 |
13 | export const LoggedIn = Template.bind({});
14 | LoggedIn.args = {
15 | ...HeaderStories.LoggedIn.args,
16 | };
17 |
18 | export const LoggedOut = Template.bind({});
19 | LoggedOut.args = {
20 | ...HeaderStories.LoggedOut.args,
21 | };
22 |
--------------------------------------------------------------------------------
/client/src/components/headerSignUp/HeaderSignUp.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "@emotion/styled";
3 | import logoSrc from "../../assets/logo.svg";
4 |
5 | const HeaderSignUp = () => {
6 | return (
7 |
8 |
9 |
10 | );
11 | };
12 |
13 | export default HeaderSignUp;
14 |
15 | //Styling
16 | const Container = styled.div`
17 | background-color: #a79292;
18 | display: flex;
19 | padding: 5px;
20 | img {
21 | margin: auto;
22 | }
23 | `;
24 |
--------------------------------------------------------------------------------
/client/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import App from "./App";
4 | import * as serviceWorker from "./serviceWorker";
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById("root")
11 | );
12 |
13 | // If you want your app to work offline and load faster, you can change
14 | // unregister() to register() below. Note this comes with some pitfalls.
15 | // Learn more about service workers: https://bit.ly/CRA-PWA
16 | serviceWorker.unregister();
17 |
--------------------------------------------------------------------------------
/client/src/components/signUpButton/SignUpButton.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "@emotion/styled";
3 |
4 | const SignUpButton = () => {
5 | return Registrieren ;
6 | };
7 |
8 | export default SignUpButton;
9 |
10 | const StyledSignUpButton = styled.button`
11 | text-align: center;
12 | border-radius: 1.8em;
13 | padding: 0.6em 1.8em;
14 | outline: none;
15 | border: none;
16 | cursor: pointer;
17 | color: var(--font-color-light);
18 | background-color: var(--button-bg-color);
19 | `;
20 |
--------------------------------------------------------------------------------
/client/src/assets/completed.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.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 | "globals": {
15 | "Atomics": "readonly",
16 | "SharedArrayBuffer": "readonly"
17 | },
18 | "parserOptions": {
19 | "ecmaFeatures": {
20 | "jsx": true
21 | },
22 | "ecmaVersion": 2018,
23 | "sourceType": "module"
24 | },
25 | "plugins": ["react"],
26 | "rules": {}
27 | }
28 |
--------------------------------------------------------------------------------
/client/src/assets/people.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/components/userData/UserDataDate.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styled from "@emotion/styled";
4 |
5 | export default function UserDataDate({ account }) {
6 | return (
7 | <>
8 | {account.date}
9 | >
10 | );
11 | }
12 |
13 | UserDataDate.propTypes = {
14 | account: PropTypes.any,
15 | };
16 |
17 | //styling
18 | const Date = styled.div`
19 | text-align: center;
20 | font-family: var(--font-lora);
21 | font-size: 2.2rem;
22 | color: var(--font-color-dark);
23 | margin: 30px 0;
24 | `;
25 |
--------------------------------------------------------------------------------
/client/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "MarryMe",
3 | "name": "MarryMe App",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/client/src/stories/header.css:
--------------------------------------------------------------------------------
1 | .wrapper {
2 | font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
3 | border-bottom: 1px solid rgba(0, 0, 0, 0.1);
4 | padding: 15px 20px;
5 | display: flex;
6 | align-items: center;
7 | justify-content: space-between;
8 | }
9 |
10 | svg {
11 | display: inline-block;
12 | vertical-align: top;
13 | }
14 |
15 | h1 {
16 | font-weight: 900;
17 | font-size: 20px;
18 | line-height: 1;
19 | margin: 6px 0 6px 10px;
20 | display: inline-block;
21 | vertical-align: top;
22 | }
23 |
24 | button + button {
25 | margin-left: 10px;
26 | }
27 |
--------------------------------------------------------------------------------
/client/src/components/userData/UserDataNames.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styled from "@emotion/styled";
4 |
5 | export default function UserDataNames({ account }) {
6 | return (
7 |
8 |
9 | {account.yourName} und {account.partnerName}
10 |
11 | heiraten in
12 |
13 | );
14 | }
15 | //Styling
16 | const NamesContainer = styled.div`
17 | text-align: center;
18 | p {
19 | text-align: center;
20 | font-family: var(--font-lora);
21 | font-size: 2.2rem;
22 | }
23 | `;
24 |
25 | UserDataNames.propTypes = {
26 | account: PropTypes.any,
27 | };
28 |
--------------------------------------------------------------------------------
/client/src/components/countDown/CountDown.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styled from "@emotion/styled";
4 |
5 | export default function CountDown({ date }) {
6 | const countDownDate = new Date(date).getTime();
7 | const now = new Date().getTime();
8 | const difference = countDownDate - now;
9 |
10 | const days = Math.floor(difference / (1000 * 60 * 60 * 24));
11 |
12 | return (
13 | <>
14 | {days} Tagen
15 | >
16 | );
17 | }
18 | //Styling
19 | const Days = styled.div`
20 | text-align: center;
21 | font-family: var(--font-lora);
22 | font-size: 2.2rem;
23 | `;
24 |
25 | CountDown.propTypes = {
26 | date: PropTypes.any,
27 | };
28 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const path = require("path");
3 | const jsonServer = require("json-server");
4 |
5 | const app = express();
6 | const router = jsonServer.router("db.json");
7 | const middlewares = jsonServer.defaults();
8 | const port = process.env.PORT || 3001;
9 |
10 | // Serve any static files
11 | app.use(express.static(path.join(__dirname, "client/build")));
12 | app.use(
13 | "/storybook",
14 | express.static(path.join(__dirname, "client/storybook-static"))
15 | );
16 |
17 | app.use(
18 | jsonServer.rewriter({
19 | "/api/*": "/$1",
20 | })
21 | );
22 |
23 | app.use(middlewares);
24 | app.use(router);
25 |
26 | app.listen(port, () => {
27 | console.log(`Example app listening at http://localhost:${port}`);
28 | });
29 |
--------------------------------------------------------------------------------
/client/src/assets/heart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/stories/button.css:
--------------------------------------------------------------------------------
1 | .storybook-button {
2 | font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
3 | font-weight: 700;
4 | border: 0;
5 | border-radius: 3em;
6 | cursor: pointer;
7 | display: inline-block;
8 | line-height: 1;
9 | }
10 | .storybook-button--primary {
11 | color: white;
12 | background-color: #1ea7fd;
13 | }
14 | .storybook-button--secondary {
15 | color: #333;
16 | background-color: transparent;
17 | box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
18 | }
19 | .storybook-button--small {
20 | font-size: 12px;
21 | padding: 10px 16px;
22 | }
23 | .storybook-button--medium {
24 | font-size: 14px;
25 | padding: 11px 20px;
26 | }
27 | .storybook-button--large {
28 | font-size: 16px;
29 | padding: 12px 24px;
30 | }
31 |
--------------------------------------------------------------------------------
/client/src/stories/Button.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import { Button } from "./Button";
4 |
5 | export default {
6 | title: "Example/Button",
7 | component: Button,
8 | argTypes: {
9 | backgroundColor: { control: "color" },
10 | },
11 | };
12 |
13 | const Template = (args) => ;
14 |
15 | export const Primary = Template.bind({});
16 | Primary.args = {
17 | primary: true,
18 | label: "Button",
19 | };
20 |
21 | export const Secondary = Template.bind({});
22 | Secondary.args = {
23 | label: "Button",
24 | };
25 |
26 | export const Large = Template.bind({});
27 | Large.args = {
28 | size: "large",
29 | label: "Button",
30 | };
31 |
32 | export const Small = Template.bind({});
33 | Small.args = {
34 | size: "small",
35 | label: "Button",
36 | };
37 |
--------------------------------------------------------------------------------
/client/src/components/Modal.js:
--------------------------------------------------------------------------------
1 | import styled from "@emotion/styled";
2 | import React from "react";
3 | import PropTypes from "prop-types";
4 |
5 | const Backdrop = styled.div`
6 | position: fixed;
7 | top: 0;
8 | z-index: 1;
9 | height: 100%;
10 | width: 100%;
11 | background-color: black;
12 | opacity: 0.5;
13 | `;
14 |
15 | const Container = styled.div`
16 | padding: 10px;
17 | position: fixed;
18 | top: 15%;
19 | bottom: 5%;
20 | left: 5%;
21 | right: 5%;
22 | background-color: #cebebe;
23 | z-index: 2;
24 | border-radius: 15px;
25 | `;
26 |
27 | function Modal({ children }) {
28 | return (
29 | <>
30 |
31 | {children}
32 | >
33 | );
34 | }
35 |
36 | export default Modal;
37 |
38 | Modal.propTypes = { children: PropTypes.node };
39 |
--------------------------------------------------------------------------------
/client/src/assets/addNew.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/hooks/useAsync.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, useCallback } from "react";
2 |
3 | function useAsync(asyncFunction, params) {
4 | const [data, setData] = useState(null);
5 | const [loading, setLoading] = useState(false);
6 | const [error, setError] = useState(false);
7 |
8 | const fetchData = useCallback(async () => {
9 | try {
10 | setLoading(true);
11 | setError(false);
12 | setData(null);
13 | const newData = await asyncFunction(params);
14 | setData(newData);
15 | } catch (error) {
16 | console.error(error);
17 | setError(true);
18 | } finally {
19 | setLoading(false);
20 | }
21 | }, [asyncFunction, params]);
22 | useEffect(() => {
23 | fetchData();
24 | }, [fetchData]);
25 | return { data, loading, error, refetch: fetchData };
26 | }
27 |
28 | export default useAsync;
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 |
15 | runs-on: ubuntu-latest
16 |
17 | strategy:
18 | matrix:
19 | node-version: [10.x, 12.x, 14.x]
20 |
21 | steps:
22 | - uses: actions/checkout@v2
23 | - name: Use Node.js ${{ matrix.node-version }}
24 | uses: actions/setup-node@v1
25 | with:
26 | node-version: ${{ matrix.node-version }}
27 | - run: npm ci
28 | - run: npm run build --if-present
29 | - run: npm test
30 |
--------------------------------------------------------------------------------
/client/src/components/MenuList/MenuList.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "@emotion/styled";
3 | import PropTypes from "prop-types";
4 |
5 | const MenuList = ({ open, children }) => {
6 | return {children} ;
7 | };
8 |
9 | export default MenuList;
10 |
11 | //Styling
12 | const Menu = styled.ul`
13 | margin-top: 0;
14 | list-style: none;
15 | display: flex;
16 | border-right: 1.5px solid black;
17 | transition: transform 0.3s ease-in-out;
18 | transform: ${({ open }) => (open ? "translateX(-27%)" : "translateX(-130%)")};
19 | position: fixed;
20 | flex-flow: column nowrap;
21 | background-color: var(--menu-bg-color);
22 | top: 0;
23 | right: 0;
24 | height: 100%;
25 | width: 85%;
26 | z-index: 20;
27 | `;
28 |
29 | MenuList.propTypes = {
30 | open: PropTypes.bool,
31 | children: PropTypes.any,
32 | };
33 |
--------------------------------------------------------------------------------
/client/src/components/DeleteButton.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import deleteIconSrc from "../assets/delete.svg";
3 | import styled from "@emotion/styled";
4 | import PropTypes from "prop-types";
5 |
6 | const DeleteButton = ({ onClick }) => {
7 | return (
8 |
9 |
10 | ToDo löschen
11 |
12 | );
13 | };
14 | export default DeleteButton;
15 |
16 | const StyledButton = styled.button`
17 | display: flex;
18 | flex-direction: row;
19 | outline: none;
20 | border: none;
21 | cursor: pointer;
22 | color: grey;
23 | margin: 0px;
24 | padding: 0px;
25 | background-color: Transparent;
26 | font-size: 0.9em;
27 | p {
28 | margin: 0px;
29 | padding-left: 0.4em;
30 | align-self: center;
31 | }
32 | `;
33 |
34 | DeleteButton.propTypes = {
35 | onClick: PropTypes.func,
36 | };
37 |
--------------------------------------------------------------------------------
/client/src/components/menu/Menu.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import MenuListItem from "../menuListItem/MenuListItem";
3 | import PropTypes from "prop-types";
4 | import timeIconSrc from "../../assets/time.svg";
5 | import tasksIconSrc from "../../assets/tasks.svg";
6 | import euroIconSrc from "../../assets/euro.svg";
7 | import peopleIconSrc from "../../assets/people.svg";
8 | import MenuList from "../MenuList/MenuList";
9 |
10 | const Menu = ({ open }) => {
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 | );
19 | };
20 |
21 | export default Menu;
22 |
23 | Menu.propTypes = {
24 | open: PropTypes.bool.isRequired,
25 | };
26 |
--------------------------------------------------------------------------------
/client/src/App.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import GlobalStyles from "./GlobalStyles";
3 | import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
4 | import { ToDoPage } from "./pages/ToDoPage";
5 | import Home from "./pages/Home";
6 | import SignUpPage from "./pages/SignUpPage";
7 | import GuestsPage from "./pages/GuestsPage";
8 | import Budget from "./pages/Budget";
9 |
10 | function App() {
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | );
35 | }
36 |
37 | export default App;
38 |
--------------------------------------------------------------------------------
/client/src/assets/tasks.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/components/menuListItem/MenuListItem.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "@emotion/styled";
3 | import { Link } from "react-router-dom";
4 | import PropTypes from "prop-types";
5 |
6 | const MenuListItem = ({ to, text, icon, description }) => {
7 | return (
8 |
9 |
10 |
11 | {text}
12 |
13 |
14 | );
15 | };
16 |
17 | export default MenuListItem;
18 |
19 | //styling
20 | const Container = styled.li`
21 | margin-top: 2em;
22 | list-style-type: none;
23 | a {
24 | display: flex;
25 | padding-top: 2rem;
26 | padding-left: 0.5rem;
27 | max-width: 90%;
28 | align-items: center;
29 | text-decoration: none;
30 | color: var(--font-color-dark);
31 | cursor: pointer;
32 | }
33 |
34 | img {
35 | padding-right: 0.5em;
36 | }
37 | `;
38 |
39 | MenuListItem.propTypes = {
40 | to: PropTypes.any,
41 | text: PropTypes.string,
42 | icon: PropTypes.any,
43 | description: PropTypes.string,
44 | };
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # 
4 |
5 | **THIS IS A FINAL PROJECT FOR MY WEB DEVELOPER BOOTCAMP AT [neue fische](https://www.neuefische.de/)**
6 |
7 | Marry Me is the app which helps you to plan and organize your wedding. With the to-do list, guest list and budget planning you can keep track of all the important things.
8 |
9 | ## 📲 Design and layout infos
10 |
11 | The app is designed and laid out for smartphone screens, so adjust the settings in your view accordingly to IPhone 5 or 6/7/8.
12 |
13 | ## 🖱 Deployment
14 |
15 | The app is deployed on Heroku and can be tested here:
16 | [MarryMe Deployment](https://marry-me-app.herokuapp.com/)
17 |
18 | ## 🔧 Development
19 |
20 | ### Requirements
21 |
22 | Node.js and npm
23 |
24 | ### 👨💻 Install all dependencies
25 |
26 | `npm install`
27 |
28 | Since there is a postinstall, the system automatically searches the client folder and installs the required dependencies there, too.
29 |
30 | ### 💻 Run dev server with:
31 |
32 | `npm run dev`
33 |
--------------------------------------------------------------------------------
/client/src/components/form/DropDown.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Dropdown from "react-dropdown";
3 | import "react-dropdown/style.css";
4 | import styled from "@emotion/styled";
5 | import PropTypes from "prop-types";
6 |
7 | const DropDown = ({ category, handleCategoryChange }) => {
8 | const options = [
9 | "",
10 | "Location",
11 | "Musik",
12 | "Foto/Video",
13 | "Bekleidung",
14 | "Blumen und Deko",
15 | "Einladungen",
16 | "Sonstiges",
17 | ];
18 |
19 | const defaultOption = options[0];
20 |
21 | return (
22 |
23 |
30 |
31 | );
32 | };
33 |
34 | export default DropDown;
35 |
36 | const DropdownContainer = styled.div`
37 | display: flex;
38 | flex-direction: row;
39 | font-size: 0.9em;
40 | `;
41 | DropDown.propTypes = {
42 | category: PropTypes.string,
43 | handleCategoryChange: PropTypes.func,
44 | };
45 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 AlexRyzhkova
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/components/form/Input.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import styled from "@emotion/styled";
4 |
5 | const Input = ({ topic, type, value, onChange, placeholder }) => {
6 | return (
7 |
8 | {topic}
9 |
16 |
17 | );
18 | };
19 | export default Input;
20 |
21 | const StyledInput = styled.label`
22 | display: grid;
23 | grid-template-columns: 1fr 60%;
24 | width: 100%;
25 | height: auto;
26 | margin-bottom: 2em;
27 | grid-column-gap: 1em;
28 |
29 | p {
30 | margin: 0px;
31 | grid-column: 1/2;
32 | justify-self: end;
33 | }
34 |
35 | input {
36 | outline: none;
37 | border: none;
38 | font-size: 0.9em;
39 | grid-column: 2/3;
40 | }
41 | `;
42 |
43 | Input.propTypes = {
44 | type: PropTypes.string,
45 | value: PropTypes.string,
46 | onChange: PropTypes.func,
47 | placeholder: PropTypes.string,
48 | topic: PropTypes.string,
49 | };
50 |
--------------------------------------------------------------------------------
/client/src/components/List/List.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ToDoListItem from "../ToDoListItem/ToDoListItem";
3 | import styled from "@emotion/styled";
4 | import PropTypes from "prop-types";
5 |
6 | const List = ({ items = [], onClick, error, loading }) => {
7 | return (
8 |
9 | {error && Error
}
10 | {loading && Loading ...
}
11 |
12 | {items?.map((item) => (
13 | onClick(item.id)}
21 | />
22 | ))}
23 |
24 | );
25 | };
26 |
27 | export default List;
28 |
29 | const ListContainer = styled.div`
30 | display: flex;
31 | flex-direction: column;
32 | margin: 5px;
33 | align-self: center;
34 | height: 100vh;
35 |
36 | button {
37 | margin: 0.3em;
38 | /* max-width: 50rem; */
39 | }
40 | `;
41 |
42 | List.propTypes = {
43 | items: PropTypes.array,
44 | onClick: PropTypes.func,
45 | error: PropTypes.any,
46 | loading: PropTypes.any,
47 | };
48 |
--------------------------------------------------------------------------------
/client/src/stories/assets/direction.svg:
--------------------------------------------------------------------------------
1 | illustration/direction
--------------------------------------------------------------------------------
/client/src/components/List/List.stories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import List from "./List";
3 |
4 | export default {
5 | title: "List",
6 | component: List,
7 | };
8 | const toDos = [
9 | {
10 | id: 1,
11 | title: "Angebot anfordern",
12 | category: "Location",
13 | completed: false,
14 | },
15 | {
16 | id: 2,
17 | title: "Dj anrufen",
18 | category: "Musik",
19 | completed: true,
20 | },
21 | {
22 | id: 3,
23 | title: "Kleid auswählen",
24 | category: "Bekleidung",
25 | completed: false,
26 | },
27 | {
28 | id: 4,
29 | title: "Menü abstimmen",
30 | category: "Location",
31 | completed: false,
32 | },
33 | {
34 | id: 5,
35 | title: "Kleid auswählen",
36 | category: "Bekleidung",
37 | completed: false,
38 | },
39 | {
40 | id: 6,
41 | title: "Menü abstimmen",
42 | category: "Location",
43 | completed: false,
44 | },
45 | {
46 | id: 7,
47 | title: "Kleid auswählen",
48 | category: "Bekleidung",
49 | completed: false,
50 | },
51 | {
52 | id: 8,
53 | title: "Menü abstimmen",
54 | category: "Location",
55 | completed: false,
56 | },
57 | ];
58 |
59 | export const ListAllItems = () =>
;
60 |
61 | export const Empty = () =>
;
62 |
--------------------------------------------------------------------------------
/client/src/stories/page.css:
--------------------------------------------------------------------------------
1 | section {
2 | font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
3 | font-size: 14px;
4 | line-height: 24px;
5 | padding: 48px 20px;
6 | margin: 0 auto;
7 | max-width: 600px;
8 | color: #333;
9 | }
10 |
11 | h2 {
12 | font-weight: 900;
13 | font-size: 32px;
14 | line-height: 1;
15 | margin: 0 0 4px;
16 | display: inline-block;
17 | vertical-align: top;
18 | }
19 |
20 | p {
21 | margin: 1em 0;
22 | }
23 |
24 | a {
25 | text-decoration: none;
26 | color: #1ea7fd;
27 | }
28 |
29 | ul {
30 | padding-left: 30px;
31 | margin: 1em 0;
32 | }
33 |
34 | li {
35 | margin-bottom: 8px;
36 | }
37 |
38 | .tip {
39 | display: inline-block;
40 | border-radius: 1em;
41 | font-size: 11px;
42 | line-height: 12px;
43 | font-weight: 700;
44 | background: #e7fdd8;
45 | color: #66bf3c;
46 | padding: 4px 12px;
47 | margin-right: 10px;
48 | vertical-align: top;
49 | }
50 |
51 | .tip-wrapper {
52 | font-size: 13px;
53 | line-height: 20px;
54 | margin-top: 40px;
55 | margin-bottom: 40px;
56 | }
57 |
58 | .tip-wrapper svg {
59 | display: inline-block;
60 | height: 12px;
61 | width: 12px;
62 | margin-right: 4px;
63 | vertical-align: top;
64 | margin-top: 3px;
65 | }
66 |
67 | .tip-wrapper svg path {
68 | fill: #1ea7fd;
69 | }
70 |
--------------------------------------------------------------------------------
/client/src/stories/Button.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import "./button.css";
4 |
5 | /**
6 | * Primary UI component for user interaction
7 | */
8 | export const Button = ({ primary, backgroundColor, size, label, ...props }) => {
9 | const mode = primary
10 | ? "storybook-button--primary"
11 | : "storybook-button--secondary";
12 | return (
13 |
21 | {label}
22 |
23 | );
24 | };
25 |
26 | Button.propTypes = {
27 | /**
28 | * Is this the principal call to action on the page?
29 | */
30 | primary: PropTypes.bool,
31 | /**
32 | * What background color to use
33 | */
34 | backgroundColor: PropTypes.string,
35 | /**
36 | * How large should the button be?
37 | */
38 | size: PropTypes.oneOf(["small", "medium", "large"]),
39 | /**
40 | * Button contents
41 | */
42 | label: PropTypes.string.isRequired,
43 | /**
44 | * Optional click handler
45 | */
46 | onClick: PropTypes.func,
47 | };
48 |
49 | Button.defaultProps = {
50 | backgroundColor: null,
51 | primary: false,
52 | size: "medium",
53 | onClick: undefined,
54 | };
55 |
--------------------------------------------------------------------------------
/client/src/stories/assets/flow.svg:
--------------------------------------------------------------------------------
1 | illustration/flow
--------------------------------------------------------------------------------
/client/src/components/Header/Header.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import styled from "@emotion/styled";
3 | import logoSrc from "../../assets/logo.svg";
4 | import MenuIconSrc from "../../assets/menu.svg";
5 | import Menu from "../menu/Menu";
6 |
7 | const Header = () => {
8 | const [open, setOpen] = useState(false);
9 |
10 | return (
11 |
12 |
13 | setOpen(!open)}
16 | src={MenuIconSrc}
17 | alt="Menu icon"
18 | />
19 |
20 |
21 |
22 |
23 | );
24 | };
25 |
26 | export default Header;
27 |
28 | //Styling
29 | const Container = styled.div`
30 | background-color: #a79292;
31 | display: flex;
32 | padding: 5px;
33 | align-items: center;
34 | position: relative;
35 | `;
36 | const MenuButton = styled.button`
37 | justify-self: flex-start;
38 | padding: 0px 0px 0px 10px;
39 | outline: none;
40 | background-color: #a79292;
41 | border: none;
42 | position: absolute;
43 | left: 2 rem;
44 |
45 | img {
46 | position: ${({ open }) => (open ? "fixed" : "relative")};
47 | fill: ${({ open }) => (open ? "#383636" : "#fff")};
48 | z-index: 50;
49 | }
50 | `;
51 |
52 | const Logo = styled.img`
53 | margin: auto;
54 | `;
55 |
--------------------------------------------------------------------------------
/client/src/pages/SignUpPage.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import SignUpForm from "../components/signUpForm/SignUpForm";
3 | import HeaderSignUp from "../components/headerSignUp/HeaderSignUp";
4 | import signUpPictureSrc from "../assets/signUpBg.png";
5 | import styled from "@emotion/styled";
6 |
7 | export const SignUpPage = () => {
8 | return (
9 |
10 |
11 |
12 |
13 | Willkommen bei
14 |
15 | Marry Me
16 |
17 |
18 | Melde dich an
19 |
20 |
21 | );
22 | };
23 |
24 | export default SignUpPage;
25 |
26 | // styling
27 | const Container = styled.div`
28 | display: flex;
29 | flex-direction: column;
30 | height: 100vh;
31 | form {
32 | padding: 3em 0;
33 | align-self: stretch;
34 | }
35 | h2 {
36 | padding-top: 1em;
37 | align-self: center;
38 | }
39 | `;
40 | const ImageContainer = styled.div`
41 | display: flex;
42 | padding: 3.1em 0;
43 | height: 100%;
44 | background: url(${signUpPictureSrc});
45 | background-repeat: no-repeat;
46 | background-position: 40% 60%;
47 | background-size: cover;
48 | background-position: right;
49 | align-items: center;
50 | justify-items: center;
51 |
52 | h1 {
53 | text-align: center;
54 | margin: auto;
55 | }
56 | `;
57 |
--------------------------------------------------------------------------------
/client/src/pages/AddListItem.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Modal from "../components/Modal";
3 | import styled from "@emotion/styled";
4 | import PropTypes from "prop-types";
5 | import closeIconSrc from "../assets/close.svg";
6 | import AddNewItemForm from "../components/addNewItemForm/AddNewItemForm";
7 |
8 | export default function AddListItem({
9 | handleCloseClick,
10 | guest,
11 | onSetShowModal,
12 | onRefetch,
13 | }) {
14 | return (
15 |
16 |
17 |
18 |
19 |
20 | Neuer Gast
21 |
26 |
27 |
28 | );
29 | }
30 |
31 | //styling
32 | const Container = styled.div`
33 | display: flex;
34 | flex-direction: column;
35 | align-items: center;
36 |
37 | h2 {
38 | padding-bottom: 2em;
39 | }
40 | `;
41 | const CloseButton = styled.button`
42 | background: transparent;
43 | border: none;
44 | align-self: flex-start;
45 | outline: none;
46 | cursor: pointer;
47 | `;
48 |
49 | AddListItem.propTypes = {
50 | handleCloseClick: PropTypes.func,
51 | guest: PropTypes.any,
52 | onSetShowModal: PropTypes.func,
53 | onRefetch: PropTypes.func,
54 | };
55 |
--------------------------------------------------------------------------------
/client/src/stories/assets/code-brackets.svg:
--------------------------------------------------------------------------------
1 | illustration/code-brackets
--------------------------------------------------------------------------------
/client/src/pages/CreateToDo.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "react-dropdown/style.css";
3 | import Form from "../components/form/Form";
4 | import Modal from "../components/Modal";
5 | import styled from "@emotion/styled";
6 | import PropTypes from "prop-types";
7 | import closeIconSrc from "../assets/close.svg";
8 |
9 | export default function CreateToDo({
10 | handleCloseClick,
11 | toDo,
12 | onSetShowModal,
13 | onRefetch,
14 | }) {
15 | return (
16 |
17 |
18 |
19 |
20 |
21 | Neues ToDo
22 |
27 |
28 |
29 | );
30 | }
31 |
32 | //styling
33 | const CreateToDoContainer = styled.div`
34 | display: flex;
35 | flex-direction: column;
36 | align-items: center;
37 |
38 | h2 {
39 | padding-bottom: 2em;
40 | }
41 | `;
42 | const CloseButton = styled.button`
43 | background: transparent;
44 | border: none;
45 | align-self: flex-start;
46 | outline: none;
47 | cursor: pointer;
48 | `;
49 |
50 | CreateToDo.propTypes = {
51 | handleCloseClick: PropTypes.func,
52 | toDo: PropTypes.any,
53 | onSetShowModal: PropTypes.func,
54 | onRefetch: PropTypes.func,
55 | };
56 |
--------------------------------------------------------------------------------
/client/src/pages/Home.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { getAccount } from "../apiAccount/fetchAccount";
3 | import Countdown from "../components/countDown/CountDown";
4 | import Header from "../components/Header/Header";
5 | import UserDataDate from "../components/userData/UserDataDate";
6 | import UserDataNames from "../components/userData/UserDataNames";
7 | import useAsync from "../hooks/useAsync";
8 | import bgImageSrc from "../assets/homeBgImage.png";
9 | import styled from "@emotion/styled";
10 |
11 | export default function Home() {
12 | const { data: account, error, loading } = useAsync(getAccount);
13 |
14 | return (
15 |
16 |
17 | {error && Error
}
18 | {loading && Loading ...
}
19 | {account && }
20 |
21 | {account && }
22 |
23 | );
24 | }
25 |
26 | // Styling
27 |
28 | const HomeContainer = styled.div`
29 | display: flex;
30 | flex-direction: column;
31 | height: 100vh;
32 | h1,
33 | p {
34 | margin-top: 0.5em;
35 | }
36 | section {
37 | display: flex;
38 | flex-direction: column;
39 | background-image: url(${bgImageSrc});
40 | background-repeat: no-repeat;
41 | background-position: center;
42 | height: 100%;
43 | div {
44 | margin: auto;
45 | }
46 | }
47 | `;
48 |
--------------------------------------------------------------------------------
/client/src/stories/assets/comments.svg:
--------------------------------------------------------------------------------
1 | illustration/comments
--------------------------------------------------------------------------------
/client/src/stories/assets/repo.svg:
--------------------------------------------------------------------------------
1 | illustration/repo
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@babel/core": "^7.11.6",
7 | "@emotion/core": "^10.0.35",
8 | "@emotion/styled": "^10.0.27",
9 | "@storybook/addon-actions": "^6.0.21",
10 | "@storybook/addon-essentials": "^6.0.21",
11 | "@storybook/addon-links": "^6.0.21",
12 | "@storybook/node-logger": "^6.0.21",
13 | "@storybook/preset-create-react-app": "^3.1.4",
14 | "@storybook/react": "^6.0.21",
15 | "@testing-library/jest-dom": "^4.2.4",
16 | "@testing-library/react": "^9.5.0",
17 | "@testing-library/user-event": "^7.2.1",
18 | "babel-loader": "^8.1.0",
19 | "prop-types": "^15.7.2",
20 | "react": "^16.13.1",
21 | "react-dom": "^16.13.1",
22 | "react-dropdown": "^1.8.0",
23 | "react-hook-form": "^6.9.0",
24 | "react-is": "^16.13.1",
25 | "react-router-dom": "^5.2.0",
26 | "react-scripts": "3.4.3"
27 | },
28 | "scripts": {
29 | "start": "react-scripts start",
30 | "build": "react-scripts build",
31 | "test": "react-scripts test",
32 | "eject": "react-scripts eject",
33 | "storybook": "start-storybook -p 6006 -s public",
34 | "build-storybook": "build-storybook -s public"
35 | },
36 | "browserslist": {
37 | "production": [
38 | ">0.2%",
39 | "not dead",
40 | "not op_mini all"
41 | ],
42 | "development": [
43 | "last 1 chrome version",
44 | "last 1 firefox version",
45 | "last 1 safari version"
46 | ]
47 | },
48 | "proxy": "http://localhost:3001",
49 | "devDependencies": {
50 | "mutationobserver-shim": "^0.3.7"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/client/src/components/listItem/ListItem.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import deleteIconSrc from "../../assets/delete.svg";
3 | import styled from "@emotion/styled";
4 | import PropTypes from "prop-types";
5 | import deleteGuest from "../../api/deleteGuest";
6 |
7 | export default function ListItem({
8 | guest,
9 | firstname,
10 | lastname,
11 | onRefetch,
12 | guestId,
13 | }) {
14 | async function handleDeleteGuest(event) {
15 | event.preventDefault();
16 | await deleteGuest(guestId);
17 | await onRefetch();
18 | }
19 |
20 | return (
21 |
22 |
23 | {firstname} {lastname}
24 |
25 |
26 |
27 |
28 |
29 | );
30 | }
31 |
32 | //Styling
33 |
34 | const Container = styled.li`
35 | display: flex;
36 | justify-content: space-between;
37 | align-items: center;
38 | padding: 1em;
39 | border-radius: 30px;
40 | margin: 5px;
41 | background-color: #ffffff;
42 | color: #141313;
43 | box-shadow: 2px 2px 6px 0px lightgrey;
44 | margin: 1em 1.7em;
45 |
46 | button {
47 | margin-right: 0.5em;
48 | display: flex;
49 | align-items: center;
50 | outline: none;
51 | border: none;
52 | cursor: pointer;
53 | color: grey;
54 | background-color: transparent;
55 | }
56 | p {
57 | padding-left: 0.7em;
58 | }
59 | `;
60 |
61 | ListItem.propTypes = {
62 | text: PropTypes.string,
63 | firstname: PropTypes.string,
64 | lastname: PropTypes.string,
65 | guest: PropTypes.array,
66 | onRefetch: PropTypes.func,
67 | guestId: PropTypes.any,
68 | };
69 |
--------------------------------------------------------------------------------
/client/src/stories/Header.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Button } from "./Button";
5 | import "./header.css";
6 |
7 | export const Header = ({ user, onLogin, onLogout, onCreateAccount }) => (
8 |
51 | );
52 |
53 | Header.propTypes = {
54 | user: PropTypes.shape({}),
55 | onLogin: PropTypes.func.isRequired,
56 | onLogout: PropTypes.func.isRequired,
57 | onCreateAccount: PropTypes.func.isRequired,
58 | };
59 |
60 | Header.defaultProps = {
61 | user: null,
62 | };
63 |
--------------------------------------------------------------------------------
/client/src/components/ToDoListItem/ToDoListItem.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "@emotion/styled";
3 | import PropTypes from "prop-types";
4 | import completedSrc from "../../assets/completed.svg";
5 | import uncompletedSrc from "../../assets/uncompleted.svg";
6 |
7 | const ToDoListItem = ({
8 | title,
9 | category,
10 | completed,
11 | onCompletedClick,
12 | onClick,
13 | }) => {
14 | return (
15 |
16 |
17 |
18 |
19 | {title}
20 | {category}
21 |
22 | );
23 | };
24 | export default ToDoListItem;
25 |
26 | //Styling
27 | const Button = styled.button`
28 | border-radius: 1.8em;
29 | background-color: #ffffff;
30 | color: #141313;
31 | padding: 0.9em 0.3em 0.3em 0.3em;
32 | display: grid;
33 | grid-template-rows: auto auto;
34 | grid-template-columns: 10% 1fr auto;
35 | outline: none;
36 | box-shadow: 2px 2px 6px 0px lightgrey;
37 | column-gap: 10px;
38 | cursor: pointer;
39 | border: none;
40 | `;
41 |
42 | const CompletedButton = styled.button`
43 | grid-row: 1/3;
44 | grid-column: 1/2;
45 | outline: none;
46 | border: none;
47 | background-color: #ffffff;
48 | align-self: center;
49 | `;
50 |
51 | const TitelSpan = styled.span`
52 | align-self: end;
53 | justify-self: start;
54 | `;
55 |
56 | const CategorySpan = styled.span`
57 | font-size: 0.9rem;
58 | padding-right: 10px;
59 | grid-row: 2/3;
60 | grid-column: 3/4;
61 | `;
62 |
63 | // Proptypes
64 | ToDoListItem.propTypes = {
65 | title: PropTypes.string,
66 | category: PropTypes.string,
67 | completed: PropTypes.bool,
68 | onCompletedClick: PropTypes.func,
69 | onClick: PropTypes.func,
70 | };
71 |
--------------------------------------------------------------------------------
/client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | Marry Me
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/db.json:
--------------------------------------------------------------------------------
1 | {
2 | "toDos": [
3 | {
4 | "id": 1,
5 | "title": "Angebot anfordern",
6 | "category": "Location",
7 | "completed": false
8 | },
9 | {
10 | "id": 2,
11 | "title": "Dj anrufen",
12 | "category": "Musik",
13 | "completed": false
14 | },
15 | {
16 | "id": 3,
17 | "title": "Kleid auswählen",
18 | "category": "Bekleidung",
19 | "completed": false
20 | },
21 | {
22 | "id": 4,
23 | "title": "Menü abstimmen",
24 | "category": "Location",
25 | "completed": false
26 | }
27 | ],
28 | "account": {
29 | "yourName": "Anna",
30 | "partnerName": "Max",
31 | "date": "2021-05-10"
32 | },
33 |
34 | "guests": [
35 | {
36 | "id": 1,
37 | "firstname": "Max",
38 | "lastname": "Mustermann"
39 | },
40 | {
41 | "id": 2,
42 | "firstname": "Max",
43 | "lastname": "Mustermann"
44 | },
45 | {
46 | "id": 3,
47 | "firstname": "Max",
48 | "lastname": "Mustermann"
49 | },
50 | {
51 | "id": 4,
52 | "firstname": "Max",
53 | "lastname": "Mustermann"
54 | },
55 | {
56 | "id": 5,
57 | "firstname": "Max",
58 | "lastname": "Mustermann"
59 | },
60 | {
61 | "id": 6,
62 | "firstname": "Max",
63 | "lastname": "Mustermann"
64 | },
65 | {
66 | "id": 7,
67 | "firstname": "Max",
68 | "lastname": "Mustermann"
69 | },
70 | {
71 | "id": 8,
72 | "firstname": "Max",
73 | "lastname": "Mustermann"
74 | },
75 | {
76 | "id": 9,
77 | "firstname": "Max",
78 | "lastname": "Mustermann"
79 | },
80 | {
81 | "id": 10,
82 | "firstname": "Max",
83 | "lastname": "Mustermann"
84 | },
85 | {
86 | "id": 11,
87 | "firstname": "Max",
88 | "lastname": "Mustermann"
89 | }
90 | ]
91 | }
92 |
--------------------------------------------------------------------------------
/client/src/components/addNewItemForm/AddNewItemForm.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import PropTypes from "prop-types";
3 | import styled from "@emotion/styled";
4 | import Input from "../form/Input";
5 | import Button from "../form/Button";
6 | import { postGuest } from "../../api/postGuest";
7 |
8 | const AddNewItem = ({ guest, onSetShowModal, onRefetch }) => {
9 | const [firstname, setFirstname] = useState("");
10 | const [lastname, setLastname] = useState("");
11 |
12 | async function handleSubmit(event) {
13 | event.preventDefault();
14 | const guest = { firstname, lastname };
15 | await postGuest(guest);
16 | onSetShowModal(false);
17 | await onRefetch();
18 | }
19 |
20 | return (
21 |
22 | {
27 | setFirstname(event.target.value);
28 | }}
29 | />
30 | {
35 | setLastname(event.target.value);
36 | }}
37 | />
38 | {/* */}
39 |
40 |
41 | );
42 | };
43 |
44 | export default AddNewItem;
45 |
46 | //styling
47 |
48 | const StyledForm = styled.form`
49 | display: flex;
50 | flex-direction: column;
51 | background-color: #cebebe;
52 | z-index: 2;
53 | width: 100%;
54 |
55 | button:first-of-type {
56 | align-self: flex-end;
57 | margin-top: 3em;
58 | padding-right: 2em;
59 | }
60 | button:last-of-type {
61 | margin: 3em 0px;
62 | align-self: center;
63 | }
64 |
65 | div:nth-child(3) {
66 | align-self: flex-end;
67 | }
68 | `;
69 |
70 | AddNewItem.propTypes = {
71 | guest: PropTypes.any,
72 | onSetShowModal: PropTypes.func,
73 | onRefetch: PropTypes.func,
74 | };
75 |
--------------------------------------------------------------------------------
/client/src/stories/assets/plugin.svg:
--------------------------------------------------------------------------------
1 | illustration/plugin
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "full-stack-template",
3 | "privat": true,
4 | "version": "1.0.0",
5 | "description": "Express server with CRA, ESLint, Git Hooks and Prettier",
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": "npm run lint && cd client && npm test",
11 | "prettify": "prettier --write .",
12 | "dev": "concurrently \"npm run server\" \"npm run client\"",
13 | "client": "cd client && npm start",
14 | "lint": "eslint . --ext .js",
15 | "server": "nodemon server.js",
16 | "storybook": "cd client && npm run storybook",
17 | "start": "node server.js"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/AlexRyzhkova/full-stack-template.git"
22 | },
23 | "keywords": [],
24 | "author": "Alexandra Ryzhkova",
25 | "license": "MIT",
26 | "bugs": {
27 | "url": "https://github.com/AlexRyzhkova/full-stack-template/issues"
28 | },
29 | "homepage": "https://github.com/AlexRyzhkova/full-stack-template#readme",
30 | "dependencies": {
31 | "express": "^4.17.1",
32 | "json-server": "^0.16.1"
33 | },
34 | "devDependencies": {
35 | "@typescript-eslint/eslint-plugin": "^2.34.0",
36 | "@typescript-eslint/parser": "^2.34.0",
37 | "babel-eslint": "^10.1.0",
38 | "concurrently": "^5.3.0",
39 | "eslint": "^6.8.0",
40 | "eslint-config-prettier": "^6.11.0",
41 | "eslint-config-react-app": "^5.2.1",
42 | "eslint-plugin-flowtype": "^4.7.0",
43 | "eslint-plugin-import": "^2.22.0",
44 | "eslint-plugin-jsx-a11y": "^6.3.1",
45 | "eslint-plugin-react": "^7.20.6",
46 | "eslint-plugin-react-hooks": "^2.5.1",
47 | "husky": "^4.2.5",
48 | "lint-staged": "^10.3.0",
49 | "nodemon": "^2.0.4",
50 | "prettier": "^2.1.1"
51 | },
52 | "husky": {
53 | "hooks": {
54 | "pre-commit": "lint-staged",
55 | "pre-push": "CI=true npm test"
56 | }
57 | },
58 | "lint-staged": {
59 | "*.js": "eslint --cache --fix",
60 | "*.{js,css,md,json}": "prettier --write"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/.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 | storybook-static/
--------------------------------------------------------------------------------
/client/src/GlobalStyles.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Global, css } from "@emotion/core";
3 | import Roboto from "./fonts/roboto-regular.ttf";
4 | import Lora from "./fonts/lora-mediumItalic.ttf";
5 |
6 | const GlobalStyles = () => {
7 | return (
8 |
77 | );
78 | };
79 |
80 | export default GlobalStyles;
81 |
--------------------------------------------------------------------------------
/client/src/stories/assets/stackalt.svg:
--------------------------------------------------------------------------------
1 | illustration/stackalt
--------------------------------------------------------------------------------
/client/src/components/signUpForm/SignUpForm.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import styled from "@emotion/styled";
3 | import { useForm } from "react-hook-form";
4 | import SignUpButton from "../signUpButton/SignUpButton";
5 | import postAccount from "../../apiAccount/postAccount";
6 | import { useHistory } from "react-router-dom";
7 |
8 | const SignUpForm = () => {
9 | const { register, handleSubmit, errors } = useForm();
10 | const history = useHistory();
11 |
12 | const onSubmit = async (userData) => {
13 | await postAccount(userData);
14 | history.push("/main");
15 | };
16 |
17 | return (
18 |
55 |
56 | );
57 | };
58 | export default SignUpForm;
59 |
60 | //styling
61 |
62 | const Form = styled.div`
63 | form {
64 | display: flex;
65 | flex-direction: column;
66 | height: 100%;
67 | justify-content: center;
68 | }
69 | section {
70 | display: flex;
71 | justify-content: space-between;
72 | align-items: center;
73 | padding: 1em;
74 | label {
75 | margin-left: 2em;
76 | }
77 |
78 | input {
79 | margin-right: 3em;
80 | outline: none;
81 | border: none;
82 | border-bottom: 1px solid #383636;
83 | background-color: transparent;
84 | color: grey;
85 | }
86 | }
87 | button {
88 | margin-top: 2em;
89 | align-self: center;
90 | }
91 | `;
92 |
--------------------------------------------------------------------------------
/client/src/pages/GuestsPage.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { getGuests } from "../api/fetchGuests";
3 | import Header from "../components/Header/Header";
4 | import ItemsList from "../components/IitemsList/ItemsList";
5 | import ListItem from "../components/listItem/ListItem";
6 | import useAsync from "../hooks/useAsync";
7 | import styled from "@emotion/styled";
8 | import addNewIconSrc from "../assets/addNew.svg";
9 | import AddListItem from "./AddListItem";
10 | import PropTypes from "prop-types";
11 | import bubbleSrc from "../assets/bubble.svg";
12 | import bubble1Src from "../assets/bubble1.svg";
13 |
14 | export default function GuestsPage({ onClick, onRefetch }) {
15 | const { data: guests, refetch } = useAsync(getGuests);
16 |
17 | const [showModal, setShowModal] = useState(false);
18 | const [guest, setGuest] = useState(null);
19 |
20 | const handleCloseClick = () => {
21 | setShowModal(false);
22 | setGuest(null);
23 | };
24 |
25 | return (
26 |
27 |
28 | {showModal && (
29 |
35 | )}
36 | Gästeliste
37 |
38 | {guests?.map((guest) => (
39 |
46 | ))}
47 |
48 | setShowModal(true)}>
49 |
50 |
51 |
52 | );
53 | }
54 |
55 | //styling
56 | const Container = styled.div`
57 | background-image: url(${bubbleSrc}), url(${bubble1Src});
58 | background-repeat: no-repeat, no-repeat;
59 | background-position: 480% 30%, 100px 500px;
60 | background-size: auto, auto;
61 | display: flex;
62 | flex-direction: column;
63 | max-height: 100vh;
64 | overflow: auto;
65 | position: relative;
66 | height: 100%;
67 |
68 | div:first-child {
69 | position: sticky;
70 | top: 0;
71 | }
72 | h2 {
73 | text-align: center;
74 | margin-top: 0.5em;
75 | }
76 | `;
77 |
78 | const OpenButton = styled.button`
79 | align-self: flex-end;
80 | background-color: transparent;
81 | border: none;
82 | outline: none;
83 | cursor: pointer;
84 | position: sticky;
85 | bottom: 0;
86 | padding: 1em;
87 | `;
88 |
89 | GuestsPage.propTypes = {
90 | onClick: PropTypes.func,
91 | onRefetch: PropTypes.func,
92 | };
93 |
--------------------------------------------------------------------------------
/client/src/pages/ToDoPage.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import Header from "../components/Header/Header";
3 | import List from "../components/List/List";
4 | import filterIconSrc from "../assets/filter.svg";
5 | import addNewIconSrc from "../assets/addNew.svg";
6 | import styled from "@emotion/styled";
7 | import { getToDo, getTodos } from "../api/fetchToDos";
8 | import CreateToDo from "./CreateToDo";
9 | import useAsync from "../hooks/useAsync";
10 | import bubble1Src from "../assets/bubble1.svg";
11 | import bubbleSrc from "../assets/bubble.svg";
12 |
13 | export const ToDoPage = () => {
14 | const [showModal, setShowModal] = useState(false);
15 | const [toDo, setToDo] = useState(null);
16 | const { data: toDos, error, loading, refetch } = useAsync(getTodos);
17 |
18 | const handleCloseClick = () => {
19 | setShowModal(false);
20 | setToDo(null);
21 | };
22 |
23 | const showDetailModal = async (id) => {
24 | const toDoDetails = await getToDo(id);
25 | setToDo(toDoDetails);
26 | console.log(toDoDetails);
27 | setShowModal(true);
28 | };
29 |
30 | return (
31 |
32 |
33 |
34 | {showModal && (
35 |
41 | )}
42 | To-Do Liste
43 |
44 | showDetailModal(id)}
47 | error={error}
48 | loading={loading}
49 | />
50 | setShowModal(true)}>
51 |
52 |
53 |
54 |
55 | );
56 | };
57 |
58 | //styling
59 | const Container = styled.div`
60 | display: flex;
61 | flex-direction: column;
62 | position: relative;
63 | max-height: 100vh;
64 | overflow: auto;
65 | background-image: url(${bubbleSrc}), url(${bubble1Src});
66 | background-repeat: no-repeat, no-repeat;
67 | background-position: 500% 30%, 200px 400px;
68 |
69 | div:first-child {
70 | position: sticky;
71 | top: 0;
72 | }
73 | `;
74 |
75 | const Main = styled.div`
76 | display: flex;
77 | flex-direction: column;
78 | /* flex-grow: 1; */
79 |
80 | h2 {
81 | padding: 20px 0 15px 0;
82 | align-self: center;
83 | }
84 |
85 | img {
86 | align-self: flex-end;
87 | padding: 0 20px 10px 0;
88 | }
89 | `;
90 |
91 | const OpenButton = styled.button`
92 | align-self: flex-end;
93 | background-color: transparent;
94 | border: none;
95 | outline: none;
96 | position: sticky;
97 | bottom: 0;
98 | padding: 1em;
99 | `;
100 |
--------------------------------------------------------------------------------
/client/src/components/form/Form.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import PropTypes from "prop-types";
3 | import styled from "@emotion/styled";
4 | import Input from "./Input";
5 | import Button from "./Button";
6 | import DropDown from "./DropDown";
7 | import { postTodo } from "../../api/postToDo";
8 | import DeleteButton from "../DeleteButton";
9 | import { deleteTodo } from "../../api/deleteToDo";
10 |
11 | const Form = ({
12 | // topic,
13 | // placeholder,
14 | // toDoId,
15 | toDo,
16 | onSetShowModal,
17 | onRefetch,
18 | }) => {
19 | const [title, setTitle] = useState("");
20 | const [description, setDescription] = useState("");
21 | const [category, setCategory] = useState("");
22 | // const [loading, setLoading] = useState(false);
23 |
24 | async function handleSubmit(event) {
25 | event.preventDefault();
26 | const toDo = { title, category, description };
27 | // setLoading(true);
28 | await postTodo(toDo);
29 | onSetShowModal(false);
30 | await onRefetch();
31 | }
32 |
33 | function handleCategoryChange(category) {
34 | setCategory(category.value);
35 | }
36 |
37 | async function handleDeleteToDo(event) {
38 | event.preventDefault();
39 | await deleteTodo(toDo.id);
40 | await onRefetch();
41 | }
42 |
43 | return (
44 |
45 | {
50 | setTitle(event.target.value);
51 | }}
52 | />
53 | {
58 | setDescription(event.target.value);
59 | }}
60 | />
61 |
65 |
66 |
67 |
68 | );
69 | };
70 |
71 | export default Form;
72 |
73 | //styling
74 |
75 | const StyledForm = styled.form`
76 | display: flex;
77 | flex-direction: column;
78 | background-color: #cebebe;
79 | z-index: 2;
80 | width: 100%;
81 |
82 | button:first-of-type {
83 | align-self: flex-end;
84 | margin-top: 3em;
85 | padding-right: 2em;
86 | }
87 | button:last-of-type {
88 | margin: 3em 0px;
89 | align-self: center;
90 | }
91 |
92 | div:nth-child(3) {
93 | align-self: flex-end;
94 | }
95 | `;
96 |
97 | Form.propTypes = {
98 | placeholder: PropTypes.string,
99 | topic: PropTypes.string,
100 | category: PropTypes.string,
101 | toDoId: PropTypes.any,
102 | toDo: PropTypes.any,
103 | handleDeleteToDo: PropTypes.func,
104 | onSetShowModal: PropTypes.func,
105 | onRefetch: PropTypes.func,
106 | };
107 |
--------------------------------------------------------------------------------
/client/src/stories/Page.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 |
4 | import { Header } from "./Header";
5 | import "./page.css";
6 |
7 | export const Page = ({ user, onLogin, onLogout, onCreateAccount }) => (
8 |
9 |
15 |
16 |
17 | Pages in Storybook
18 |
19 | We recommend building UIs with a{" "}
20 |
25 | component-driven
26 | {" "}
27 | process starting with atomic components and ending with pages.
28 |
29 |
30 | Render pages with mock data. This makes it easy to build and review page
31 | states without needing to navigate to them in your app. Here are some
32 | handy patterns for managing page data in Storybook:
33 |
34 |
35 |
36 | Use a higher-level connected component. Storybook helps you compose
37 | such data from the `"`args`"` of child component stories
38 |
39 |
40 | Assemble data in the page component from your services. You can mock
41 | these services out using Storybook.
42 |
43 |
44 |
45 | Get a guided tutorial on component-driven development at{" "}
46 |
51 | Learn Storybook
52 |
53 | . Read more in the{" "}
54 |
59 | docs
60 |
61 | .
62 |
63 |
64 |
Tip Adjust the width of the canvas with the{" "}
65 |
71 |
72 |
77 |
78 |
79 | Viewports addon in the toolbar
80 |
81 |
82 |
83 | );
84 | Page.propTypes = {
85 | user: PropTypes.shape({}),
86 | onLogin: PropTypes.func.isRequired,
87 | onLogout: PropTypes.func.isRequired,
88 | onCreateAccount: PropTypes.func.isRequired,
89 | };
90 |
91 | Page.defaultProps = {
92 | user: null,
93 | };
94 |
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | ## Available Scripts
4 |
5 | In the project directory, you can run:
6 |
7 | ### `npm start`
8 |
9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
11 |
12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console.
14 |
15 | ### `npm test`
16 |
17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
19 |
20 | ### `npm run build`
21 |
22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance.
24 |
25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed!
27 |
28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
29 |
30 | ### `npm run eject`
31 |
32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
33 |
34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
35 |
36 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
37 |
38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
39 |
40 | ## Learn More
41 |
42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
43 |
44 | To learn React, check out the [React documentation](https://reactjs.org/).
45 |
46 | ### Code Splitting
47 |
48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
49 |
50 | ### Analyzing the Bundle Size
51 |
52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
53 |
54 | ### Making a Progressive Web App
55 |
56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
57 |
58 | ### Advanced Configuration
59 |
60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
61 |
62 | ### Deployment
63 |
64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
65 |
66 | ### `npm run build` fails to minify
67 |
68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
69 |
--------------------------------------------------------------------------------
/client/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === "localhost" ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === "[::1]" ||
17 | // 127.0.0.0/8 are considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener("load", () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | "This web app is being served cache-first by a service " +
46 | "worker. To learn more, visit https://bit.ly/CRA-PWA"
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then((registration) => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === "installed") {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | "New content is available and will be used when all " +
74 | "tabs for this page are closed. See https://bit.ly/CRA-PWA."
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log("Content is cached for offline use.");
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch((error) => {
97 | console.error("Error during service worker registration:", error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl, {
104 | headers: { "Service-Worker": "script" },
105 | })
106 | .then((response) => {
107 | // Ensure service worker exists, and that we really are getting a JS file.
108 | const contentType = response.headers.get("content-type");
109 | if (
110 | response.status === 404 ||
111 | (contentType != null && contentType.indexOf("javascript") === -1)
112 | ) {
113 | // No service worker found. Probably a different app. Reload the page.
114 | navigator.serviceWorker.ready.then((registration) => {
115 | registration.unregister().then(() => {
116 | window.location.reload();
117 | });
118 | });
119 | } else {
120 | // Service worker found. Proceed as normal.
121 | registerValidSW(swUrl, config);
122 | }
123 | })
124 | .catch(() => {
125 | console.log(
126 | "No internet connection found. App is running in offline mode."
127 | );
128 | });
129 | }
130 |
131 | export function unregister() {
132 | if ("serviceWorker" in navigator) {
133 | navigator.serviceWorker.ready
134 | .then((registration) => {
135 | registration.unregister();
136 | })
137 | .catch((error) => {
138 | console.error(error.message);
139 | });
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/client/src/stories/Introduction.stories.mdx:
--------------------------------------------------------------------------------
1 | import { Meta } from '@storybook/addon-docs/blocks';
2 | import Code from './assets/code-brackets.svg';
3 | import Colors from './assets/colors.svg';
4 | import Comments from './assets/comments.svg';
5 | import Direction from './assets/direction.svg';
6 | import Flow from './assets/flow.svg';
7 | import Plugin from './assets/plugin.svg';
8 | import Repo from './assets/repo.svg';
9 | import StackAlt from './assets/stackalt.svg';
10 |
11 |
12 |
13 |
116 |
117 | # Welcome to Storybook
118 |
119 | Storybook helps you build UI components in isolation from your app's business logic, data, and context.
120 | That makes it easy to develop hard-to-reach states. Save these UI states as **stories** to revisit during development, testing, or QA.
121 |
122 | Browse example stories now by navigating to them in the sidebar.
123 | View their code in the `src/storybook-examples` directory to learn how they work.
124 | We recommend building UIs with a [**component-driven**](https://componentdriven.org) process starting with atomic components and ending with pages.
125 |
126 | Configure
127 |
128 |
158 |
159 | Learn
160 |
161 |
191 |
192 |
193 | Tip Edit the Markdown in src/storybook-examples/welcome.mdx
194 |
195 |
--------------------------------------------------------------------------------
/client/src/stories/assets/colors.svg:
--------------------------------------------------------------------------------
1 | illustration/colors
--------------------------------------------------------------------------------
/client/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/src/assets/logo_Readme.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------