diff --git a/src/App.js b/src/App.js index 535630e..1c063e9 100644 --- a/src/App.js +++ b/src/App.js @@ -15,6 +15,9 @@ import NewRestaurant from "./components/Dialogs/NewRestaurant"; import RegisterDialog from "./components/Dialogs/RegisterDialog"; import ForgotPassword from "./components/Dialogs/ForgotPassword"; import ResetPassword from "./components/Dialogs/ResetPassword"; +import Contact from "./components/Output/Contact"; +import Settings from "./components/Dialogs/Settings"; +import EditRestaurant from "./components/Dialogs/EditRestaurant"; const theme = createMuiTheme({ palette: { @@ -62,11 +65,18 @@ function App(props) { + + + } /> - } /> + } /> + } + /> diff --git a/src/Services.js b/src/Services.js index d9ef5b4..7de2014 100644 --- a/src/Services.js +++ b/src/Services.js @@ -10,3 +10,26 @@ export function extractTags(tags) { } return results; } + +function findInArray(array, string) { + const result = array.indexOf(string) > -1; + return result; +} + +export function prepareTags(tags) { + let outTags = { + cardPayments: findInArray(tags, "Płatność kartą"), + petFriendly: findInArray(tags, "Lubimy zwierzaki"), + glutenFree: findInArray(tags, "Bezglutenowe"), + vegan: findInArray(tags, "Wegańskie"), + vegetarian: findInArray(tags, "Wegetariańskie"), + alcohol: findInArray(tags, "Podajemy alkohol"), + delivery: findInArray(tags, "Dowozimy"), + }; + return outTags; +} + +export const openInNewTab = (url) => { + const newWindow = window.open(url, "_blank", "noopener,noreferrer"); + if (newWindow) newWindow.opener = null; +}; diff --git a/src/actions/index.js b/src/actions/index.js index 56f86a0..ea51be4 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -115,15 +115,22 @@ export const tryLogin = (username, password) => { dispatch( toggles.setLoggedIn( response.data.firstname, + response.data.lastname, jwt, response.data.id, - response.data.email + response.data.email, + response.data.NIP, + response.data.adress, + response.data.companyName, + response.data.restaurants ) ); dispatch(push("/")); }) .catch((err) => { - if (err.response.status === 404) { + if (!err.response) { + console.log(err); + } else if (err.response.status === 404) { dispatch( toggles.setLoginResult( "Użytkownik o podanym adresie email nie istnieje." diff --git a/src/actions/toggles.js b/src/actions/toggles.js index 2b66e2d..178dece 100644 --- a/src/actions/toggles.js +++ b/src/actions/toggles.js @@ -10,10 +10,30 @@ export const hideDishes = () => { }; }; -export const setLoggedIn = (username, jwt, id, email) => { +export const setLoggedIn = ( + firstname, + lastname, + jwt, + userId, + email, + NIP, + adress, + companyName, + restaurants +) => { return { type: "SET_LOGGEDIN", - payload: { username: username, jwt: jwt, id: id, email: email }, + payload: { + firstname: firstname, + lastname: lastname, + jwt: jwt, + userId: userId, + email: email, + NIP: NIP, + adress: adress, + companyName: companyName, + restaurants: restaurants, + }, }; }; diff --git a/src/components/Dialogs/EditRestaurant.js b/src/components/Dialogs/EditRestaurant.js new file mode 100644 index 0000000..e3b84e0 --- /dev/null +++ b/src/components/Dialogs/EditRestaurant.js @@ -0,0 +1,50 @@ +import React from "react"; +import PictogramsSeparate from "../Output/PictogramsSeparate"; +import DishList from "../Output/DishList"; +import CircularProgress from "@material-ui/core/CircularProgress"; +import { extractTags } from "../../Services"; +import { useSelector } from "react-redux"; + +export default function EditRestaurant(props) { + const restaurant = useSelector((state) => state.restaurant); + const showDishList = useSelector((state) => state.data.showDishList); + + return ( +
+
+
+
+

{restaurant.name}

+

{restaurant.description}

+
+

+ Miejscowość:{" "} + {restaurant.city} +

+

+ Godziny pracy:{" "} + {restaurant.workingHours} +

+ {restaurant.phone && ( +

+ Kontakt:{" "} + {restaurant.phone} +

+ )} +
+
+ +
+
+
+

Menu

+ {showDishList === false && } + {showDishList === true && } +
+
+
+ ); +} diff --git a/src/components/Dialogs/NewRestaurant.js b/src/components/Dialogs/NewRestaurant.js index a1ec7c1..469510a 100644 --- a/src/components/Dialogs/NewRestaurant.js +++ b/src/components/Dialogs/NewRestaurant.js @@ -19,12 +19,15 @@ 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"; // 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 TwitterIcon from "@material-ui/icons/Twitter"; +import InstagramIcon from "@material-ui/icons/Instagram"; import LanguageIcon from "@material-ui/icons/Language"; import ButtonPrimary from "../Input/ButtonPrimary"; @@ -100,18 +103,19 @@ export default function NewRestaurant() { tags: [], phone: "", facebook: "", - twitter: "", + instagram: "", www: "", nameError: false, cityError: false, adressError: false, descriptionError: false, charLeft: 400, - open: true, - response: "", + 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 = [ @@ -124,26 +128,59 @@ export default function NewRestaurant() { "Dowozimy", ]; const history = useHistory(); + const token = useSelector((state) => state.data.jwt); // HANDLERS const sendForm = () => { - // format tags - const formattedTags; + const formattedTags = prepareTags(state.tags); const data = { name: state.name, - city: state.city, - adress: state.adress, - coordinates: state.coordinates, - placesId: state.placesId, - imgURL: state.imgURL, - workingHours: `${state.hoursFrom} - ${state.hoursTo}`, - description: state.description, - tags: formattedTags, - links: {}, - phone: request.phone, - hidden: request.hidden, - } + city: state.city, + adress: state.adress, + coordinates: state.coordinates, + placesId: state.placesId, + imgURL: state.imgURL, + workingHours: `${state.hoursFrom} - ${state.hoursTo}`, + description: state.description, + tags: formattedTags, + facebook: state.facebook, + instagram: state.instagram, + www: state.www, + phone: state.phone, + hidden: false, + }; + setOpen(false); + axios({ + url: "http://localhost:4000/restaurant", + method: "POST", + data: data, + headers: { + "x-auth-token": token, + }, + }) + .then((response) => { + setLoading(false); + setState({ + ...state, + response: + "Lokal został dodany, aktywuj subskrypcję, aby był widoczny w wynikach wyszukiwania.", + }); + setTimeout(() => { + history.push("/"); + }, 5000); + }) + .catch((error) => { + setLoading(false); + setState({ + ...state, + response: + "Wystąpił nieoczekiwany błąd, przepraszamy za utrudnienia. Spróbuj ponownie za chwilę.", + }); + setTimeout(() => { + history.push("/"); + }, 5000); + }); }; const setCoordinatesAndPlacesID = (coordinates, placesID) => { @@ -167,7 +204,6 @@ export default function NewRestaurant() { } break; case 2: - setState({ ...state, open: false }); sendForm(); break; default: @@ -219,16 +255,16 @@ export default function NewRestaurant() { return (
- {!state.open && ( + {!open && ( )} Dodaj Lokal @@ -372,7 +408,7 @@ export default function NewRestaurant() { className={styles.textInputFullWidth} variant="outlined" label="Tagi" - placeholder="Dodaj tagi" + placeholder="Wybierz tagi" /> )} /> @@ -412,16 +448,16 @@ export default function NewRestaurant() { /> - setState({ ...state, twitter: event.target.value }) + setState({ ...state, instagram: event.target.value }) } InputProps={{ startAdornment: ( - + ), }} @@ -456,6 +492,11 @@ export default function NewRestaurant() { )} {activeStep === 2 && ( +

+ Kliknij w miejscu, w którym znajduje się Twoja restauracja, by + dodać marker.
+ Jeżeli lokal znajduje się na mapie - kliknij jego ikonkę. +

setCoordinatesAndPlacesID(coordinates, placesID) diff --git a/src/components/Dialogs/Settings.js b/src/components/Dialogs/Settings.js new file mode 100644 index 0000000..8765eb4 --- /dev/null +++ b/src/components/Dialogs/Settings.js @@ -0,0 +1,153 @@ +import React, { useState } from "react"; +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 { makeStyles } from "@material-ui/core/styles"; +import ButtonSecondary from "../Input/ButtonSecondary"; +import ButtonPrimary from "../Input/ButtonPrimary"; +import IconButton from "@material-ui/core/IconButton"; +import TextField from "@material-ui/core/TextField"; +import CloseIcon from "@material-ui/icons/Close"; +import { useSelector } from "react-redux"; +import { useHistory } from "react-router-dom"; + +const useStyles = makeStyles((theme) => ({ + root: { + textAlign: "center", + "& .MuiPaper-root": { + minWidth: "30%", + 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", + }, + }, + timePicker: { + margin: theme.spacing(2), + "& .MuiInputBase-root": { + color: "#bbbbbb", + }, + "& .MuiInputLabel-root": { + color: "#bbbbbb", + }, + }, + stepLabel: { + "& .MuiStepLabel-label": { + color: "#bbbbbb", + }, + "& .MuiStepLabel-active": { + color: "#bbbbbb", + }, + }, +})); + +export default function Settings() { + const history = useHistory(); + const style = useStyles(); + const data = useSelector((state) => state.data.userData); + const initialState = { + firstname: data.firstname, + lastname: data.lastname, + email: data.userEmail, + NIP: data.billing.NIP, + adress: data.billing.adress, + companyName: data.billing.companyName, + }; + const [state, setState] = useState(initialState); + return ( + + Ustawienia konta + {}} + aria-label="close" + > + + + + + + setState({ ...state, firstname: event.target.value }) + } + label="Imię" + variant="outlined" + /> + + setState({ ...state, lastname: event.target.value }) + } + label="Nazwisko" + variant="outlined" + /> + + setState({ ...state, email: event.target.value }) + } + label="Email" + variant="outlined" + /> + + setState({ ...state, companyName: event.target.value }) + } + label="Nazwa firmy" + variant="outlined" + /> + setState({ ...state, NIP: event.target.value })} + label="NIP" + variant="outlined" + /> + + setState({ ...state, adress: event.target.value }) + } + label="Adres firmy" + variant="outlined" + /> + history.push("/")} text="Anuluj" /> + + + + ); +} diff --git a/src/components/Input/ImageUpload.js b/src/components/Input/ImageUpload.js index e29197c..e362081 100644 --- a/src/components/Input/ImageUpload.js +++ b/src/components/Input/ImageUpload.js @@ -6,7 +6,7 @@ import axios from "axios"; export default function ImageUpload(props) { const [imagePreviewURL, setPreviewURL] = useState(props.img); let showCircle = false; - const token = useSelector((state) => state.data.jwt); + const token = useSelector((state) => state.data.userData.jwt); const handleInputChange = (event) => { let data = new FormData(); @@ -33,7 +33,7 @@ export default function ImageUpload(props) { let imagePreview = (
- {showCircle ? : "Proszę wybrać obraz."} + {showCircle ? : "Proszę wybrać obraz. (max. 2MB)"}
); if (imagePreviewURL) { diff --git a/src/components/Output/Contact.js b/src/components/Output/Contact.js new file mode 100644 index 0000000..9478fd4 --- /dev/null +++ b/src/components/Output/Contact.js @@ -0,0 +1,58 @@ +import React from "react"; +import Paper from "@material-ui/core/Paper"; +import List from "@material-ui/core/List"; +import ListItem from "@material-ui/core/ListItem"; +import ListItemText from "@material-ui/core/ListItemText"; +import Divider from "@material-ui/core/Divider"; +import { makeStyles } from "@material-ui/core/styles"; +import LogoMain from "./logoMain"; + +const useStyles = makeStyles((theme) => ({ + root: { + textAlign: "center", + }, + secondary: { + "& .MuiListItemText-secondary": { + color: "#7b7b7b", + fontSize: "13px", + }, + textAlign: "center", + }, +})); + +export default function Contact() { + const style = useStyles(); + return ( + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/components/Output/InfoDialog.js b/src/components/Output/InfoDialog.js index 4768d47..d471ce1 100644 --- a/src/components/Output/InfoDialog.js +++ b/src/components/Output/InfoDialog.js @@ -1,17 +1,12 @@ import React from "react"; -import CloseIcon from "@material-ui/icons/Close"; 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 CircularProgress from "@material-ui/core/CircularProgress"; -import { useHistory } from "react-router-dom"; export default function InfoDialog(props) { - const history = useHistory(); const loading = props.loading; const loginStyles = makeStyles((theme) => ({ root: { @@ -32,25 +27,12 @@ export default function InfoDialog(props) { const styles = loginStyles(); return ( - history.push("/")} - open={true} - aria-labelledby="title" - > + {props.title} - history.push("/")} - aria-label="close" - > - -

{props.text}

{loading && } -
); diff --git a/src/components/Output/ListItemRestaurant.js b/src/components/Output/ListItemRestaurant.js new file mode 100644 index 0000000..419f1a9 --- /dev/null +++ b/src/components/Output/ListItemRestaurant.js @@ -0,0 +1,62 @@ +import React from "react"; +import ListItemIcon from "@material-ui/core/ListItemIcon"; +import ListItemText from "@material-ui/core/ListItemText"; +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"; + +const useStyles = makeStyles((theme) => ({ + root: { + "& .MuiBadge-colorPrimary": { + backgroundColor: "#13ff00", + }, + "& .MuiBadge-colorError": { + backgroundColor: "#ff0000", + }, + }, + secondary: { + "& .MuiListItemText-secondary": { + color: "#7b7b7b", + fontSize: "13px", + }, + }, +})); + +export default function ListItemRestaurant(props) { + const styles = useStyles(); + const badgeData = { + color: "", + secondaryText: "", + }; + const badgeInit = () => { + if (!props.subscriptionActive || props.subscriptionActive === false) { + badgeData.color = "error"; + badgeData.secondaryText = "Aktywuj sybskrypcję"; + } else { + badgeData.color = "primary"; + badgeData.secondaryText = "Subskrypcja aktywna"; + } + }; + badgeInit(); + + return ( + + + + + + + + + ); +} diff --git a/src/components/Output/Social.js b/src/components/Output/Social.js index 8bcbdd1..b845df8 100644 --- a/src/components/Output/Social.js +++ b/src/components/Output/Social.js @@ -3,17 +3,27 @@ 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"; +import { openInNewTab } from "../../Services.js"; export default function Social(props) { return (
- + openInNewTab("https://www.facebook.com")} + color="secondary" + > - + openInNewTab("https://twitter.com/menuifood")} + color="secondary" + > - + openInNewTab("https://www.instagram.com")} + color="secondary" + >
diff --git a/src/components/TopBar.js b/src/components/TopBar.js index 4f065f0..413482b 100644 --- a/src/components/TopBar.js +++ b/src/components/TopBar.js @@ -13,12 +13,13 @@ import ListItemText from "@material-ui/core/ListItemText"; import ListItemIcon from "@material-ui/core/ListItemIcon"; import HomeIcon from "@material-ui/icons/Home"; import MailIcon from "@material-ui/icons/Mail"; -import PaymentIcon from "@material-ui/icons/Payment"; import { logout } from "../actions"; +import { showRegulamin } from "../actions/toggles.js"; import { makeStyles } from "@material-ui/core/styles"; import ButtonSecondary from "./Input/ButtonSecondary"; import ListSubheader from "@material-ui/core/ListSubheader"; import { useHistory } from "react-router-dom"; +import GavelIcon from "@material-ui/icons/Gavel"; const useStyles = makeStyles((theme) => ({ paper: { @@ -38,7 +39,7 @@ const useStyles = makeStyles((theme) => ({ export default function TopBar() { const classes = useStyles(); const loggedIn = useSelector((state) => state.data.loggedIn); - const username = useSelector((state) => state.data.username); + const username = useSelector((state) => state.data.userData.firstname); const history = useHistory(); const dispatch = useDispatch(); const handleClick = (button) => { @@ -50,18 +51,18 @@ export default function TopBar() { case "logIn": history.push("/login"); break; + case "kontakt": + history.push("/kontakt"); + break; + case "regulamin": + dispatch(showRegulamin()); + break; case "register": history.push("/register"); break; case "logOut": dispatch(logout()); break; - case "myRestaurant": - history.push("/restaurant"); - break; - case "addDish": - history.push("/"); - break; default: return true; } @@ -128,17 +129,17 @@ export default function TopBar() { - handleClick("contact")}> + handleClick("kontakt")}> - handleClick("pricing")}> + handleClick("regulamin")}> - + - + diff --git a/src/components/UserMenu.js b/src/components/UserMenu.js index 136bd1c..b1401bc 100644 --- a/src/components/UserMenu.js +++ b/src/components/UserMenu.js @@ -1,4 +1,5 @@ import React, { useState } from "react"; +import { useSelector } from "react-redux"; import Collapse from "@material-ui/core/Collapse"; import { makeStyles } from "@material-ui/core/styles"; import List from "@material-ui/core/List"; @@ -12,6 +13,7 @@ import FastfoodIcon from "@material-ui/icons/Fastfood"; import AddIcon from "@material-ui/icons/Add"; import SettingsIcon from "@material-ui/icons/Settings"; import { useHistory } from "react-router-dom"; +import ListItemRestaurant from "./Output/ListItemRestaurant"; const useStyles = makeStyles((theme) => ({ root: { @@ -29,17 +31,22 @@ const useStyles = makeStyles((theme) => ({ })); export default function UserMenu(props) { - const [open, setOpen] = useState(false); + const restaurants = useSelector((state) => state.data.userData.restaurants); + const [open, setOpen] = useState(true); const classes = useStyles(); const handleClick = () => { setOpen(!open); }; - const handleAddRestaurant = () => { + const handleButtonClick = (url) => { props.closeMenu(); - history.push("/newRestaurant"); + history.push(url); }; const history = useHistory(); + const viewRestaurants = restaurants.map((restaurant) => { + return ; + }); + return ( } > - + handleButtonClick("/settings")} + disableGutters + > - + - + {open ? : } - + handleButtonClick("/newRestaurant")}> - + + {viewRestaurants} diff --git a/src/reducers/data.js b/src/reducers/data.js index c6d9092..cad8130 100644 --- a/src/reducers/data.js +++ b/src/reducers/data.js @@ -5,6 +5,19 @@ const initialState = { username: "", userId: "", userEmail: "", + userData: { + jwt: "", + firstname: "", + lastname: "", + userId: "", + userEmail: "", + billing: { + NIP: "", + adress: "", + companyName: "", + }, + restaurants: [], + }, dialogs: { registerCircularProgress: false, registerForm: true, @@ -29,19 +42,37 @@ const data = (state = initialState, action) => { return (state = { ...state, loggedIn: true, - username: action.payload.username, - jwt: action.payload.jwt, - userEmail: action.payload.email, - userId: action.payload.id, + userData: { + jwt: action.payload.jwt, + firstname: action.payload.firstname, + lastname: action.payload.lastname, + userId: action.payload.userId, + userEmail: action.payload.email, + billing: { + NIP: action.payload.NIP, + adress: action.payload.adress, + companyName: action.payload.companyName, + }, + restaurants: action.payload.restaurants, + }, }); case "SET_LOGGEDOUT": return (state = { ...state, loggedIn: false, - username: "", - jwt: "", - userEmail: "", - userId: "", + userData: { + jwt: "", + firstname: "", + lastname: "", + userId: "", + userEmail: "", + billing: { + NIP: "", + adress: "", + companyName: "", + }, + restaurants: [], + }, }); case "DIALOG_REGISTER_CIRCLE_SHOW": return (state = { diff --git a/src/styles/NewRestaurant.scss b/src/styles/NewRestaurant.scss index 1e11e3b..ddc3deb 100644 --- a/src/styles/NewRestaurant.scss +++ b/src/styles/NewRestaurant.scss @@ -26,7 +26,7 @@ align-items: center; justify-content: center; font-size: 0.8rem; - background-size: contain; + background-size: cover; background-position: center; }