web client v0.5

//notifications
//delete restaurant
//change picture
//redesigned data store
This commit is contained in:
2020-09-27 18:40:39 +02:00
parent 3fdc93ef28
commit d2842a1db3
24 changed files with 810 additions and 501 deletions

View File

@@ -14,6 +14,7 @@ import RestaurantMenuIcon from "@material-ui/icons/RestaurantMenu";
import FastfoodIcon from "@material-ui/icons/Fastfood";
import AddIcon from "@material-ui/icons/Add";
import Badge from "@material-ui/core/Badge";
import SearchIcon from "@material-ui/icons/Search";
//--------------
import EditRestaurantInfo from "../EditRestaurant/EditRestaurantInfo";
import EditRestaurantLocation from "../EditRestaurant/EditRestaurantLocation";
@@ -84,6 +85,15 @@ export default function EditRestaurant(props) {
<h3 className="editRestaurant-title">{restaurant.name}</h3>
<Divider />
<List className={classes.main} component="nav">
<ListItem
button
onClick={() => history.push(`/restaurant/${restaurant._id}`)}
>
<ListItemIcon>
<SearchIcon color="primary" />
</ListItemIcon>
<ListItemText primary="Podgląd" />
</ListItem>
<ListItem
button
selected={tab === 0}

View File

@@ -8,15 +8,13 @@ import ButtonSecondary from "../Input/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 { useDispatch } from "react-redux";
import Link from "@material-ui/core/Link";
import validator from "validator";
import InputAdornment from "@material-ui/core/InputAdornment";
import AccountCircle from "@material-ui/icons/AccountCircle";
import CircularProgress from "@material-ui/core/CircularProgress";
import { remindPassword } from "../../actions";
import { remindPassword, notification } from "../../actions";
import { useHistory } from "react-router-dom";
import { setReminderResult } from "../../actions/toggles";
export default function ForgotPassword(props) {
const initialData = {
@@ -24,12 +22,6 @@ export default function ForgotPassword(props) {
emailError: false,
};
const [data, setData] = useState(initialData);
const reminderResult = useSelector(
(state) => state.data.dialogs.reminderResult
);
const reminderCircle = useSelector(
(state) => state.data.dialogs.reminderCircularProgress
);
const dispatch = useDispatch();
const history = useHistory();
@@ -82,7 +74,7 @@ export default function ForgotPassword(props) {
if (validateLogin()) {
dispatch(remindPassword(data.email));
} else {
dispatch(setReminderResult("Podaj poprawne dane."));
dispatch(notification("Podaj poprawne dane.", "error"));
}
};
@@ -129,10 +121,6 @@ export default function ForgotPassword(props) {
),
}}
/>
<p>
{reminderResult}
<span>{reminderCircle && <CircularProgress />}</span>
</p>
<div className="login-dialog-buttons">
<ButtonSecondary
onClick={() => handleRemind()}

View File

@@ -8,14 +8,13 @@ import ButtonSecondary from "../Input/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 { setLoginResult } from "../../actions/toggles";
import { useDispatch } from "react-redux";
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";
import { tryLogin, notification } from "../../actions";
import { useHistory } from "react-router-dom";
export default function LoginDialog(props) {
@@ -26,7 +25,6 @@ export default function LoginDialog(props) {
passwordError: false,
};
const [loginData, setLoginData] = useState(initialData);
const loginResult = useSelector((state) => state.data.dialogs.loginResult);
const dispatch = useDispatch();
const history = useHistory();
@@ -82,7 +80,7 @@ export default function LoginDialog(props) {
if (validateLogin()) {
dispatch(tryLogin(loginData.email, loginData.password));
} else {
dispatch(setLoginResult("Podaj poprawne dane logowania."));
dispatch(notification("Podaj poprawne dane logowania.", "error"));
}
};
@@ -140,7 +138,6 @@ export default function LoginDialog(props) {
),
}}
/>
<p>{loginResult}</p>
<div className="login-dialog-buttons">
<ButtonSecondary onClick={() => handleLogin()} text="Zaloguj" />
</div>

View File

