Login / Register / SideMenu / Fixes

This commit is contained in:
2020-08-09 17:50:53 +02:00
parent e5a68f4b85
commit 769be397e5
19 changed files with 960 additions and 160 deletions

1
.gitignore vendored
View File

@@ -13,6 +13,7 @@
# misc # misc
.DS_Store .DS_Store
.env
.env.local .env.local
.env.development.local .env.development.local
.env.test.local .env.test.local

7
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "web_client", "name": "web_client",
"version": "0.1.0", "version": "0.3.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -13624,6 +13624,11 @@
"spdx-expression-parse": "^3.0.0" "spdx-expression-parse": "^3.0.0"
} }
}, },
"validator": {
"version": "13.1.1",
"resolved": "https://registry.npmjs.org/validator/-/validator-13.1.1.tgz",
"integrity": "sha512-8GfPiwzzRoWTg7OV1zva1KvrSemuMkv07MA9TTl91hfhe+wKrsrgVN4H2QSFd/U/FhiU3iWPYVgvbsOGwhyFWw=="
},
"vary": { "vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

View File

@@ -1,6 +1,6 @@
{ {
"name": "web_client", "name": "web_client",
"version": "0.1.0", "version": "0.3.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@material-ui/core": "^4.11.0", "@material-ui/core": "^4.11.0",
@@ -16,7 +16,8 @@
"react-redux": "^7.2.0", "react-redux": "^7.2.0",
"react-scripts": "3.4.1", "react-scripts": "3.4.1",
"redux": "^4.0.5", "redux": "^4.0.5",
"redux-thunk": "^2.3.0" "redux-thunk": "^2.3.0",
"validator": "^13.1.1"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",

View File

@@ -13,7 +13,7 @@ import { useSelector } from "react-redux";
const theme = createMuiTheme({ const theme = createMuiTheme({
palette: { palette: {
primary: { primary: {
main: "#0e8496", main: "#d68000",
}, },
secondary: { secondary: {
light: "#ffffff", light: "#ffffff",

View File

@@ -1,5 +1,6 @@
import axios from "axios"; import axios from "axios";
import * as toggles from "./toggles"; import * as toggles from "./toggles";
const backend = "http://localhost:4000/";
const autocomplete = (input) => { const autocomplete = (input) => {
return { return {
@@ -17,9 +18,7 @@ export const clearAutocomplete = () => {
export const fetchAutocomplete = (input) => { export const fetchAutocomplete = (input) => {
return function (dispatch) { return function (dispatch) {
axios axios
.get( .get(backend + "search/autocomplete?string=" + encodeURI(input))
"http://localhost:4000/search/autocomplete?string=" + encodeURI(input)
)
.then((response) => { .then((response) => {
const cities = Array.from(response.data.cities); const cities = Array.from(response.data.cities);
const restaurants = Array.from(response.data.restaurants); const restaurants = Array.from(response.data.restaurants);
@@ -36,7 +35,7 @@ export const fetchAutocomplete = (input) => {
export const fetchSearch = (input) => { export const fetchSearch = (input) => {
return function (dispatch) { return function (dispatch) {
axios axios
.get("http://localhost:4000/search?string=" + encodeURI(input)) .get(backend + "search?string=" + encodeURI(input))
.then((response) => { .then((response) => {
const data = response.data; const data = response.data;
if (Object.keys(data).length > 0) { if (Object.keys(data).length > 0) {
@@ -80,7 +79,7 @@ export const setRestaurant = (restaurant) => {
export const fetchRestaurant = (id) => { export const fetchRestaurant = (id) => {
return function (dispatch) { return function (dispatch) {
axios axios
.get("http://localhost:4000/restaurant?restaurantId=" + id) .get(backend + "restaurant?restaurantId=" + id)
.then((response) => { .then((response) => {
dispatch(setRestaurant(response.data)); dispatch(setRestaurant(response.data));
dispatch(toggles.hideDishes()); dispatch(toggles.hideDishes());
@@ -101,7 +100,7 @@ const setDishes = (data) => {
export const fetchAllDishes = (id) => { export const fetchAllDishes = (id) => {
return function (dispatch) { return function (dispatch) {
axios axios
.get("http://localhost:4000/restaurant/dishes?restaurantId=" + id) .get(backend + "restaurant/dishes?restaurantId=" + id)
.then((response) => { .then((response) => {
dispatch(setDishes(response.data)); dispatch(setDishes(response.data));
dispatch(toggles.showDishes()); dispatch(toggles.showDishes());
@@ -109,10 +108,38 @@ export const fetchAllDishes = (id) => {
}; };
}; };
export const tryLogin = (username) => { export const tryLogin = (username, password) => {
const data = { email: username, password: password };
return function (dispatch) { return function (dispatch) {
dispatch(toggles.setLoggedIn(username)); axios
.post(backend + "user/login", data)
.then((response) => {
const jwt = response.headers["x-auth-token"];
console.log(response.headers);
dispatch(
toggles.setLoggedIn(
response.data.firstname,
jwt,
response.data.id,
response.data.email
)
);
dispatch(toggles.hideLoginDialog()); dispatch(toggles.hideLoginDialog());
})
.catch((err) => {
if (err.response.status === 404) {
dispatch(
toggles.setLoginResult(
"Użytkownik o podanym adresie email nie istnieje."
)
);
} else if (err.response.status === 401) {
dispatch(toggles.setLoginResult("Podane hasło jest nieprawidłowe"));
} else {
dispatch(toggles.setLoginResult("Błąd serwera..."));
}
});
}; };
}; };
@@ -121,3 +148,39 @@ export const logout = () => {
dispatch(toggles.setLoggedOut()); dispatch(toggles.setLoggedOut());
}; };
}; };
export const tryRegister = (data) => {
return function (dispatch) {
dispatch(toggles.showRegisterCircle());
axios
.post(backend + "user/register", data)
.then((response) => {
if (response.status === 201) {
dispatch(
toggles.setRegisterResult(
"Dziękujemy za rejestrację. Teraz możesz zalogować się do swojego konta."
)
);
dispatch(toggles.hideRegisterCircle());
dispatch(toggles.hideRegisterForm());
}
})
.catch((err) => {
if (err.response.status === 500) {
dispatch(
toggles.setRegisterResult(
"Wystąpił nieoczekiwany błąd serwera, przepraszamy..."
)
);
dispatch(toggles.hideRegisterCircle());
} else if (err.response.status === 409) {
dispatch(
toggles.setRegisterResult(
"Adres email jest już zajęty, proszę użyć innego."
)
);
dispatch(toggles.hideRegisterCircle());
}
});
};
};

View File

@@ -22,10 +22,10 @@ export const hideLoginDialog = () => {
}; };
}; };
export const setLoggedIn = (username) => { export const setLoggedIn = (username, jwt, id, email) => {
return { return {
type: "SET_LOGGEDIN", type: "SET_LOGGEDIN",
payload: username, payload: { username: username, jwt: jwt, id: id, email: email },
}; };
}; };
@@ -34,3 +34,59 @@ export const setLoggedOut = () => {
type: "SET_LOGGEDOUT", type: "SET_LOGGEDOUT",
}; };
}; };
export const showRegisterDialog = () => {
return {
type: "DIALOG_REGISTER_VISIBLE",
};
};
export const hideRegisterDialog = () => {
return {
type: "DIALOG_REGISTER_HIDDEN",
};
};
export const showRegulaminDialog = () => {
return {
type: "DIALOG_REGULAMIN_VISIBLE",
};
};
export const hideRegulaminDialog = () => {
return {
type: "DIALOG_REGULAMIN_HIDDEN",
};
};
export const showRegisterCircle = () => {
return {
type: "DIALOG_REGISTER_CIRCLE_SHOW",
};
};
export const hideRegisterCircle = () => {
return {
type: "DIALOG_REGISTER_CIRCLE_HIDE",
};
};
export const hideRegisterForm = () => {
return {
type: "DIALOG_REGISTER_FORM_HIDE",
};
};
export const setRegisterResult = (text) => {
return {
type: "DIALOG_REGISTER_SET_RESULT",
payload: text,
};
};
export const setLoginResult = (text) => {
return {
type: "DIALOG_LOGIN_SET_RESULT",
payload: text,
};
};

View File

@@ -4,13 +4,13 @@ import Button from "@material-ui/core/Button";
const StylizedButton = withStyles({ const StylizedButton = withStyles({
root: { root: {
background: "#01c3a9", background: "#d68000",
color: "#262626", color: "#262626",
margin: "16px 16px 16px 16px", margin: "16px 16px 16px 16px",
padding: "8px 16px 8px 16px", padding: "8px 16px 8px 16px",
borderColor: "white", borderColor: "white",
"&:hover": { "&:hover": {
backgroundColor: "#00af98", backgroundColor: "#b46c00",
}, },
}, },
label: { label: {

View File

@@ -7,10 +7,10 @@ export default function CardDish(props) {
name, name,
price, price,
imgUrl, imgUrl,
notes, //notes,
weight, weight,
vegan, //vegan,
vegetarian, //vegetarian,
allergens, allergens,
} = props.dish; } = props.dish;

View File

@@ -1,98 +1,14 @@
import React from "react"; import React from "react";
import { makeStyles } from "@material-ui/core/styles"; import LoginDialog from "./Dialogs/LoginDialog";
import DialogTitle from "@material-ui/core/DialogTitle"; import RegisterDialog from "./Dialogs/RegisterDialog";
import DialogContent from "@material-ui/core/DialogContent"; import RegulaminDialog from "./Dialogs/RegulaminDialog";
import Dialog from "@material-ui/core/Dialog";
import Divider from "@material-ui/core/Divider";
import ButtonSecondary from "./ButtonSecondary";
import IconButton from "@material-ui/core/IconButton";
import TextField from "@material-ui/core/TextField";
import CloseIcon from "@material-ui/icons/Close";
import { useSelector, useDispatch } from "react-redux";
import { hideLoginDialog } from "../actions/toggles";
import { tryLogin } from "../actions";
export default function (props) { export default function (props) {
var loginDialog = useSelector((state) => state.data.dialogs.logIn);
const dispatch = useDispatch();
const loginStyles = makeStyles((theme) => ({
root: {
textAlign: "center",
"& .MuiPaper-root": {
backgroundColor: "#262626",
color: "#bbbbbb",
},
},
closeButton: {
color: "#bbbbbb",
position: "absolute",
right: theme.spacing(1),
top: theme.spacing(1),
},
textInput: {
marginTop: "20px",
marginBottom: "10px",
width: "90%",
"& .MuiInputBase-root": {
color: "#01c3a9",
},
"& .MuiInputLabel-root": {
color: "#bbbbbb",
},
},
}));
const loginClass = loginStyles();
return ( return (
<div> <div>
<Dialog <LoginDialog />
className={loginClass.root} <RegisterDialog />
onClose={() => dispatch(hideLoginDialog())} <RegulaminDialog />
open={loginDialog}
aria-labelledby="login-title"
>
<DialogTitle id="login-title">Logowanie</DialogTitle>
<IconButton
className={loginClass.closeButton}
onClick={() => dispatch(hideLoginDialog())}
aria-label="close"
>
<CloseIcon />
</IconButton>
<Divider />
<DialogContent>
<TextField
className={loginClass.textInput}
required
id="email"
label="Email"
type="email"
variant="outlined"
/>
<TextField
className={loginClass.textInput}
required
id="password"
label="Hasło"
type="password"
variant="outlined"
/>
<div className="login-dialog-buttons">
<ButtonSecondary
onClick={() => dispatch(tryLogin("jonasz@bankai.pl"))}
text="Zaloguj"
/>
</div>
<p>
Nie masz konta?
<span>
<p>Zarejestruj się.</p>
</span>
</p>
</DialogContent>
</Dialog>
</div> </div>
); );
} }

View File

@@ -0,0 +1,174 @@
import React, { useState } from "react";
import { makeStyles } from "@material-ui/core/styles";
import DialogTitle from "@material-ui/core/DialogTitle";
import DialogContent from "@material-ui/core/DialogContent";
import Dialog from "@material-ui/core/Dialog";
import Divider from "@material-ui/core/Divider";
import ButtonSecondary from ".././ButtonSecondary";
import IconButton from "@material-ui/core/IconButton";
import TextField from "@material-ui/core/TextField";
import CloseIcon from "@material-ui/icons/Close";
import { useSelector, useDispatch } from "react-redux";
import {
hideLoginDialog,
showRegisterDialog,
setLoginResult,
} from "../../actions/toggles";
import Link from "@material-ui/core/Link";
import LockIcon from "@material-ui/icons/Lock";
import validator from "validator";
import InputAdornment from "@material-ui/core/InputAdornment";
import AccountCircle from "@material-ui/icons/AccountCircle";
import { tryLogin } from "../../actions";
export default function LoginDialog(props) {
const initialData = {
email: "",
password: "",
emailError: false,
passwordError: false,
};
const [loginData, setLoginData] = useState(initialData);
const loginDialog = useSelector((state) => state.data.dialogs.logIn);
const loginResult = useSelector((state) => state.data.dialogs.loginResult);
const dispatch = useDispatch();
const loginStyles = makeStyles((theme) => ({
root: {
textAlign: "center",
"& .MuiPaper-root": {
backgroundColor: "#262626",
color: "#bbbbbb",
},
},
closeButton: {
color: "#bbbbbb",
position: "absolute",
right: theme.spacing(1),
top: theme.spacing(1),
},
textInput: {
marginTop: "20px",
marginBottom: "10px",
width: "90%",
"& .MuiInputBase-root": {
color: "#01c3a9",
},
"& .MuiInputLabel-root": {
color: "#bbbbbb",
},
},
link: {
fontSize: "0.9rem",
},
}));
const handleRegisterClick = () => {
dispatch(hideLoginDialog());
dispatch(showRegisterDialog());
};
const loginClass = loginStyles();
const validateLogin = () => {
var valid;
var validation = {
email: validator.isEmail(loginData.email),
password: !validator.isEmpty(loginData.password),
};
setLoginData({
...loginData,
emailError: !validation.email,
passwordError: !validation.password,
});
valid = validation.email && validation.password;
return valid;
};
const handleLogin = () => {
if (validateLogin()) {
dispatch(tryLogin(loginData.email, loginData.password));
} else {
dispatch(setLoginResult("Podaj poprawne dane logowania."));
}
};
// CODE
return (
<div>
<Dialog
className={loginClass.root}
onClose={() => dispatch(hideLoginDialog())}
open={loginDialog}
aria-labelledby="login-title"
>
<DialogTitle id="login-title">Logowanie</DialogTitle>
<IconButton
className={loginClass.closeButton}
onClick={() => dispatch(hideLoginDialog())}
aria-label="close"
>
<CloseIcon />
</IconButton>
<Divider />
<DialogContent>
<TextField
className={loginClass.textInput}
required
id="email"
label="Email"
type="email"
variant="outlined"
error={loginData.emailError}
onChange={(event) => (loginData.email = event.target.value)}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<AccountCircle color="primary" />
</InputAdornment>
),
}}
/>
<TextField
className={loginClass.textInput}
required
id="password"
label="Hasło"
type="password"
variant="outlined"
error={loginData.passwordError}
onChange={(event) => (loginData.password = event.target.value)}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<LockIcon color="primary" />
</InputAdornment>
),
}}
/>
<p>{loginResult}</p>
<div className="login-dialog-buttons">
<ButtonSecondary onClick={() => handleLogin()} text="Zaloguj" />
</div>
<Link
className={loginClass.link}
href="#"
onClick={() => handleRegisterClick()}
>
Nie pamiętam hasła.
</Link>
<p>
Nie masz konta?{" "}
<span>
<Link href="#" onClick={() => handleRegisterClick()}>
Zarejestruj się.
</Link>
</span>
</p>
</DialogContent>
</Dialog>
</div>
);
}

View File

@@ -0,0 +1,281 @@
import React, { useState } from "react";
import { makeStyles } from "@material-ui/core/styles";
import DialogTitle from "@material-ui/core/DialogTitle";
import DialogContent from "@material-ui/core/DialogContent";
import Dialog from "@material-ui/core/Dialog";
import Divider from "@material-ui/core/Divider";
import ButtonSecondary from ".././ButtonSecondary";
import IconButton from "@material-ui/core/IconButton";
import TextField from "@material-ui/core/TextField";
import CloseIcon from "@material-ui/icons/Close";
import { useSelector, useDispatch } from "react-redux";
import {
hideRegisterDialog,
showRegulaminDialog,
setRegisterResult,
showLoginDialog,
} from "../../actions/toggles";
import { tryRegister } from "../../actions";
import InputAdornment from "@material-ui/core/InputAdornment";
import AccountCircle from "@material-ui/icons/AccountCircle";
import EmailIcon from "@material-ui/icons/Email";
import LockIcon from "@material-ui/icons/Lock";
import Link from "@material-ui/core/Link";
import validator from "validator";
import CircularProgress from "@material-ui/core/CircularProgress";
export default function RegisterDialog(props) {
// SETUP
const initialFormData = {
firstname: "",
lastname: "",
email: "",
password: "",
repeatPassword: "",
firstnameError: false,
lastnameError: false,
emailError: false,
passwordError: false,
repeatPasswordError: false,
};
const [formData, setFormData] = useState(initialFormData);
var registerDialog = useSelector((state) => state.data.dialogs.register);
var circularProgress = useSelector(
(state) => state.data.dialogs.registerCircularProgress
);
var registerForm = useSelector((state) => state.data.dialogs.registerForm);
var registerResult = useSelector(
(state) => state.data.dialogs.registerResult
);
const dispatch = useDispatch();
// STYLES
const loginStyles = makeStyles((theme) => ({
root: {
textAlign: "center",
"& .MuiPaper-root": {
backgroundColor: "#262626",
color: "#bbbbbb",
},
"& .MuiFormHelperText-root": {
color: "#606060",
textAlign: "center",
},
},
closeButton: {
color: "#bbbbbb",
position: "absolute",
right: theme.spacing(1),
top: theme.spacing(1),
},
textInput: {
marginTop: "20px",
marginBottom: "10px",
width: "90%",
"& .MuiInputBase-root": {
color: "#01c3a9",
},
"& .MuiInputLabel-root": {
color: "#bbbbbb",
},
},
}));
const loginClass = loginStyles();
// HANDLERS
const handleRegulaminClick = (event) => {
event.preventDefault();
dispatch(showRegulaminDialog());
};
const validateForm = () => {
var valid;
const validations = {
firstname: !validator.isEmpty(formData.firstname),
lastname: !validator.isEmpty(formData.lastname),
email: validator.isEmail(formData.email),
password: validator.isLength(formData.password, {
min: 8,
max: 128,
}),
repeatPassword: formData.password === formData.repeatPassword,
};
setFormData({
...formData,
firstnameError: !validations.firstname,
lastnameError: !validations.lastname,
emailError: !validations.email,
passwordError: !validations.password,
repeatPasswordError: !validations.repeatPassword,
});
valid =
validations.firstname &&
validations.lastname &&
validations.email &&
validations.password &&
validations.repeatPassword;
return valid;
};
const sendForm = (form) => {
if (validateForm()) {
dispatch(tryRegister(form));
} else {
dispatch(setRegisterResult("Proszę poprawić poprawić formularz."));
}
};
const openLogin = () => {
dispatch(hideRegisterDialog());
dispatch(showLoginDialog());
};
// CODE
return (
<div>
<Dialog
className={loginClass.root}
onClose={() => dispatch(hideRegisterDialog())}
open={registerDialog}
aria-labelledby="login-title"
>
<DialogTitle id="login-title">Rejestracja</DialogTitle>
<IconButton
className={loginClass.closeButton}
onClick={() => dispatch(hideRegisterDialog())}
aria-label="close"
>
<CloseIcon />
</IconButton>
<Divider />
<DialogContent>
{registerForm && (
<div>
<TextField
className={loginClass.textInput}
required
id="firstname"
label="Imię"
type="name"
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<AccountCircle color="primary" />
</InputAdornment>
),
}}
error={formData.firstnameError}
onChange={(event) => (formData.firstname = event.target.value)}
/>
<TextField
className={loginClass.textInput}
required
id="lastname"
label="Nazwisko"
type="name"
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<AccountCircle color="primary" />
</InputAdornment>
),
}}
error={formData.lastnameError}
onChange={(event) => (formData.lastname = event.target.value)}
/>
<TextField
className={loginClass.textInput}
required
id="email"
label="Email"
type="email"
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<EmailIcon color="primary" />
</InputAdornment>
),
}}
error={formData.emailError}
onChange={(event) => (formData.email = event.target.value)}
/>
<TextField
className={loginClass.textInput}
required
id="password"
label="Hasło (min. 8 znaków)"
type="password"
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<LockIcon color="primary" />
</InputAdornment>
),
}}
error={formData.passwordError}
onChange={(event) => (formData.password = event.target.value)}
/>
<TextField
className={loginClass.textInput}
required
id="repeat-password"
label="Powtórz hasło"
type="password"
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<LockIcon color="primary" />
</InputAdornment>
),
}}
error={formData.repeatPasswordError}
onChange={(event) =>
(formData.repeatPassword = event.target.value)
}
/>
</div>
)}
<p>{registerResult}</p>
<Divider />
<div className="register-dialog-actions">
{circularProgress && <CircularProgress />}
{registerForm && (
<p>
Rejestracja oznacza akceptację{" "}
<span>
<Link
href="#"
onClick={(event) => handleRegulaminClick(event)}
>
regulaminu
</Link>
</span>
</p>
)}
<div className="register-dialog-button">
{registerForm ? (
<ButtonSecondary
onClick={() => sendForm(formData)}
text="Zarejestruj"
/>
) : (
<ButtonSecondary onClick={() => openLogin()} text="Logowanie" />
)}
</div>
</div>
</DialogContent>
</Dialog>
</div>
);
}

View File

@@ -0,0 +1,53 @@
import React from "react";
export default function Regulamin() {
return (
<div className="regulamin">
<h3>1. Postanowienia ogólne</h3>
<p>
Sklep Internetowy "Nazwa Sklepu" działający pod adresem
www.twoja-domena.pl, prowadzony jest przez "nazwa -Firmy" Sp. z o.o. z
siedzibą w Bielsku-Białej, ul. Cieszyńska 367, wpisaną przez Sąd
Rejonowy w Bielsku-Białej, VIII Wydział Gospodarczy Krajowego Rejestru
Sądowego pod numerem KRS: 00000000001. NIP: numer 547-000-00-00. Regon
numer: 000000001.
</p>
<h3>2. Zwroty i reklamacje</h3>
<p>
Zwrot towaru. Zgodnie z Ustawą z 2 marca 2000 roku o ochronie
niektórych praw konsumentów oraz o odpowiedzialności za szkodę
wyrządzoną przez produkt niebezpieczny, klient może zrezygnować z
towaru kupionego w naszym sklepie bez podania przyczyny w ciągu 10 dni
od dnia odebrania przesyłki. Wzór oświadczenia o odstąpieniu stanowi
załącznik do niniejszego regulaminu. Jest to możliwe tylko wówczas, gdy
towar nie nosi śladów użytkowania, jest kompletny i nie został w żaden
sposób zniszczony. Zwracany towar należy odesłać razem z otrzymaną wraz
z nim fakturą. Sklep gwarantuje zwrot kwoty równej cenie towaru.
Pieniądze zostaną zwrócone w ciągu 5 dni roboczych przelewem bankowym na
konto wskazane przez klienta lub przekazem pocztowym na adres wskazany w
zamówieniu. Koszt odesłania towaru nie podlega zwrotowi.
</p>
<h3>3. Dane osobowe</h3>
<p>
Informujemy, że dane osobowe podane na formularzu zamówienia w sklepie
internetowym Nazwa Sklepu, gromadzone i przetwarzane zgodnie z
Ustawą z dn. 29 sierpnia 1997r. o ochronie danych osobowych (Dz.U. z
2002 roku nr 101 poz. 926 z późniejszymi zmianami). Zgromadzone dane
osobowe wykorzystywane tylko i wyłącznie do celów realizacji
złożonych zamówień oraz informowania o promocjach i w żadnym wypadku nie
będą udostępniane osobom trzecim. Każdy klient naszego sklepu ma prawo
do wglądu, modyfikacji oraz usunięcia swoich danych osobowych z naszej
bazy danych.
</p>
<h3>4. Postanowienia końcowe</h3>
<p>
Wszystkie ceny podawane w złotych polskich i zawierają podatek VAT.
Cena podana przy każdym towarze jest wiążąca w chwili złożenia
zamówienia przez klienta. Sklep zastrzega sobie prawo do zmiany cen
towarów znajdujących się w ofercie, wprowadzania nowych towarów do
oferty sklepu internetowego, przeprowadzania i odwoływania akcji
promocyjnych na stronach sklepu bądź wprowadzania w nich zmian.
</p>
</div>
);
}

View File

@@ -0,0 +1,69 @@
import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import DialogTitle from "@material-ui/core/DialogTitle";
import DialogContent from "@material-ui/core/DialogContent";
import Dialog from "@material-ui/core/Dialog";
import Divider from "@material-ui/core/Divider";
import IconButton from "@material-ui/core/IconButton";
import CloseIcon from "@material-ui/icons/Close";
import Paper from "@material-ui/core/Paper";
import { useSelector, useDispatch } from "react-redux";
import { hideRegulaminDialog } from "../../actions/toggles";
import Regulamin from "./Regulamin";
export default function RegulaminDialog(props) {
var regulaminDialog = useSelector((state) => state.data.dialogs.regulamin);
const dispatch = useDispatch();
const loginStyles = makeStyles((theme) => ({
root: {
textAlign: "center",
"& .MuiPaper-root": {
backgroundColor: "#262626",
color: "#bbbbbb",
},
"& .MuiFormHelperText-root": {
color: "#606060",
textAlign: "center",
},
},
closeButton: {
color: "#bbbbbb",
position: "absolute",
right: theme.spacing(1),
top: theme.spacing(1),
},
paper: {
textAlign: "left",
padding: "18px",
maxHeight: "50vh",
overflow: "auto",
},
}));
const styles = loginStyles();
return (
<div>
<Dialog
className={styles.root}
open={regulaminDialog}
onClose={() => dispatch(hideRegulaminDialog())}
aria-labelledby="regulamin-title"
>
<DialogTitle id="regulamin-title">Regulamin</DialogTitle>
<IconButton
className={styles.closeButton}
onClick={() => dispatch(hideRegulaminDialog())}
aria-label="close"
>
<CloseIcon />
</IconButton>
<Divider />
<DialogContent>
<Paper className={styles.paper}>{<Regulamin />}</Paper>
</DialogContent>
</Dialog>
</div>
);
}

21
src/components/Social.js Normal file
View File

@@ -0,0 +1,21 @@
import React from "react";
import FacebookIcon from "@material-ui/icons/Facebook";
import TwitterIcon from "@material-ui/icons/Twitter";
import InstagramIcon from "@material-ui/icons/Instagram";
import IconButton from "@material-ui/core/IconButton";
export default function Social(props) {
return (
<div className="social-container">
<IconButton color="secondary">
<FacebookIcon />
</IconButton>
<IconButton color="secondary">
<TwitterIcon />
</IconButton>
<IconButton color="secondary">
<InstagramIcon />
</IconButton>
</div>
);
}

View File

@@ -1,4 +1,6 @@
import React from "react"; import React from "react";
import UserMenu from "./UserMenu";
import Social from "./Social";
import logo from "../public/logo_white.svg"; import logo from "../public/logo_white.svg";
import IconButton from "@material-ui/core/IconButton"; import IconButton from "@material-ui/core/IconButton";
import MenuIcon from "@material-ui/icons/Menu"; import MenuIcon from "@material-ui/icons/Menu";
@@ -13,24 +15,25 @@ import ListItemIcon from "@material-ui/core/ListItemIcon";
import HomeIcon from "@material-ui/icons/Home"; import HomeIcon from "@material-ui/icons/Home";
import MailIcon from "@material-ui/icons/Mail"; import MailIcon from "@material-ui/icons/Mail";
import PaymentIcon from "@material-ui/icons/Payment"; import PaymentIcon from "@material-ui/icons/Payment";
import FastfoodIcon from "@material-ui/icons/Fastfood"; import { showLoginDialog, showRegisterDialog } from "../actions/toggles";
import AddCircleIcon from "@material-ui/icons/AddCircle";
import FacebookIcon from "@material-ui/icons/Facebook";
import TwitterIcon from "@material-ui/icons/Twitter";
import InstagramIcon from "@material-ui/icons/Instagram";
import { showLoginDialog } from "../actions/toggles";
import { logout } from "../actions"; import { logout } from "../actions";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import ButtonSecondary from "./ButtonSecondary"; import ButtonSecondary from "./ButtonSecondary";
import ListSubheader from "@material-ui/core/ListSubheader";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
paper: { paper: {
backgroundColor: "#262626", backgroundColor: "#262626",
color: "#bbbbbb", color: "#bbbbbb",
"& .MuiListItemIcon-root": { "& .MuiListItemIcon-root": {
color: "#01c3a9", color: "#d68000",
}, },
}, },
subheader: {
color: "#767676",
fontSize: "0.8rem",
fontWeight: "400",
},
})); }));
export default function TopBar() { export default function TopBar() {
@@ -50,6 +53,9 @@ export default function TopBar() {
case "logIn": case "logIn":
dispatch(showLoginDialog()); dispatch(showLoginDialog());
break; break;
case "register":
dispatch(showRegisterDialog());
break;
case "logOut": case "logOut":
dispatch(logout()); dispatch(logout());
break; break;
@@ -108,7 +114,14 @@ export default function TopBar() {
onClose={toggleDrawer(false)} onClose={toggleDrawer(false)}
PaperProps={{ className: classes.paper }} PaperProps={{ className: classes.paper }}
> >
<List style={{ width: "300px" }}> <List
style={{ width: "300px" }}
subheader={
<ListSubheader className={classes.subheader}>
Nawigacja
</ListSubheader>
}
>
<ListItem button onClick={() => handleClick("menui")}> <ListItem button onClick={() => handleClick("menui")}>
<ListItemIcon> <ListItemIcon>
<HomeIcon /> <HomeIcon />
@@ -119,7 +132,7 @@ export default function TopBar() {
<ListItemIcon> <ListItemIcon>
<MailIcon /> <MailIcon />
</ListItemIcon> </ListItemIcon>
<ListItemText primary="Skontaktuj się" /> <ListItemText primary="Kontakt" />
</ListItem> </ListItem>
<ListItem button onClick={() => handleClick("pricing")}> <ListItem button onClick={() => handleClick("pricing")}>
<ListItemIcon> <ListItemIcon>
@@ -128,28 +141,20 @@ export default function TopBar() {
<ListItemText primary="Cennik" /> <ListItemText primary="Cennik" />
</ListItem> </ListItem>
<Divider /> <Divider />
{loggedIn && ( <ListItem>{loggedIn && <UserMenu />}</ListItem>
<ListItem button onClick={() => handleClick("myRestaurant")}>
<ListItemIcon>
<FastfoodIcon />
</ListItemIcon>
<ListItemText primary="Moja restauracja" />
</ListItem>
)}
{loggedIn && (
<ListItem button onClick={() => handleClick("addDish")}>
<ListItemIcon>
<AddCircleIcon />
</ListItemIcon>
<ListItemText primary="Dodaj danie" />
</ListItem>
)}
</List> </List>
<div className="drawer-bottom">
<div className="drawer-buttons"> <div className="drawer-buttons">
{!loggedIn && ( {!loggedIn && (
<ButtonSecondary <ButtonSecondary
onClick={() => handleClick("logIn")} onClick={() => handleClick("logIn")}
text="Zaloguj się" text="Logowanie"
/>
)}
{!loggedIn && (
<ButtonSecondary
onClick={() => handleClick("register")}
text="Rejestracja"
/> />
)} )}
{loggedIn && ( {loggedIn && (
@@ -159,16 +164,7 @@ export default function TopBar() {
/> />
)} )}
</div> </div>
<div className="drawer-bottom"> <Social />
<IconButton color="secondary">
<FacebookIcon />
</IconButton>
<IconButton color="secondary">
<TwitterIcon />
</IconButton>
<IconButton color="secondary">
<InstagramIcon />
</IconButton>
</div> </div>
</Drawer> </Drawer>
</div> </div>

View File

@@ -0,0 +1,71 @@
import React, { useState } from "react";
import Collapse from "@material-ui/core/Collapse";
import { makeStyles } from "@material-ui/core/styles";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import ListSubheader from "@material-ui/core/ListSubheader";
import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText";
import ExpandLess from "@material-ui/icons/ExpandLess";
import ExpandMore from "@material-ui/icons/ExpandMore";
import FastfoodIcon from "@material-ui/icons/Fastfood";
import AddIcon from "@material-ui/icons/Add";
import SettingsIcon from "@material-ui/icons/Settings";
const useStyles = makeStyles((theme) => ({
root: {
width: "100%",
},
nested: {
paddingLeft: theme.spacing(4),
},
subheader: {
color: "#767676",
fontSize: "0.8rem",
fontWeight: "400",
paddingLeft: 0,
},
}));
export default function UserMenu(props) {
const [open, setOpen] = useState(false);
const classes = useStyles();
const handleClick = () => {
setOpen(!open);
};
return (
<List
className={classes.root}
subheader={
<ListSubheader className={classes.subheader}>
Menu użytkownika
</ListSubheader>
}
>
<ListItem button disableGutters>
<ListItemIcon>
<SettingsIcon />
</ListItemIcon>
<ListItemText primary="Moje konto" />
</ListItem>
<ListItem button onClick={handleClick} disableGutters>
<ListItemIcon>
<FastfoodIcon />
</ListItemIcon>
<ListItemText primary="Moje lokale" />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
<ListItem>
<ListItemIcon>
<AddIcon />
</ListItemIcon>
<ListItemText primary="Dodaj lokal" />
</ListItem>
</List>
</Collapse>
</List>
);
}

View File

@@ -3,11 +3,18 @@ const initialState = {
loggedIn: false, loggedIn: false,
jwt: "", jwt: "",
username: "", username: "",
userId: "",
userEmail: "",
dialogs: { dialogs: {
logIn: false, logIn: false,
register: false, register: false,
contact: false, contact: false,
pricing: false, pricing: false,
regulamin: false,
registerCircularProgress: false,
registerForm: true,
registerResult: "",
loginResult: "",
}, },
}; };
@@ -20,14 +27,73 @@ const data = (state = initialState, action) => {
case "DIALOG_LOGIN_VISIBLE": case "DIALOG_LOGIN_VISIBLE":
return (state = { ...state, dialogs: { ...state.dialogs, logIn: true } }); return (state = { ...state, dialogs: { ...state.dialogs, logIn: true } });
case "SET_LOGGEDIN": case "SET_LOGGEDIN":
return (state = { ...state, loggedIn: true, username: action.payload }); return (state = {
...state,
loggedIn: true,
username: action.payload.username,
jwt: action.payload.jwt,
userEmail: action.payload.email,
userId: action.payload.id,
});
case "SET_LOGGEDOUT": case "SET_LOGGEDOUT":
return (state = { ...state, loggedIn: false, username: "" }); return (state = {
...state,
loggedIn: false,
username: "",
jwt: "",
userEmail: "",
userId: "",
});
case "DIALOG_LOGIN_HIDDEN": case "DIALOG_LOGIN_HIDDEN":
return (state = { return (state = {
...state, ...state,
dialogs: { ...state.dialogs, logIn: false }, dialogs: { ...state.dialogs, logIn: false },
}); });
case "DIALOG_REGISTER_VISIBLE":
return (state = {
...state,
dialogs: { ...state.dialogs, register: true },
});
case "DIALOG_REGISTER_HIDDEN":
return (state = {
...state,
dialogs: { ...state.dialogs, register: false },
});
case "DIALOG_REGULAMIN_VISIBLE":
return (state = {
...state,
dialogs: { ...state.dialogs, regulamin: true },
});
case "DIALOG_REGULAMIN_HIDDEN":
return (state = {
...state,
dialogs: { ...state.dialogs, regulamin: false },
});
case "DIALOG_REGISTER_CIRCLE_SHOW":
return (state = {
...state,
dialogs: { ...state.dialogs, registerCircularProgress: true },
});
case "DIALOG_REGISTER_CIRCLE_HIDE":
return (state = {
...state,
dialogs: { ...state.dialogs, registerCircularProgress: false },
});
case "DIALOG_REGISTER_FORM_HIDE":
return (state = {
...state,
dialogs: { ...state.dialogs, registerForm: false },
});
case "DIALOG_REGISTER_SET_RESULT":
return (state = {
...state,
dialogs: { ...state.dialogs, registerResult: action.payload },
});
case "DIALOG_LOGIN_SET_RESULT":
return (state = {
...state,
dialogs: { ...state.dialogs, loginResult: action.payload },
});
default: default:
return state; return state;
} }

View File

@@ -4,4 +4,22 @@
.login-dialog-buttons { .login-dialog-buttons {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center;
flex-direction: column;
p {
font-size: 0.8rem;
}
}
.register-dialog-actions {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
p {
font-size: 0.8rem;
}
}
.regulamin {
} }

View File

@@ -50,6 +50,7 @@
margin-bottom: 20px; margin-bottom: 20px;
display: flex; display: flex;
justify-content: center; justify-content: center;
flex-direction: column;
position: absolute; position: absolute;
bottom: 0; bottom: 0;
} }
@@ -58,3 +59,11 @@
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
.social-container {
width: 100%;
margin-bottom: 20px;
margin-top: 20px;
display: flex;
justify-content: center;
}