web client v0.9 (restaurant view)

This commit is contained in:
2020-10-04 17:24:36 +02:00
parent 21ea3f821e
commit b3ade3de7d
15 changed files with 456 additions and 36 deletions

View File

@@ -25,6 +25,12 @@
overflow: auto;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
.logo {
height: 5rem;
margin: 16px;

View File

@@ -46,3 +46,8 @@ export const openInNewTab = (url) => {
const newWindow = window.open(url, "_blank", "noopener,noreferrer");
if (newWindow) newWindow.opener = null;
};
export const formatDateBasic = (input) => {
const date = new Date(input);
return date.toLocaleString("pl-PL", { dateStyle: "long" });
};

View File

@@ -78,7 +78,6 @@ export const fetchRestaurant = (id) => {
.then((response) => {
dispatch(setRestaurant(response.data));
dispatch(toggles.hideDishes());
dispatch(push("/restaurant"));
dispatch(fetchAllDishes(id));
})
.catch((err) =>

View File

@@ -14,6 +14,7 @@ import RestaurantMenuIcon from "@material-ui/icons/RestaurantMenu";
import AddIcon from "@material-ui/icons/Add";
import Badge from "@material-ui/core/Badge";
import SearchIcon from "@material-ui/icons/Search";
import { formatDateBasic } from "../../Services.js";
//--------------
import EditRestaurantInfo from "../EditRestaurant/EditRestaurantInfo";
import EditRestaurantLocation from "../EditRestaurant/EditRestaurantLocation";
@@ -66,7 +67,9 @@ export default function EditRestaurant(props) {
badgeData.secondaryText = "Nieaktywna";
} else {
badgeData.color = "primary";
badgeData.secondaryText = `Aktywna do: ${restaurant.subscriptionDue}`;
badgeData.secondaryText = `Aktywna do: ${formatDateBasic(
restaurant.subscriptionDue
)}`;
}
};
dispatch(fetchAllDishes(restaurant._id));

View File

@@ -0,0 +1,64 @@
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 ButtonSecondary from "../Input/ButtonSecondary";
import ButtonPrimary from "../Input/ButtonPrimary";
import IconButton from "@material-ui/core/IconButton";
import CloseIcon from "@material-ui/icons/Close";
export default function PasswordConfirmation(props) {
const loginStyles = makeStyles((theme) => ({
root: {
textAlign: "center",
"& .MuiPaper-root": {
backgroundColor: "#262626",
color: "#bbbbbb",
},
minWidth: "250px",
},
closeButton: {
color: "#bbbbbb",
position: "absolute",
right: theme.spacing(1),
top: theme.spacing(1),
},
}));
const loginClass = loginStyles();
return (
<Dialog
className={loginClass.root}
onClose={props.cancel}
open={props.open}
aria-labelledby="login-title"
>
<DialogTitle id="login-title">Aktywacja subskrypcji</DialogTitle>
<IconButton
className={loginClass.closeButton}
onClick={props.cancel}
aria-label="close"
>
<CloseIcon />
</IconButton>
<Divider />
<DialogContent>
<p>
Płatność została rozpoczęta. Jeżeli chcesz zrealizować teraz,
kliknij przycisk "Zapłać" (zostaniesz przekierowany do portalu
Przelewy24). Jeżeli nie masz teraz ochoty zapłacić, na swoim adresie
email znajdziesz ten sam link do płatności oraz fakturę pro-forma.
(Link do płatności jest ważny 7 dni - po upływie tego czasu transakcja
jest automatycznie anulowana)
</p>
<div className="yesno-dialog-buttons">
<ButtonPrimary onClick={props.cancel} text="Zamknij" />
<ButtonSecondary onClick={props.accept} text="Zapłać" />
</div>
</DialogContent>
</Dialog>
);
}

View File

@@ -1,9 +1,124 @@
import React from "react";
import React, { useState } from "react";
import ButtonSecondary from "../Input/ButtonSecondary";
import Divider from "@material-ui/core/Divider";
import axios from "axios";
import { useDispatch, useSelector } from "react-redux";
import { notification, refreshUserData } from "../../actions";
import { backend } from "../../config.js";
import { showBackdrop, hideBackdrop } from "../../actions/toggles";
import PaymentDialog from "../Dialogs/PaymentDialog";
import { openInNewTab, formatDateBasic } from "../../Services.js";
export default function EditRestaurantSubscription(props) {
const { subscriptionActive, subscriptionDue } = props.restaurant;
const initialState = {
transactionId: "",
open: false,
};
const [state, setState] = useState(initialState);
const userData = useSelector((state) => state.data.userData);
const token = userData.jwt;
const dispatch = useDispatch();
const handleActivateSubscription = (type) => {
const data = {
restaurantId: props.restaurant._id,
userData: {
firstname: userData.firstname,
lastname: userData.lastname,
userId: userData.userId,
userEmail: userData.userEmail,
NIP: userData.billing.NIP,
adress: userData.billing.adress,
companyName: userData.billing.companyName,
},
type: type,
};
dispatch(showBackdrop());
axios({
method: "POST",
url: backend + "/restaurant/subscription",
data: data,
headers: {
"x-auth-token": token,
},
})
.then((response) => {
dispatch(hideBackdrop());
if (response.status === 200) {
setState({ ...state, open: true });
} else {
dispatch(notification("Wystąpił błąd, spróbuj ponownie.", "error"));
}
})
.catch((error) => {
dispatch(hideBackdrop());
dispatch(notification("Wystąpił błąd, spróbuj ponownie.", "error"));
});
};
const onCancel = () => {
setState({ ...state, open: false });
};
const onAccept = () => {
dispatch(refreshUserData(token));
openInNewTab("https://secure.przelewy24.pl/trnRequest/0");
};
return (
<div className="editRestaurant-tab">
<p>Subscription</p>
<div className="editSubscription-tab">
<PaymentDialog open={state.open} cancel={onCancel} accept={onAccept} />
<div className="subscription-text">
<h3>Subskrypcja</h3>
{subscriptionActive ? (
<h5>Aktywna do: {formatDateBasic(subscriptionDue)}</h5>
) : (
<h5>Nieaktywna</h5>
)}
</div>
{!subscriptionActive && (
<div>
<div className="subscription-cards">
<div className="subscription-card">
<h1>1 rok</h1>
<Divider style={{ width: "100%" }} />
<h2>
<strike>600</strike> 500zł (netto)
</h2>
<p>Aktywuj subskrypcję na 12 miesięcy i zapłać mniej.</p>
<Divider style={{ width: "100%" }} />
<ButtonSecondary
text="Aktywuj"
onClick={() => handleActivateSubscription(12)}
/>
</div>
<div className="subscription-card">
<h1>1 miesiąc</h1>
<Divider style={{ width: "100%" }} />
<h2>50 (netto)</h2>
<p>Aktywuj subskrypcję na jeden miesiąc.</p>
<Divider style={{ width: "100%" }} />
<ButtonSecondary
text="Aktywuj"
onClick={() => handleActivateSubscription(1)}
/>
</div>
</div>
<p>
Aktywuj subskrypcję, aby Twoja restauracja była widoczna. Na tydzień
przed końcem subskrypcji wyślemy Ci email z możliwością opłacenia
subskrypcji na kolejny okres.
</p>
</div>
)}
{subscriptionActive && (
<p>
Subskrypcja jest aktywna - Twoja restauracja jest dostępna do
przeglądania dla użytkowników. Jeżeli chcesz żeby restauracja była
niewidoczna, przejdź do zakładki "Informacje"
</p>
)}
</div>
);
}

View File

@@ -19,6 +19,7 @@ import { notification, refreshUserData } from "../../actions";
import { showBackdrop, hideBackdrop } from "../../actions/toggles.js";
import { backend } from "../../config";
import Tooltip from "@material-ui/core/Tooltip";
import LunchSetDishList from "../Output/LunchSetDishList";
const useStyles = makeStyles((theme) => ({
root: {
@@ -86,6 +87,7 @@ export default function EditCategoriesList(props) {
sendForm("delete", selectedSet);
setOpen(false);
};
const SetList = props.lunchMenu.map((set) => {
return (
<Accordion key={set.id} className={classes.root}>
@@ -107,6 +109,7 @@ export default function EditCategoriesList(props) {
</Tooltip>
</div>
</AccordionSummary>
<LunchSetDishList lunchSet={set} restaurantId={props.restaurantId} />
</Accordion>
);
});

View File

@@ -10,7 +10,7 @@ import Switch from "@material-ui/core/Switch";
import axios from "axios";
import { backend } from "../../config";
import YesNo from "../Dialogs/YesNo";
import { notification, fetchAllDishes } from "../../actions";
import { notification, fetchAllDishes, refreshUserData } from "../../actions";
import Tooltip from "@material-ui/core/Tooltip";
import FastfoodIcon from "@material-ui/icons/Fastfood";
import AddToSet from "../Dialogs/AddToSet";
@@ -110,6 +110,7 @@ export default function EditDishList(props) {
.then((response) => {
dispatch(hideBackdrop());
dispatch(notification("Dodano do zestawu.", "success"));
dispatch(refreshUserData(token));
})
.catch((error) => {
dispatch(hideBackdrop());

View File

@@ -0,0 +1,24 @@
import React from "react";
import { GoogleMap, LoadScript, Marker } from "@react-google-maps/api";
function GoogleMapStatic(props) {
const containerStyle = {
width: "50%",
height: "500px",
};
const center = {
lat: props.coordinates[0],
lng: props.coordinates[1],
};
return (
<LoadScript googleMapsApiKey="AIzaSyDAlZSiBanP52qpZ1kaH06XkuA2zndLUd8">
<GoogleMap mapContainerStyle={containerStyle} center={center} zoom={7}>
<Marker position={center} visible={true} />
</GoogleMap>
</LoadScript>
);
}
export default React.memo(GoogleMapStatic);

View File

@@ -0,0 +1,115 @@
import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
import IconButton from "@material-ui/core/IconButton";
import DeleteIcon from "@material-ui/icons/Delete";
import axios from "axios";
import { backend } from "../../config";
import YesNo from "../Dialogs/YesNo";
import { notification, refreshUserData } from "../../actions";
import Tooltip from "@material-ui/core/Tooltip";
import { hideBackdrop, showBackdrop } from "../../actions/toggles";
export default function EditDishList(props) {
const [open, setOpen] = useState(false);
const [selectedDish, setDish] = useState("");
const dispatch = useDispatch();
const token = useSelector((state) => state.data.userData.jwt);
const filterDishes = (dishes, setDishes) => {
let result = [];
dishes.map((dish) => {
if (setDishes.includes(dish._id)) {
result.push(dish);
}
return true;
});
return result;
};
const selectDish = (dishId) => {
setDish(dishId);
setOpen(true);
};
const onCancel = () => {
setOpen(false);
};
const onAccept = () => {
setOpen(false);
removeFromSet();
};
const removeFromSet = () => {
const data = {
setName: props.lunchSet.lunchSetName,
restaurantId: props.restaurantId,
dishId: selectedDish,
action: "delete",
};
dispatch(showBackdrop());
axios({
method: "POST",
url: backend + "/restaurant/lunch",
data: data,
headers: {
"x-auth-token": token,
},
})
.then((response) => {
dispatch(hideBackdrop());
dispatch(notification("Zmodyfikowano zestaw.", "success"));
dispatch(refreshUserData(token));
})
.catch((error) => {
dispatch(hideBackdrop());
dispatch(notification("Wystąpił błąd.", "error"));
});
};
const allDishes = useSelector((state) => state.dishes);
const thisSetDishes = filterDishes(allDishes, props.lunchSet.lunchSetDishes);
const Dishes = thisSetDishes.map((dish) => {
return (
<ListItem key={dish._id}>
<div className="editRestaurant-dish">
<div className="editRestaurant-dishLeft">
<div
className="editRestaurant-dishImg"
style={
dish.imgUrl !== "empty"
? { backgroundImage: `url(${dish.imgUrl})` }
: { backgroundColor: "#7e7e7e" }
}
></div>
<h3>{dish.name}</h3>
</div>
<div className="editRestaurant-dishRight">
<Tooltip title="Usuń">
<IconButton
color="primary"
component="span"
onClick={() => selectDish(dish._id)}
>
<DeleteIcon />
</IconButton>
</Tooltip>
</div>
</div>
</ListItem>
);
});
return (
<List>
<YesNo open={open} cancel={onCancel} accept={onAccept} />
{thisSetDishes.length === 0 ? (
<ListItem style={{ marginLeft: "14px", fontSize: "12px" }}>
Zestaw jest pusty.
</ListItem>
) : (
Dishes
)}
</List>
);
}

View File

@@ -1,33 +1,45 @@
import React from "react";
import React, { useEffect } from "react";
import PictogramsSeparate from "./PictogramsSeparate";
import DishList from "./DishList";
import CircularProgress from "@material-ui/core/CircularProgress";
import { useParams } from "react-router-dom";
import { extractTags } from "../../Services";
import { useSelector } from "react-redux";
import { useSelector, useDispatch } from "react-redux";
import { fetchRestaurant } from "../../actions";
import GoogleMapStatic from "./GoogleMapStatic";
export default function Restaurant(props) {
const restaurant = useSelector((state) => state.restaurant);
const { id } = useParams();
const dispatch = useDispatch();
const showDishList = useSelector((state) => state.data.showDishList);
useEffect(() => {
if (restaurant._id !== id) {
dispatch(fetchRestaurant(id));
} else {
document.title = restaurant.name;
}
});
return (
<div className="restaurant-container">
<div className="restaurant-content">
<div className="restaurant-info">
<div className="restaurant-left">
<div
className="restaurant-hero"
style={{ backgroundImage: "url(" + restaurant.imgUrl + ")" }}
></div>
<div className="restaurant-info">
<h1>{restaurant.name}</h1>
<p>{restaurant.description}</p>
<hr />
<p>
Miejscowość:{" "}
<span className="restaurant-span">{restaurant.city}</span>
</p>
<p>
Godziny pracy:{" "}
<span className="restaurant-span">{restaurant.workingHours}</span>
Adres:{" "}
<span className="restaurant-span">
{restaurant.city}, {restaurant.adress}
</span>
</p>
<p>Godziny pracy: </p>
{restaurant.phone && (
<p>
Kontakt:{" "}
@@ -39,6 +51,8 @@ export default function Restaurant(props) {
<PictogramsSeparate pictograms={extractTags(restaurant.tags)} />
</div>
</div>
</div>
<div className="restaurant-content">
<div className="restaurant-dishes">
<h3>Menu</h3>
{showDishList === false && <CircularProgress />}
@@ -48,3 +62,9 @@ export default function Restaurant(props) {
</div>
);
}
//
/* {restaurant.location !== undefined && (
<GoogleMapStatic coordinates={restaurant.location.coordinates} />
)} */

View File

@@ -32,6 +32,7 @@ h4 {
.carddish-img {
background-color: $secondary-color;
background-size: cover;
width: 150px;
height: 150px;
border-radius: 10px;

View File

@@ -125,3 +125,62 @@
.editRestaurant-dish:hover {
background-color: #3b3b3b;
}
.editSubscription-tab {
padding: 16px;
display: flex;
flex-flow: column;
align-items: center;
width: 100%;
overflow: auto;
}
.subscription-text {
display: flex;
background-color: #242424;
width: 100%;
justify-content: space-between;
border-radius: 8px;
h3 {
font-weight: 300;
padding-left: 14px;
}
h5 {
color: #c0c0c0;
font-weight: 400;
padding-right: 14px;
font-size: 14px;
}
}
.subscription-cards {
display: flex;
justify-content: space-around;
margin-top: 18px;
}
.subscription-card {
background-color: #242424;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
min-width: 260px;
max-width: 300px;
border-radius: 8px;
h1 {
font-weight: 200;
}
h2 {
font-weight: 300;
font-size: 1.2rem;
color: #c0c0c0;
}
p {
font-weight: 400;
font-size: 14px;
padding-left: 16px;
padding-right: 16px;
color: #838383;
}
}

View File

@@ -2,8 +2,7 @@
position: fixed;
bottom: 0;
background-color: $dark-gray;
p {
color: white;
color: rgb(180, 180, 180);
}
}

View File

@@ -1,15 +1,19 @@
.restaurant-container {
width: 100vw;
height: 100%;
min-width: 70%;
min-height: 600px;
max-height: 80vh;
max-width: 80%;
background-color: $dark-gray;
border-radius: 15px;
display: flex;
justify-content: center;
align-items: flex-start;
flex-direction: row;
overflow: auto;
h1 {
font-weight: 100;
color: $gray;
background-color: $dark-gray;
padding: 15px;
font-size: 3rem;
font-size: 2rem;
margin-bottom: 16px;
margin-top: 0;
}
@@ -22,11 +26,16 @@
}
}
.restaurant-left {
max-width: 30%;
background-color: #1d1d1d;
height: 100%;
}
.restaurant-hero {
width: 100%;
height: 500px;
height: 360px;
border-top-left-radius: 15px;
border-top-right-radius: 15px;
background-size: cover;
}
@@ -44,7 +53,7 @@
.restaurant-dishes {
min-width: 60%;
h3 {
font-size: 1.6rem;
font-size: 1.2rem;
font-weight: 400;
}
}
@@ -52,21 +61,18 @@
.restaurant-content {
padding-top: 10rem;
padding-bottom: 10rem;
width: 100%;
display: flex;
justify-content: space-between;
justify-content: center;
align-items: flex-start;
min-width: 70%;
}
.restaurant-info {
text-align: center;
max-width: 30%;
background-color: #1d1d1d;
box-shadow: -2px 10px 20px rgba(0, 0, 0, 0.212);
border-radius: 15px;
p {
font-size: 1rem;
font-size: 0.9rem;
font-weight: 300;
margin-top: 8px;
margin-bottom: 8px;
@@ -83,6 +89,6 @@
}
.restaurant-span {
font-weight: 500;
font-weight: 400;
color: $gray;
}