@@ -18,10 +18,11 @@ import ImageUpload from "../Input/ImageUpload";
import validator from "validator";
import { useHistory } from "react-router-dom";
import InputGoogleMaps from "../Input/InputGoogleMaps";
import InfoDialog from "../Output/InfoDialog";
import { prepareTags } from "../../Services.js";
import axios from "axios";
import { useSelector } from "react-redux";
import { useSelector, useDispatch } from "react-redux";
import { notification, refreshUserData } from "../../actions";
import { showBackdrop, hideBackdrop } from "../../actions/toggles.js";
import InputWorkingHours from "../Input/InputWorkingHours";
// ICONS
import FastfoodIcon from "@material-ui/icons/Fastfood";
@@ -84,13 +85,14 @@ const useStyles = makeStyles((theme) => ({
}));
export default function NewRestaurant() {
const dispatch = useDispatch();
const initialState = {
name: "",
city: "",
adress: "",
coordinates: [52.354293, 19.42377],
placesId: "",
imgURL: "",
imgUrl: "",
workingHours: {
pn: "8:00 - 22:00",
wt: "8:00 - 22:00",
@@ -113,12 +115,9 @@ export default function NewRestaurant() {
adressError: false,
descriptionError: false,
charLeft: 400,
response: "Dodawanie lokalu, prosimy o chwilę cierpliwości...",
};
const steps = ["Informacje", "Zdjęcie", "Lokalizacja"];
const [state, setState] = useState(initialState);
const [open, setOpen] = useState(true);
const [loading, setLoading] = useState(true);
const [activeStep, setActiveStep] = React.useState(0);
const styles = useStyles();
const availableTags = [
@@ -131,7 +130,7 @@ export default function NewRestaurant() {
"Dowozimy",
];
const history = useHistory();
const token = useSelector((state) => state.data.jwt);
const token = useSelector((state) => state.data.userData.jwt);
// HANDLERS
@@ -143,7 +142,7 @@ export default function NewRestaurant() {
adress: state.adress,
coordinates: state.coordinates,
placesId: state.placesId,
imgURL: state.imgURL,
imgUrl: state.imgUrl,
workingHours: state.workingHours,
description: state.description,
tags: formattedTags,
@@ -151,7 +150,7 @@ export default function NewRestaurant() {
phone: state.phone,
hidden: false,
};
setOpen(false);
dispatch(showBackdrop());
axios({
url: "http://localhost:4000/restaurant",
method: "POST",
@@ -161,26 +160,26 @@ export default function NewRestaurant() {
},
})
.then((response) => {
setLoading(false);
setState({
...state,
response:
dispatch(hideBackdrop());
dispatch(
notification(
"Lokal został dodany, aktywuj subskrypcję, aby był widoczny w wynikach wyszukiwania.",
});
setTimeout(() => {
history.push("/");
}, 5000);
"success"
)
);
dispatch(refreshUserData(token));
history.push("/");
})
.catch((error) => {
setLoading(false);
setState({
...state,
response:
dispatch(hideBackdrop());
console.log(error);
dispatch(
notification(
"Wystąpił nieoczekiwany błąd, przepraszamy za utrudnienia. Spróbuj ponownie za chwilę.",
});
setTimeout(() => {
history.push("/");
}, 5000);
"error"
)
);
history.push("/");
});
};
@@ -200,7 +199,7 @@ export default function NewRestaurant() {
}
break;
case 1:
if (!validator.isEmpty(state.imgURL)) {
if (!validator.isEmpty(state.imgUrl)) {
setActiveStep(2);
}
break;
@@ -213,7 +212,7 @@ export default function NewRestaurant() {
};
const handleImageUploaded = (link) => {
setState({ ...state, imgURL: link });
setState({ ...state, imgUrl: link });
};
const handleDescriptionChange = (event) => {
@@ -256,16 +255,9 @@ export default function NewRestaurant() {
return (
<div>
{!open && (
<InfoDialog
title={"Dodawanie lokalu"}
text={state.response}
loading={loading}
/>
)}
<Dialog
className={styles.root}
open={open}
open={true}
aria-labelledby="newRestaurant-title"
>
<DialogTitle id="newRestaurant-title">Dodaj Lokal</DialogTitle>
@@ -469,7 +461,7 @@ export default function NewRestaurant() {
<Paper>
<h4>Dodaj zdjęcie lokalu.</h4>
<ImageUpload
img={state.imgURL}
img={state.imgUrl}
onUpload={(link) => handleImageUploaded(link)}
/>
</Paper>

View File

@@ -0,0 +1,88 @@
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 "../Input/ButtonSecondary";
import IconButton from "@material-ui/core/IconButton";
import TextField from "@material-ui/core/TextField";
import CloseIcon from "@material-ui/icons/Close";
import LockIcon from "@material-ui/icons/Lock";
import InputAdornment from "@material-ui/core/InputAdornment";
export default function PasswordConfirmation(props) {
const [password, setPassword] = useState("");
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 (
<Dialog
className={loginClass.root}
onClose={props.onCancel}
open={props.open}
aria-labelledby="login-title"
>
<DialogTitle id="login-title">Potwierdź hasłem</DialogTitle>
<IconButton
className={loginClass.closeButton}
onClick={props.onCancel}
aria-label="close"
>
<CloseIcon />
</IconButton>
<Divider />
<DialogContent>
<TextField
className={loginClass.textInput}
id="password"
label="Hasło"
type="password"
value={password}
variant="outlined"
onChange={(event) => setPassword(event.target.value)}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<LockIcon color="primary" />
</InputAdornment>
),
}}
/>
<div className="login-dialog-buttons">
<ButtonSecondary
onClick={() => props.onSubmit(password)}
text="Potwierdź"
/>
</div>
</DialogContent>
</Dialog>
);
}

View File

@@ -8,9 +8,9 @@ import ButtonSecondary from "../Input/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 { setRegisterResult, showRegulamin } from "../../actions/toggles";
import { tryRegister } from "../../actions";
import { useDispatch } from "react-redux";
import { showRegulamin } from "../../actions/toggles";
import { tryRegister, notification } from "../../actions";
import InputAdornment from "@material-ui/core/InputAdornment";
import AccountCircle from "@material-ui/icons/AccountCircle";
import BusinessIcon from "@material-ui/icons/Business";
@@ -18,12 +18,9 @@ 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";
import { useHistory } from "react-router-dom";
export default function RegisterDialog(props) {
// SETUP
const initialFormData = {
firstname: "",
lastname: "",
@@ -43,13 +40,6 @@ export default function RegisterDialog(props) {
repeatPasswordError: false,
};
const [formData, setFormData] = useState(initialFormData);
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();
const history = useHistory();
@@ -138,7 +128,7 @@ export default function RegisterDialog(props) {
if (validateForm()) {
dispatch(tryRegister(form));
} else {
dispatch(setRegisterResult("Proszę poprawić formularz."));
dispatch(notification("Proszę poprawić formularz.", "error"));
}
};
@@ -162,179 +152,161 @@ export default function RegisterDialog(props) {
</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="companyName"
label="Nazwa firmy"
type="name"
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<BusinessIcon color="primary" />
</InputAdornment>
),
}}
error={formData.companyNameError}
onChange={(event) =>
(formData.companyName = event.target.value)
}
/>
<TextField
className={loginClass.textInput}
required
id="adress"
label="Adres firmy"
type="name"
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<BusinessIcon color="primary" />
</InputAdornment>
),
}}
error={formData.adressError}
onChange={(event) => (formData.adress = event.target.value)}
/>
<TextField
className={loginClass.textInput}
required
id="NIP"
label="NIP"
type="name"
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<BusinessIcon color="primary" />
</InputAdornment>
),
}}
error={formData.NIPError}
onChange={(event) => (formData.NIP = 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>
<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="companyName"
label="Nazwa firmy"
type="name"
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<BusinessIcon color="primary" />
</InputAdornment>
),
}}
error={formData.companyNameError}
onChange={(event) => (formData.companyName = event.target.value)}
/>
<TextField
className={loginClass.textInput}
required
id="adress"
label="Adres firmy"
type="name"
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<BusinessIcon color="primary" />
</InputAdornment>
),
}}
error={formData.adressError}
onChange={(event) => (formData.adress = event.target.value)}
/>
<TextField
className={loginClass.textInput}
required
id="NIP"
label="NIP"
type="name"
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<BusinessIcon color="primary" />
</InputAdornment>
),
}}
error={formData.NIPError}
onChange={(event) => (formData.NIP = 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>
<Divider />
<div className="register-dialog-actions">
{circularProgress && <CircularProgress />}
{registerForm && (
<p>
Rejestracja oznacza akceptację{" "}
<span>
<Link
href="#"
onClick={(event) => handleRegulaminClick(event)}
>
regulaminu
</Link>
</span>
</p>
)}
<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={() => history.push("/login")}
text="Logowanie"
/>
)}
<ButtonSecondary
onClick={() => sendForm(formData)}
text="Zarejestruj"
/>
</div>
</div>
</DialogContent>

