Files
menui_web/src/components/Dialogs/NewRestaurant.js

563 lines
17 KiB
JavaScript

import React, { useState, useEffect } 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 Stepper from "@material-ui/core/Stepper";
import Step from "@material-ui/core/Step";
import StepLabel from "@material-ui/core/StepLabel";
import Paper from "@material-ui/core/Paper";
import Autocomplete from "@material-ui/lab/Autocomplete";
import InputAdornment from "@material-ui/core/InputAdornment";
import ImageUpload from "../Input/ImageUpload";
import validator from "validator";
import { useHistory } from "react-router-dom";
import InputGoogleMaps from "../Input/InputGoogleMaps";
import { prepareTags } from "../../Services.js";
import axios from "axios";
import { useSelector, useDispatch } from "react-redux";
import { notification, refreshUserData } from "../../actions";
import { showBackdrop, hideBackdrop } from "../../actions/toggles.js";
import InputWorkingHours from "../Input/InputWorkingHours";
import InputLunchMenuHours from "../Input/InputLunchMenuHours";
import { backend, restaurantTypes } from "../../config";
import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@material-ui/core/FormControl";
import Select from "@material-ui/core/Select";
import InputLabel from "@material-ui/core/InputLabel";
// ICONS
import FastfoodIcon from "@material-ui/icons/Fastfood";
import LocationCityIcon from "@material-ui/icons/LocationCity";
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 ButtonPrimary from "../Input/ButtonPrimary";
// SETUP
const useStyles = makeStyles((theme) => ({
root: {
margin: "auto",
textAlign: "center",
maxHeight: "90vh",
"& .MuiPaper-root": {
width: "auto",
backgroundColor: "#262626",
color: "#bbbbbb",
},
},
closeButton: {
color: "#bbbbbb",
position: "absolute",
right: theme.spacing(1),
top: theme.spacing(1),
},
textInput: {
margin: theme.spacing(2),
"& .MuiInputBase-root": {
color: "#bbbbbb",
},
"& .MuiInputLabel-root": {
color: "#bbbbbb",
},
},
textInputFullWidth: {
marginTop: theme.spacing(2),
marginBottom: theme.spacing(2),
"& .MuiInputBase-root": {
color: "#bbbbbb",
},
"& .MuiInputLabel-root": {
color: "#bbbbbb",
},
"$ .MuiFormHelperText-root": {
color: "#bbbbbb",
},
},
stepLabel: {
"& .MuiStepLabel-label": {
color: "#bbbbbb",
},
"& .MuiStepLabel-active": {
color: "#bbbbbb",
},
},
formControl: {
marginTop: theme.spacing(2),
marginBottom: theme.spacing(2),
minWidth: 100,
maxHeight: 400,
"& .MuiInputBase-root": {
color: "#bbbbbb",
},
"$ .MuiSelect-root": {
color: "#bbbbbb",
},
"& .MuiInputLabel-root": {
color: "#bbbbbb",
},
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}));
export default function NewRestaurant() {
useEffect(() => {
document.title = "Menui - Dodaj lokal";
});
const dispatch = useDispatch();
const initialState = {
name: "",
city: "",
adress: "",
coordinates: [52.354293, 19.42377],
placesId: "",
type: "polska",
imgUrl: "",
workingHours: {
pn: "8:00 - 22:00",
wt: "8:00 - 22:00",
sr: "8:00 - 22:00",
cz: "8:00 - 22:00",
pt: "8:00 - 22:00",
sb: "8:00 - 22:00",
nd: "8:00 - 22:00",
},
lunchHours: "12:30 - 13:30",
description: "",
tags: [],
phone: "",
links: {
facebook: "",
instagram: "",
www: "",
},
nameError: false,
cityError: false,
adressError: false,
descriptionError: false,
charLeft: 400,
};
const steps = ["Informacje", "Zdjęcie", "Lokalizacja"];
const [state, setState] = useState(initialState);
const [activeStep, setActiveStep] = React.useState(0);
const styles = useStyles();
const availableTags = [
"Płatność kartą",
"Lubimy zwierzaki",
"Bezglutenowe",
"Wegańskie",
"Wegetariańskie",
"Podajemy alkohol",
"Dowozimy",
];
const history = useHistory();
const token = useSelector((state) => state.data.userData.jwt);
const availableTypes = restaurantTypes.map((type) => {
return <MenuItem key={type} value={type}>{type}</MenuItem>
});
// HANDLERS
const sendForm = () => {
const formattedTags = prepareTags(state.tags);
const data = {
name: state.name,
city: state.city,
adress: state.adress,
coordinates: state.coordinates,
placesId: state.placesId,
lunchHours: state.lunchHours,
type: state.type,
imgUrl: state.imgUrl,
workingHours: state.workingHours,
description: state.description,
tags: formattedTags,
links: state.links,
phone: state.phone,
hidden: false,
};
dispatch(showBackdrop());
axios({
url: backend + "restaurant",
method: "POST",
data: data,
headers: {
"x-auth-token": token,
},
})
.then((response) => {
dispatch(hideBackdrop());
dispatch(
notification(
"Lokal został dodany, aktywuj subskrypcję, aby był widoczny w wynikach wyszukiwania.",
"success"
)
);
dispatch(refreshUserData(token));
history.push("/");
})
.catch((error) => {
dispatch(hideBackdrop());
console.log(error);
dispatch(
notification(
"Wystąpił nieoczekiwany błąd, przepraszamy za utrudnienia. Spróbuj ponownie za chwilę.",
"error"
)
);
history.push("/");
});
};
const setLunchHours = (hours) => {
setState({ ...state, lunchHours: hours });
}
const setCoordinatesAndPlacesID = (coordinates, placesID) => {
if (!placesID) {
setState({ ...state, coordinates: coordinates });
} else {
setState({ ...state, coordinates: coordinates, placesId: placesID });
}
};
const handleNextButton = () => {
switch (activeStep) {
case 0:
if (validateForm()) {
setActiveStep(1);
}
break;
case 1:
if (!validator.isEmpty(state.imgUrl)) {
setActiveStep(2);
}
break;
case 2:
sendForm();
break;
default:
break;
}
};
const handleImageUploaded = (link) => {
setState({ ...state, imgUrl: link });
};
const handleDescriptionChange = (event) => {
let stringLength = event.target.value.length;
let charleft = 400 - stringLength;
setState({ ...state, description: event.target.value, charLeft: charleft });
};
const handlePreviousButton = () => {
setActiveStep(activeStep - 1);
};
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
);
};
// COMPONENTS
return (
<div>
<Dialog
className={styles.root}
open={true}
aria-labelledby="newRestaurant-title"
>
<DialogTitle id="newRestaurant-title">Dodaj Lokal</DialogTitle>
<IconButton
className={styles.closeButton}
onClick={() => history.goBack()}
aria-label="close"
>
<CloseIcon />
</IconButton>
<Divider />
<DialogContent>
<Stepper activeStep={activeStep} alternativeLabel>
{steps.map((label) => (
<Step key={label}>
<StepLabel className={styles.stepLabel}>{label}</StepLabel>
</Step>
))}
</Stepper>
{activeStep === 0 && (
<Paper variant="outlined">
<div className="newRestaurant-content-fullwidth">
<TextField
className={styles.textInputFullWidth}
fullWidth
required
error={state.nameError}
value={state.name}
label="Nazwa lokalu"
placeholder="np. Pierożek"
variant="outlined"
onChange={(event) =>
setState({ ...state, name: event.target.value })
}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<FastfoodIcon color="primary" />
</InputAdornment>
),
}}
/>
</div>
<div className="newRestaurant-content-fullwidth">
<FormControl
variant="outlined"
required
fullWidth
className={styles.formControl}
>
<InputLabel id="category-select">Kuchnia</InputLabel>
<Select
labelId="category-select"
id="category"
value={state.type}
required
onChange={(event) => setState({ ...state, type: event.target.value })}
>
{availableTypes}
</Select>
</FormControl>
</div>
<div className="newRestaurant-content">
<TextField
className={styles.textInput}
required
label="Miasto"
placeholder="np. Ciechanów"
error={state.cityError}
value={state.city}
variant="outlined"
onChange={(event) =>
setState({ ...state, city: event.target.value })
}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<LocationCityIcon color="primary" />
</InputAdornment>
),
}}
/>
<TextField
className={styles.textInput}
required
label="Adres"
placeholder="np. Akacjowa 13"
variant="outlined"
error={state.adressError}
value={state.adress}
onChange={(event) =>
setState({ ...state, adress: event.target.value })
}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<LocationCityIcon color="primary" />
</InputAdornment>
),
}}
/>
<InputWorkingHours
hours={state.workingHours}
setHours={(hours) =>
setState({ ...state, workingHours: hours })
}
/>
<div className="newRestaurant-content-fullwidth">
<div className="workingHours-container" style={{ marginTop: 14 }}>
<InputLunchMenuHours nieczynne={!state.lunchHours} hours={state.lunchHours} changeValue={(value) => setLunchHours(value)}/></div>
</div>
<div className="newRestaurant-content-fullwidth">
<TextField
className={styles.textInputFullWidth}
fullWidth
label="Opis"
value={state.description}
onChange={(event) => handleDescriptionChange(event)}
multiline
error={state.descriptionError}
rows={3}
rowsMax={8}
variant="outlined"
helperText={"Pozostałe znaki: " + state.charLeft}
FormHelperTextProps={{
style: { color: "#bbbbbb" },
}}
required
/>
<Autocomplete
multiple
options={availableTags}
value={state.tags}
onChange={(event, values) =>
setState({ ...state, tags: values })
}
renderInput={(params) => (
<TextField
{...params}
className={styles.textInputFullWidth}
variant="outlined"
label="Tagi"
placeholder="Wybierz tagi"
/>
)}
/>
</div>
<TextField
className={styles.textInput}
label="Telefon"
type="tel"
variant="outlined"
value={state.phone}
onChange={(event) =>
setState({ ...state, phone: event.target.value })
}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<PhoneIcon color="primary" />
</InputAdornment>
),
}}
/>
<TextField
className={styles.textInput}
label="Facebook"
variant="outlined"
value={state.links.facebook}
onChange={(event) =>
setState({
...state,
links: { ...state.links, facebook: event.target.value },
})
}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<FacebookIcon color="primary" />
</InputAdornment>
),
}}
/>
<TextField
className={styles.textInput}
label="Instagram"
variant="outlined"
value={state.links.instagram}
onChange={(event) =>
setState({
...state,
links: { ...state.links, instagram: event.target.value },
})
}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<InstagramIcon color="primary" />
</InputAdornment>
),
}}
/>
<TextField
className={styles.textInput}
label="www"
variant="outlined"
value={state.links.www}
onChange={(event) =>
setState({
...state,
links: { ...state.links, www: event.target.value },
})
}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<LanguageIcon color="primary" />
</InputAdornment>
),
}}
/>
</div>
</Paper>
)}
{activeStep === 1 && (
<Paper>
<h4>Dodaj zdjęcie lokalu.</h4>
<ImageUpload
img={state.imgUrl}
onUpload={(link) => handleImageUploaded(link)}
/>
</Paper>
)}
{activeStep === 2 && (
<Paper>
<p>
Kliknij w miejscu, w którym znajduje się Twoja restauracja, by
dodać marker. <br />
Jeżeli lokal znajduje się na mapie - kliknij jego ikonkę.
</p>
<InputGoogleMaps
setCoordinates={(coordinates, placesID) =>
setCoordinatesAndPlacesID(coordinates, placesID)
}
coordinates={state.coordinates}
/>
</Paper>
)}
<div className="newRestaurant-bottom">
{activeStep !== 0 && (
<ButtonPrimary onClick={handlePreviousButton} text="Cofnij" />
)}
<ButtonSecondary
onClick={handleNextButton}
text={activeStep === 2 ? "Dodaj lokal" : "Dalej"}
/>
</div>
</DialogContent>
</Dialog>
</div>
);
}