View File

@@ -8,14 +8,12 @@ import ButtonSecondary from "../Input/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 { useDispatch } from "react-redux";
import validator from "validator";
import InputAdornment from "@material-ui/core/InputAdornment";
import AccountCircle from "@material-ui/icons/AccountCircle";
import CircularProgress from "@material-ui/core/CircularProgress";
import { useHistory, useLocation } from "react-router-dom";
import { changePassword } from "../../actions/index";
import { setResetResult } from "../../actions/toggles";
import { changePassword, notification } from "../../actions/index";
function useQuery() {
return new URLSearchParams(useLocation().search);
@@ -31,10 +29,6 @@ export default function ResetPassword(props) {
passwordRepeatError: false,
};
const [data, setData] = useState(initialData);
const resetResult = useSelector((state) => state.data.dialogs.resetResult);
const resetCircle = useSelector(
(state) => state.data.dialogs.resetCircularProgress
);
const dispatch = useDispatch();
const history = useHistory();
const query = useQuery();
@@ -94,7 +88,7 @@ export default function ResetPassword(props) {
if (validateLogin()) {
dispatch(changePassword(data.email, data.password, token));
} else {
dispatch(setResetResult("Popraw dane."));
dispatch(notification("Popraw dane.", "error"));
}
};
@@ -170,10 +164,6 @@ export default function ResetPassword(props) {
),
}}
/>
<div>
{resetCircle && <CircularProgress />}
<p>{resetResult}</p>
</div>
<div className="login-dialog-buttons">
<ButtonSecondary onClick={() => handleReset()} text="Zmień hasło" />
</div>

View File

@@ -1,4 +1,5 @@
import React, { useState } from "react";
import { useHistory } from "react-router-dom";
import ButtonPrimary from "../Input/ButtonPrimary";
import ButtonSecondary from "../Input/ButtonSecondary";
import TextField from "@material-ui/core/TextField";
@@ -10,8 +11,18 @@ import PhoneIcon from "@material-ui/icons/Phone";
import FacebookIcon from "@material-ui/icons/Facebook";
import InstagramIcon from "@material-ui/icons/Instagram";
import LanguageIcon from "@material-ui/icons/Language";
import FastfoodIcon from "@material-ui/icons/Fastfood";
import LocationCityIcon from "@material-ui/icons/LocationCity";
import Divider from "@material-ui/core/Divider";
import Link from "@material-ui/core/Link";
import { decodeTags } from "../../Services";
import validator from "validator";
import { useSelector, useDispatch } from "react-redux";
import { notification, refreshUserData, updateRestaurant } from "../../actions";
import { showBackdrop, hideBackdrop } from "../../actions/toggles.js";
import { prepareTags } from "../../Services.js";
import axios from "axios";
import PasswordConfirmation from "../Dialogs/PasswordConfirmation";
const useStyles = makeStyles((theme) => ({
textInput: {
@@ -36,6 +47,9 @@ const useStyles = makeStyles((theme) => ({
color: "#bbbbbb",
},
},
link: {
cursor: "pointer",
},
}));
const calculateCharLeft = (from) => {
@@ -43,6 +57,7 @@ const calculateCharLeft = (from) => {
};
export default function EditRestaurantInfo(props) {
const history = useHistory();
const initialState = {
name: props.restaurant.name,
city: props.restaurant.city,
@@ -54,9 +69,17 @@ export default function EditRestaurantInfo(props) {
tags: decodeTags(props.restaurant.tags),
workingHours: props.restaurant.workingHours,
charleft: calculateCharLeft(props.restaurant.description),
nameError: false,
cityError: false,
adressError: false,
descriptionError: false,
};
const [state, setState] = useState(initialState);
const [passwordDialog, setPasswordDialog] = useState(false);
const styles = useStyles();
const dispatch = useDispatch();
const jwt = useSelector((state) => state.data.userData.jwt);
const email = useSelector((state) => state.data.userData.userEmail);
const handleDescriptionChange = (event) => {
let stringLength = event.target.value.length;
let charleft = 400 - stringLength;
@@ -72,8 +95,112 @@ export default function EditRestaurantInfo(props) {
"Dowozimy",
];
const validateForm = () => {
const validation = {
nameValid: validator.isLength(state.name, { min: 1, max: 40 }),
cityValid: validator.isLength(state.city, { min: 1, max: 40 }),
adressValid: validator.isLength(state.name, { min: 1, max: 40 }),
descriptionValid: validator.isLength(state.description, {
min: 1,
max: 400,
}),
};
setState({
...state,
nameError: !validation.nameValid,
cityError: !validation.cityValid,
adressError: !validation.adressValid,
descriptionError: !validation.descriptionValid,
});
return (
validation.nameValid &&
validation.cityValid &&
validation.adressValid &&
validation.descriptionValid
);
};
const cancelChanges = () => {
setState(initialState);
};
const handleDelete = (password) => {
axios({
url: "http://localhost:4000/restaurant/delete",
method: "POST",
data: {
restaurantId: props.restaurant._id,
password: password,
email: email,
},
headers: {
"x-auth-token": jwt,
},
})
.then((response) => {
dispatch(refreshUserData(jwt));
dispatch(hideBackdrop());
dispatch(notification("Restauracja została usunięta", "success"));
history.push("/");
})
.catch((err) => {
console.log(err);
dispatch(hideBackdrop());
dispatch(notification("Wystąpił nieoczekiwany błąd :(", "error"));
});
};
const handleSendForm = () => {
if (validateForm()) {
const formattedTags = prepareTags(state.tags);
const data = {
restaurantId: props.restaurant._id,
dishes: props.restaurant.dishes,
categories: props.restaurant.categories,
lunchMenu: props.restaurant.lunchMenu,
name: state.name,
city: state.city,
adress: state.adress,
coordinates: props.restaurant.location.coordinates,
placesId: props.restaurant.placesId,
imgUrl: props.restaurant.imgUrl,
workingHours: state.workingHours,
description: state.description,
tags: formattedTags,
links: state.links,
phone: state.phone,
hidden: props.restaurant.hidden,
};
dispatch(showBackdrop());
axios({
url: "http://localhost:4000/restaurant",
method: "PUT",
data: data,
headers: {
"x-auth-token": jwt,
},
})
.then((response) => {
dispatch(hideBackdrop());
dispatch(notification("Dane zostały zapisane.", "success"));
dispatch(updateRestaurant(response));
})
.catch((err) => {
console.log(err);
dispatch(hideBackdrop());
dispatch(notification("Wystąpił nieoczekiwany błąd :(", "error"));
});
}
};
return (
<div className="editRestaurant-tab">
<PasswordConfirmation
open={passwordDialog}
onCancel={() => setPasswordDialog(false)}
onSubmit={handleDelete}
/>
<div className="editRestaurant-doubleColumn">
<div className="editRestaurant-sectiontitle">
<h4>Podstawowe dane</h4>
@@ -84,10 +211,18 @@ export default function EditRestaurantInfo(props) {
className={styles.textInputFullWidth}
fullWidth
value={state.name}
error={state.nameError}
onChange={(event) =>
setState({ ...state, name: event.target.value })
}
InputLabelProps={{ shrink: true }}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<FastfoodIcon color="primary" />
</InputAdornment>
),
}}
label="Nazwa lokalu"
variant="outlined"
/>
@@ -96,18 +231,34 @@ export default function EditRestaurantInfo(props) {
<TextField
className={styles.textInput}
value={state.city}
error={state.cityError}
onChange={(event) => setState({ ...state, city: event.target.value })}
InputLabelProps={{ shrink: true }}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<LocationCityIcon color="primary" />
</InputAdornment>
),
}}
label="Miasto"
variant="outlined"
/>
<TextField
className={styles.textInput}
value={state.adress}
error={state.adressError}
onChange={(event) =>
setState({ ...state, adress: event.target.value })
}
InputLabelProps={{ shrink: true }}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<LocationCityIcon color="primary" />
</InputAdornment>
),
}}
label="Adres"
variant="outlined"
/>
@@ -117,6 +268,7 @@ export default function EditRestaurantInfo(props) {
fullWidth
label="Opis"
value={state.description}
error={state.descriptionError}
onChange={handleDescriptionChange}
multiline
rows={3}
@@ -230,10 +382,17 @@ export default function EditRestaurantInfo(props) {
),
}}
/>
<div className="editRestaurant-sectiontitle">
<h4>Zaawansowane</h4>
<Divider />
</div>
<Link className={styles.link} onClick={() => setPasswordDialog(true)}>
Usuń restaurację
</Link>
</div>
<div className="editRestaurant-bottom">
<ButtonPrimary text="Anuluj" />
<ButtonSecondary text="Zapisz" />
<ButtonPrimary text="Anuluj" onClick={cancelChanges} />
<ButtonSecondary onClick={handleSendForm} text="Zapisz" />
</div>
</div>
);

View File

@@ -1,9 +1,77 @@
import React from "react";
import React, { useState } from "react";
import ImageUpload from "../Input/ImageUpload";
import ButtonSecondary from "../Input/ButtonSecondary";
import ButtonPrimary from "../Input/ButtonPrimary";
import { useDispatch, useSelector } from "react-redux";
import { notification } from "../../actions";
import { showBackdrop, hideBackdrop } from "../../actions/toggles.js";
import axios from "axios";
export default function EditRestaurantPhoto(props) {
const {
imgUrl,
dishes,
categories,
lunchMenu,
name,
city,
adress,
placesId,
location,
workingHours,
description,
tags,
links,
phone,
hidden,
} = props.restaurant;
const [url, setUrl] = useState(imgUrl);
const token = useSelector((state) => state.data.userData.jwt);
const dispatch = useDispatch();
const handleSave = () => {
dispatch(showBackdrop());
const data = {
restaurantId: props.restaurant._id,
dishes: dishes,
categories: categories,
lunchMenu: lunchMenu,
name: name,
city: city,
adress: adress,
coordinates: location.coordinates,
placesId: placesId,
imgUrl: url,
workingHours: workingHours,
description: description,
tags: tags,
links: links,
phone: phone,
hidden: hidden,
};
axios({
url: "http://localhost:4000/restaurant",
method: "PUT",
data: data,
headers: {
"x-auth-token": token,
},
})
.then((res) => {
dispatch(hideBackdrop());
dispatch(notification("Zmieniono zdjęcie.", "success"));
})
.catch((e) => {
dispatch(hideBackdrop());
dispatch(notification("Nie udało się zmienić zdjęcia :(", "error"));
});
};
return (
<div className="editRestaurant-tab">
<p>Phottttto</p>
<ImageUpload img={url} onUpload={(newUrl) => setUrl(newUrl)} />
<div className="editRestaurant-bottom">
<ButtonPrimary text="Anuluj" onClick={() => setUrl(imgUrl)} />
<ButtonSecondary onClick={handleSave} text="Zapisz" />
</div>
</div>
);
}

View File

@@ -4,14 +4,14 @@ import { useSelector } from "react-redux";
import axios from "axios";
export default function ImageUpload(props) {
const [imagePreviewURL, setPreviewURL] = useState(props.img);
let showCircle = false;
const { img } = props;
const [loading, setLoading] = useState(false);
const token = useSelector((state) => state.data.userData.jwt);
const handleInputChange = (event) => {
let data = new FormData();
data.append("menuiImage", event.target.files[0]);
setLoading(true);
axios({
url: "http://localhost:4000/img",
method: "POST",
@@ -23,24 +23,25 @@ export default function ImageUpload(props) {
},
})
.then((response) => {
setPreviewURL(response.data.imgURL);
props.onUpload(response.data.imgURL);
setLoading(false);
})
.catch((error) => {
console.log("Wystąpił błąd podczas wgrywania pliku.");
setLoading(false);
});
};
let imagePreview = (
<div className="image-preview">
{showCircle ? <CircularProgress /> : "Proszę wybrać obraz. (max. 2MB)"}
{loading ? <CircularProgress /> : "Proszę wybrać obraz. (max. 2MB)"}
</div>
);
if (imagePreviewURL) {
if (img) {
imagePreview = (
<div
className="image-preview"
style={{ backgroundImage: `url(${imagePreviewURL})` }}
style={{ backgroundImage: `url(${img})` }}
></div>
);
}

View File

@@ -0,0 +1,45 @@
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { useSnackbar } from "notistack";
import { removeSnackbar } from "../actions/toggles.js";
let displayed = [];
const Notifier = () => {
const dispatch = useDispatch();
const notifications = useSelector((store) => store.notifications || []);
const { enqueueSnackbar } = useSnackbar();
const storeDisplayed = (id) => {
displayed = [...displayed, id];
};
const removeDisplayed = (id) => {
displayed = [...displayed.filter((key) => id !== key)];
};
React.useEffect(() => {
notifications.forEach(({ key, message, options = {} }) => {
// do nothing if snackbar is already displayed
if (displayed.includes(key)) return;
// display snackbar using notistack
enqueueSnackbar(message, {
key,
...options,
onExited: (event, myKey) => {
// remove this snackbar from redux store
dispatch(removeSnackbar(myKey));
removeDisplayed(myKey);
},
});
// keep track of snackbars that we've displayed
storeDisplayed(key);
});
}, [notifications, enqueueSnackbar, dispatch]);
return null;
};
export default Notifier;

View File

@@ -0,0 +1,22 @@
import React from "react";
import Backdrop from "@material-ui/core/Backdrop";
import CircularProgress from "@material-ui/core/CircularProgress";
import { useSelector } from "react-redux";
import { makeStyles } from "@material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
backdrop: {
zIndex: theme.zIndex.drawer + 1,
color: "#fff",
},
}));
export default function AppBackdrop() {
const open = useSelector((state) => state.data.backdrop);
const classes = useStyles();
return (
<Backdrop className={classes.backdrop} open={open}>
<CircularProgress color="inherit" />
</Backdrop>
);
}

View File

@@ -5,7 +5,6 @@ import ListItem from "@material-ui/core/ListItem";
import FastfoodIcon from "@material-ui/icons/Fastfood";
import Badge from "@material-ui/core/Badge";
import { makeStyles } from "@material-ui/core/styles";
import { useHistory } from "react-router-dom";
const useStyles = makeStyles((theme) => ({
root: {
@@ -25,7 +24,6 @@ const useStyles = makeStyles((theme) => ({
}));
export default function ListItemRestaurant(props) {
const history = useHistory();
const styles = useStyles();
const badgeData = {
color: "",
@@ -45,7 +43,7 @@ export default function ListItemRestaurant(props) {
return (
<ListItem
button
onClick={() => history.push(`/editRestaurant/${props.id}`)}
onClick={() => props.onClick(`/editRestaurant/${props.id}`)}
>
<ListItemIcon>
<Badge

View File

@@ -1,13 +1,6 @@
import logo from "../../public/logo_mint.svg";
import React from "react";
import { useSelector } from "react-redux";
export default function LogoMain() {
let appMode = useSelector((store) => store.appMode);
if (appMode === "init") {
return <img src={logo} alt="Menui logo" className="logo" />;
} else {
return "";
}
return <img src={logo} alt="Menui logo" className="logo" />;
}

View File

@@ -50,6 +50,7 @@ export default function UserMenu(props) {
key={restaurant._id}
subscriptionActive={restaurant.subscriptionActive}
id={restaurant._id}
onClick={(link) => handleButtonClick(link)}
/>
);
});