web client v 0.1

This commit is contained in:
2020-09-17 19:01:01 +02:00
parent 2b378fab25
commit 1c9d9e2021
20 changed files with 794 additions and 208 deletions

91
package-lock.json generated
View File

@@ -4010,6 +4010,14 @@
"resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz",
"integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg=="
}, },
"connected-react-router": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/connected-react-router/-/connected-react-router-6.8.0.tgz",
"integrity": "sha512-E64/6krdJM3Ag3MMmh2nKPtMbH15s3JQDuaYJvOVXzu6MbHbDyIvuwLOyhQIuP4Om9zqEfZYiVyflROibSsONg==",
"requires": {
"prop-types": "^15.7.2"
}
},
"console-browserify": { "console-browserify": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
@@ -6676,6 +6684,19 @@
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
}, },
"history": {
"version": "4.10.1",
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
"integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
"requires": {
"@babel/runtime": "^7.1.2",
"loose-envify": "^1.2.0",
"resolve-pathname": "^3.0.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0",
"value-equal": "^1.0.1"
}
},
"hmac-drbg": { "hmac-drbg": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@@ -8804,6 +8825,15 @@
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==" "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="
}, },
"mini-create-react-context": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.0.tgz",
"integrity": "sha512-b0TytUgFSbgFJGzJqXPKCFCBWigAjpjo+Fl7Vf7ZbKRDptszpppKxXH6DRXEABZ/gcEQczeb0iZ7JvL8e8jjCA==",
"requires": {
"@babel/runtime": "^7.5.5",
"tiny-warning": "^1.0.3"
}
},
"mini-css-extract-plugin": { "mini-css-extract-plugin": {
"version": "0.9.0", "version": "0.9.0",
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz",
@@ -11357,6 +11387,52 @@
"react-is": "^16.9.0" "react-is": "^16.9.0"
} }
}, },
"react-router": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
"integrity": "sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw==",
"requires": {
"@babel/runtime": "^7.1.2",
"history": "^4.9.0",
"hoist-non-react-statics": "^3.1.0",
"loose-envify": "^1.3.1",
"mini-create-react-context": "^0.4.0",
"path-to-regexp": "^1.7.0",
"prop-types": "^15.6.2",
"react-is": "^16.6.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
},
"dependencies": {
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"path-to-regexp": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
"integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
"requires": {
"isarray": "0.0.1"
}
}
}
},
"react-router-dom": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.2.0.tgz",
"integrity": "sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA==",
"requires": {
"@babel/runtime": "^7.1.2",
"history": "^4.9.0",
"loose-envify": "^1.3.1",
"prop-types": "^15.6.2",
"react-router": "5.2.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
}
},
"react-scripts": { "react-scripts": {
"version": "3.4.3", "version": "3.4.3",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.3.tgz", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.4.3.tgz",
@@ -11752,6 +11828,11 @@
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
}, },
"resolve-pathname": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
"integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
},
"resolve-url": { "resolve-url": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@@ -13275,6 +13356,11 @@
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
}, },
"tiny-invariant": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",
"integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw=="
},
"tiny-warning": { "tiny-warning": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
@@ -13669,6 +13755,11 @@
"resolved": "https://registry.npmjs.org/validator/-/validator-13.1.1.tgz", "resolved": "https://registry.npmjs.org/validator/-/validator-13.1.1.tgz",
"integrity": "sha512-8GfPiwzzRoWTg7OV1zva1KvrSemuMkv07MA9TTl91hfhe+wKrsrgVN4H2QSFd/U/FhiU3iWPYVgvbsOGwhyFWw==" "integrity": "sha512-8GfPiwzzRoWTg7OV1zva1KvrSemuMkv07MA9TTl91hfhe+wKrsrgVN4H2QSFd/U/FhiU3iWPYVgvbsOGwhyFWw=="
}, },
"value-equal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
},
"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

@@ -11,11 +11,13 @@
"@testing-library/react": "^9.5.0", "@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1", "@testing-library/user-event": "^7.2.1",
"axios": "^0.19.2", "axios": "^0.19.2",
"connected-react-router": "^6.8.0",
"node-sass": "^4.14.1", "node-sass": "^4.14.1",
"react": "^16.13.1", "react": "^16.13.1",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-ga": "^3.1.2", "react-ga": "^3.1.2",
"react-redux": "^7.2.0", "react-redux": "^7.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "^3.4.3", "react-scripts": "^3.4.3",
"redux": "^4.0.5", "redux": "^4.0.5",
"redux-thunk": "^2.3.0", "redux-thunk": "^2.3.0",

View File

@@ -1,4 +1,5 @@
import React from "react"; import React from "react";
import { Router, Switch, Route } from "react-router-dom";
import "./App.scss"; import "./App.scss";
import TopBar from "./components/TopBar"; import TopBar from "./components/TopBar";
import LogoMain from "./components/Output/logoMain"; import LogoMain from "./components/Output/logoMain";
@@ -8,7 +9,11 @@ import SearchResults from "./components/Output/SearchResults";
import Restaurant from "./components/Output/Restaurant"; import Restaurant from "./components/Output/Restaurant";
import Dialogs from "./components/Dialogs"; import Dialogs from "./components/Dialogs";
import { createMuiTheme, ThemeProvider } from "@material-ui/core/styles"; import { createMuiTheme, ThemeProvider } from "@material-ui/core/styles";
import { useSelector } from "react-redux"; import LoginDialog from "./components/Dialogs/LoginDialog";
import NewRestaurant from "./components/Dialogs/NewRestaurant";
import RegisterDialog from "./components/Dialogs/RegisterDialog";
import ForgotPassword from "./components/Dialogs/ForgotPassword";
import ResetPassword from "./components/Dialogs/ResetPassword";
const theme = createMuiTheme({ const theme = createMuiTheme({
palette: { palette: {
@@ -24,30 +29,54 @@ const theme = createMuiTheme({
}, },
}); });
function App() { function App(props) {
const appMode = useSelector((store) => store.appMode);
return ( return (
<ThemeProvider theme={theme}> <Router history={props.history}>
<div className="App"> <ThemeProvider theme={theme}>
<TopBar /> <div className="App">
<div className="main-container"> <TopBar />
<LogoMain /> <div className="main-container">
{(appMode === "init" || appMode === "search results") && ( <Switch>
<SearchPanel /> <Route exact path="/">
)} <LogoMain />
{appMode === "init" && ( <SearchPanel />
<p className="darkParagraph"> <p className="darkParagraph">
Sprawdź co serwuje Twoja ulubiona restauracja. Sprawdź co serwuje Twoja ulubiona restauracja.
</p> </p>
)} </Route>
{appMode === "search results" && <SearchResults />} <Route path="/results">
{appMode === "restaurant" && <Restaurant />} <LogoMain />
<SearchPanel />
<SearchResults />
</Route>
<Route path="/restaurant">
<Restaurant />
</Route>
<Route path="/dish">
<LogoMain />
</Route>
<Route path="/login">
<LoginDialog />
</Route>
<Route path="/register">
<RegisterDialog />
</Route>
<Route path="/newRestaurant">
<NewRestaurant />
</Route>
<Route path="/forgotpassword">
<ForgotPassword />
</Route>
<Route path="/resetpassword">
<ResetPassword />
</Route>
</Switch>
</div>
<Dialogs />
<Footer />
</div> </div>
<Dialogs /> </ThemeProvider>
<Footer /> </Router>
</div>
</ThemeProvider>
); );
} }

View File

@@ -1,9 +0,0 @@
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

View File

@@ -1,5 +1,7 @@
import axios from "axios"; import axios from "axios";
import * as toggles from "./toggles"; import * as toggles from "./toggles";
import { push } from "connected-react-router";
const backend = "http://localhost:4000/"; const backend = "http://localhost:4000/";
const autocomplete = (input) => { const autocomplete = (input) => {
@@ -23,7 +25,6 @@ export const fetchAutocomplete = (input) => {
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);
const options = cities.concat(restaurants); const options = cities.concat(restaurants);
dispatch(autocomplete(options)); dispatch(autocomplete(options));
}) })
.catch((err) => { .catch((err) => {
@@ -40,7 +41,7 @@ export const fetchSearch = (input) => {
const data = response.data; const data = response.data;
if (Object.keys(data).length > 0) { if (Object.keys(data).length > 0) {
dispatch(setSearchResults(data)); dispatch(setSearchResults(data));
dispatch(setAppMode("APP_SEARCH_RESULTS")); dispatch(push("/results"));
} }
}) })
.catch((err) => { .catch((err) => {
@@ -63,12 +64,6 @@ export const setSearchQuery = (input) => {
}; };
}; };
export const setAppMode = (mode) => {
return {
type: mode,
};
};
export const setRestaurant = (restaurant) => { export const setRestaurant = (restaurant) => {
return { return {
type: "SET_RESTAURANT", type: "SET_RESTAURANT",
@@ -83,7 +78,7 @@ export const fetchRestaurant = (id) => {
.then((response) => { .then((response) => {
dispatch(setRestaurant(response.data)); dispatch(setRestaurant(response.data));
dispatch(toggles.hideDishes()); dispatch(toggles.hideDishes());
dispatch(setAppMode("APP_RESTAURANT")); dispatch(push("/restaurant"));
dispatch(fetchAllDishes(id)); dispatch(fetchAllDishes(id));
}) })
.catch((err) => console.log(err)); .catch((err) => console.log(err));
@@ -112,11 +107,11 @@ export const tryLogin = (username, password) => {
const data = { email: username, password: password }; const data = { email: username, password: password };
return function (dispatch) { return function (dispatch) {
dispatch(toggles.setLoginResult(""));
axios axios
.post(backend + "user/login", data) .post(backend + "user/login", data)
.then((response) => { .then((response) => {
const jwt = response.headers["x-auth-token"]; const jwt = response.headers["x-auth-token"];
console.log(response.headers);
dispatch( dispatch(
toggles.setLoggedIn( toggles.setLoggedIn(
response.data.firstname, response.data.firstname,
@@ -125,7 +120,7 @@ export const tryLogin = (username, password) => {
response.data.email response.data.email
) )
); );
dispatch(toggles.hideLoginDialog()); dispatch(push("/"));
}) })
.catch((err) => { .catch((err) => {
if (err.response.status === 404) { if (err.response.status === 404) {
@@ -143,6 +138,46 @@ export const tryLogin = (username, password) => {
}; };
}; };
export const remindPassword = (email) => {
return function (dispatch) {
const data = { email: email };
dispatch(toggles.setReminderResult(""));
dispatch(toggles.showReminderCircle());
axios
.post(backend + "user/forgotpassword", data)
.then((response) => {
dispatch(toggles.hideReminderCircle());
dispatch(toggles.setReminderResult(response.data));
})
.catch((e) => {
dispatch(toggles.hideReminderCircle());
dispatch(toggles.setReminderResult(e.response.data));
});
};
};
export const changePassword = (email, password, token) => {
return function (dispatch) {
const data = {
token: token,
email: email,
newPass: password,
};
dispatch(toggles.setResetResult(""));
dispatch(toggles.showResetCircle());
axios
.post(backend + "user/resetpass", data)
.then((response) => {
dispatch(toggles.hideResetCircle());
dispatch(toggles.setResetResult(response.data));
})
.catch((e) => {
dispatch(toggles.hideResetCircle());
dispatch(toggles.setResetResult(e.response.data));
});
};
};
export const logout = () => { export const logout = () => {
return function (dispatch) { return function (dispatch) {
dispatch(toggles.setLoggedOut()); dispatch(toggles.setLoggedOut());
@@ -151,6 +186,7 @@ export const logout = () => {
export const tryRegister = (data) => { export const tryRegister = (data) => {
return function (dispatch) { return function (dispatch) {
dispatch(toggles.setRegisterResult(""));
dispatch(toggles.showRegisterCircle()); dispatch(toggles.showRegisterCircle());
axios axios
.post(backend + "user/register", data) .post(backend + "user/register", data)

View File

@@ -10,18 +10,6 @@ export const hideDishes = () => {
}; };
}; };
export const showLoginDialog = () => {
return {
type: "DIALOG_LOGIN_VISIBLE",
};
};
export const hideLoginDialog = () => {
return {
type: "DIALOG_LOGIN_HIDDEN",
};
};
export const setLoggedIn = (username, jwt, id, email) => { export const setLoggedIn = (username, jwt, id, email) => {
return { return {
type: "SET_LOGGEDIN", type: "SET_LOGGEDIN",
@@ -35,30 +23,6 @@ export const setLoggedOut = () => {
}; };
}; };
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 = () => { export const showRegisterCircle = () => {
return { return {
type: "DIALOG_REGISTER_CIRCLE_SHOW", type: "DIALOG_REGISTER_CIRCLE_SHOW",
@@ -91,14 +55,52 @@ export const setLoginResult = (text) => {
}; };
}; };
export const hideNewRestaurantDialog = () => { export const setReminderResult = (text) => {
return { return {
type: "DIALOG_NEWRESTAURANT_HIDE", type: "DIALOG_REMINDER_SET_RESULT",
payload: text,
}; };
}; };
export const showNewRestaurantDialog = () => { export const setResetResult = (text) => {
return { return {
type: "DIALOG_NEWRESTAURANT_SHOW", type: "DIALOG_RESET_SET_RESULT",
payload: text,
};
};
export const hideRegulamin = () => {
return {
type: "DIALOG_REGULAMIN_HIDE",
};
};
export const showRegulamin = () => {
return {
type: "DIALOG_REGULAMIN_SHOW",
};
};
export const showReminderCircle = () => {
return {
type: "DIALOG_REMINDER_CIRCLE_SHOW",
};
};
export const hideReminderCircle = () => {
return {
type: "DIALOG_REMINDER_CIRCLE_HIDE",
};
};
export const showResetCircle = () => {
return {
type: "DIALOG_RESET_CIRCLE_SHOW",
};
};
export const hideResetCircle = () => {
return {
type: "DIALOG_RESET_CIRCLE_HIDE",
}; };
}; };

View File

@@ -1,16 +1,10 @@
import React from "react"; import React from "react";
import LoginDialog from "./Dialogs/LoginDialog";
import RegisterDialog from "./Dialogs/RegisterDialog";
import RegulaminDialog from "./Dialogs/RegulaminDialog"; import RegulaminDialog from "./Dialogs/RegulaminDialog";
import NewRestaurant from "./Dialogs/NewRestaurant";
export default function (props) { export default function (props) {
return ( return (
<div> <div>
<LoginDialog />
<RegisterDialog />
<RegulaminDialog /> <RegulaminDialog />
<NewRestaurant />
</div> </div>
); );
} }

View File

@@ -0,0 +1,154 @@
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 { useSelector, 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 { useHistory } from "react-router-dom";
import { setReminderResult } from "../../actions/toggles";
export default function ForgotPassword(props) {
const initialData = {
email: "",
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();
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 loginClass = loginStyles();
const validateLogin = () => {
var valid;
var validation = {
email: validator.isEmail(data.email),
};
setData({
...data,
emailError: !validation.email,
});
valid = validation.email;
return valid;
};
const handleRemind = () => {
if (validateLogin()) {
dispatch(remindPassword(data.email));
} else {
dispatch(setReminderResult("Podaj poprawne dane."));
}
};
// CODE
return (
<div>
<Dialog
className={loginClass.root}
onClose={() => history.push("/")}
open={true}
aria-labelledby="login-title"
>
<DialogTitle id="login-title">Odzyskiwanie hasła</DialogTitle>
<IconButton
className={loginClass.closeButton}
onClick={() => history.push("/")}
aria-label="close"
>
<CloseIcon />
</IconButton>
<Divider />
<DialogContent>
<p>
Podaj adres email do swojego konta. Link do zmiany hasła powinien
dotrzeć do Ciebie w ciągu maksymalnie 15 minut (sprawdź również
folder SPAM). Jeśli nie pamiętasz również adresu email, skontaktuj
się z obsługą klienta.
</p>
<TextField
className={loginClass.textInput}
required
id="email"
label="Email"
type="email"
variant="outlined"
error={data.emailError}
onChange={(event) => (data.email = event.target.value)}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<AccountCircle color="primary" />
</InputAdornment>
),
}}
/>
<p>
{reminderResult}
<span>{reminderCircle && <CircularProgress />}</span>
</p>
<div className="login-dialog-buttons">
<ButtonSecondary
onClick={() => handleRemind()}
text="Wyślij email"
/>
</div>
<p>
Nie masz konta?{" "}
<span>
<Link href="#" onClick={() => history.push("/register")}>
Zarejestruj się.
</Link>
</span>
</p>
</DialogContent>
</Dialog>
</div>
);
}

View File

@@ -9,17 +9,14 @@ import IconButton from "@material-ui/core/IconButton";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import CloseIcon from "@material-ui/icons/Close"; import CloseIcon from "@material-ui/icons/Close";
import { useSelector, useDispatch } from "react-redux"; import { useSelector, useDispatch } from "react-redux";
import { import { setLoginResult } from "../../actions/toggles";
hideLoginDialog,
showRegisterDialog,
setLoginResult,
} from "../../actions/toggles";
import Link from "@material-ui/core/Link"; import Link from "@material-ui/core/Link";
import LockIcon from "@material-ui/icons/Lock"; import LockIcon from "@material-ui/icons/Lock";
import validator from "validator"; import validator from "validator";
import InputAdornment from "@material-ui/core/InputAdornment"; import InputAdornment from "@material-ui/core/InputAdornment";
import AccountCircle from "@material-ui/icons/AccountCircle"; import AccountCircle from "@material-ui/icons/AccountCircle";
import { tryLogin } from "../../actions"; import { tryLogin } from "../../actions";
import { useHistory } from "react-router-dom";
export default function LoginDialog(props) { export default function LoginDialog(props) {
const initialData = { const initialData = {
@@ -29,9 +26,9 @@ export default function LoginDialog(props) {
passwordError: false, passwordError: false,
}; };
const [loginData, setLoginData] = useState(initialData); const [loginData, setLoginData] = useState(initialData);
const loginDialog = useSelector((state) => state.data.dialogs.logIn);
const loginResult = useSelector((state) => state.data.dialogs.loginResult); const loginResult = useSelector((state) => state.data.dialogs.loginResult);
const dispatch = useDispatch(); const dispatch = useDispatch();
const history = useHistory();
const loginStyles = makeStyles((theme) => ({ const loginStyles = makeStyles((theme) => ({
root: { root: {
@@ -63,11 +60,6 @@ export default function LoginDialog(props) {
}, },
})); }));
const handleRegisterClick = () => {
dispatch(hideLoginDialog());
dispatch(showRegisterDialog());
};
const loginClass = loginStyles(); const loginClass = loginStyles();
const validateLogin = () => { const validateLogin = () => {
@@ -100,14 +92,14 @@ export default function LoginDialog(props) {
<div> <div>
<Dialog <Dialog
className={loginClass.root} className={loginClass.root}
onClose={() => dispatch(hideLoginDialog())} onClose={() => history.push("/")}
open={loginDialog} open={true}
aria-labelledby="login-title" aria-labelledby="login-title"
> >
<DialogTitle id="login-title">Logowanie</DialogTitle> <DialogTitle id="login-title">Logowanie</DialogTitle>
<IconButton <IconButton
className={loginClass.closeButton} className={loginClass.closeButton}
onClick={() => dispatch(hideLoginDialog())} onClick={() => history.push("/")}
aria-label="close" aria-label="close"
> >
<CloseIcon /> <CloseIcon />
@@ -155,14 +147,14 @@ export default function LoginDialog(props) {
<Link <Link
className={loginClass.link} className={loginClass.link}
href="#" href="#"
onClick={() => handleRegisterClick()} onClick={() => history.push("/forgotpassword")}
> >
Nie pamiętam hasła. Nie pamiętam hasła.
</Link> </Link>
<p> <p>
Nie masz konta?{" "} Nie masz konta?{" "}
<span> <span>
<Link href="#" onClick={() => handleRegisterClick()}> <Link href="#" onClick={() => history.push("/register")}>
Zarejestruj się. Zarejestruj się.
</Link> </Link>
</span> </span>

View File

@@ -8,8 +8,6 @@ import ButtonSecondary from "../Input/ButtonSecondary";
import IconButton from "@material-ui/core/IconButton"; import IconButton from "@material-ui/core/IconButton";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import CloseIcon from "@material-ui/icons/Close"; import CloseIcon from "@material-ui/icons/Close";
import { useSelector, useDispatch } from "react-redux";
import { hideNewRestaurantDialog } from "../../actions/toggles";
import Stepper from "@material-ui/core/Stepper"; import Stepper from "@material-ui/core/Stepper";
import Step from "@material-ui/core/Step"; import Step from "@material-ui/core/Step";
import StepLabel from "@material-ui/core/StepLabel"; import StepLabel from "@material-ui/core/StepLabel";
@@ -18,6 +16,7 @@ import Autocomplete from "@material-ui/lab/Autocomplete";
import InputAdornment from "@material-ui/core/InputAdornment"; import InputAdornment from "@material-ui/core/InputAdornment";
import ImageUpload from "../Input/ImageUpload"; import ImageUpload from "../Input/ImageUpload";
import validator from "validator"; import validator from "validator";
import { useHistory } from "react-router-dom";
// ICONS // ICONS
import FastfoodIcon from "@material-ui/icons/Fastfood"; import FastfoodIcon from "@material-ui/icons/Fastfood";
import LocationCityIcon from "@material-ui/icons/LocationCity"; import LocationCityIcon from "@material-ui/icons/LocationCity";
@@ -62,6 +61,9 @@ const useStyles = makeStyles((theme) => ({
"& .MuiInputLabel-root": { "& .MuiInputLabel-root": {
color: "#bbbbbb", color: "#bbbbbb",
}, },
"$ .MuiFormHelperText-root": {
color: "#bbbbbb",
},
}, },
timePicker: { timePicker: {
margin: theme.spacing(2), margin: theme.spacing(2),
@@ -98,13 +100,13 @@ export default function NewRestaurant() {
nameError: false, nameError: false,
cityError: false, cityError: false,
adressError: false, adressError: false,
descriptionError: false,
charLeft: 400,
}; };
const steps = ["Informacje", "Zdjęcie", "Lokalizacja"]; const steps = ["Informacje", "Zdjęcie", "Lokalizacja"];
const [state, setState] = useState(initialState); const [state, setState] = useState(initialState);
const [activeStep, setActiveStep] = React.useState(0); const [activeStep, setActiveStep] = React.useState(0);
const styles = useStyles(); const styles = useStyles();
const dialogOpen = useSelector((state) => state.data.dialogs.newRestaurant);
const dispatch = useDispatch();
const availableTags = [ const availableTags = [
"Płatność kartą", "Płatność kartą",
"Lubimy zwierzaki", "Lubimy zwierzaki",
@@ -114,6 +116,7 @@ export default function NewRestaurant() {
"Podajemy alkohol", "Podajemy alkohol",
"Dowozimy", "Dowozimy",
]; ];
const history = useHistory();
// HANDLERS // HANDLERS
@@ -125,9 +128,14 @@ export default function NewRestaurant() {
} }
}; };
const handleDescriptionChange = (event) => {
let stringLength = event.target.value.length;
let charleft = 400 - stringLength;
setState({ ...state, description: event.target.value, charLeft: charleft });
};
const handlePreviousButton = () => { const handlePreviousButton = () => {
setActiveStep(activeStep - 1); setActiveStep(activeStep - 1);
console.log(activeStep);
}; };
const validateForm = () => { const validateForm = () => {
@@ -135,16 +143,24 @@ export default function NewRestaurant() {
nameValid: validator.isLength(state.name, { min: 1, max: 40 }), nameValid: validator.isLength(state.name, { min: 1, max: 40 }),
cityValid: validator.isLength(state.city, { min: 1, max: 40 }), cityValid: validator.isLength(state.city, { min: 1, max: 40 }),
adressValid: validator.isLength(state.name, { min: 1, max: 40 }), adressValid: validator.isLength(state.name, { min: 1, max: 40 }),
descriptionValid: validator.isLength(state.description, {
min: 1,
max: 400,
}),
}; };
setState({ setState({
...state, ...state,
nameError: !validation.nameValid, nameError: !validation.nameValid,
cityError: !validation.cityValid, cityError: !validation.cityValid,
adressError: !validation.adressValid, adressError: !validation.adressValid,
descriptionError: !validation.descriptionValid,
}); });
return ( return (
validation.nameValid && validation.cityValid && validation.adressValid validation.nameValid &&
validation.cityValid &&
validation.adressValid &&
validation.descriptionValid
); );
}; };
@@ -154,13 +170,13 @@ export default function NewRestaurant() {
<div> <div>
<Dialog <Dialog
className={styles.root} className={styles.root}
open={dialogOpen} open={true}
aria-labelledby="newRestaurant-title" aria-labelledby="newRestaurant-title"
> >
<DialogTitle id="newRestaurant-title">Dodaj Lokal</DialogTitle> <DialogTitle id="newRestaurant-title">Dodaj Lokal</DialogTitle>
<IconButton <IconButton
className={styles.closeButton} className={styles.closeButton}
onClick={() => dispatch(hideNewRestaurantDialog())} onClick={() => history.goBack()}
aria-label="close" aria-label="close"
> >
<CloseIcon /> <CloseIcon />
@@ -273,12 +289,17 @@ export default function NewRestaurant() {
fullWidth fullWidth
label="Opis" label="Opis"
value={state.description} value={state.description}
onChange={(event) => onChange={(event) => handleDescriptionChange(event)}
setState({ ...state, description: event.target.value })
}
multiline multiline
error={state.descriptionError}
rows={3} rows={3}
rowsMax={8}
variant="outlined" variant="outlined"
helperText={"Pozostałe znaki: " + state.charLeft}
FormHelperTextProps={{
style: { color: "#bbbbbb" },
}}
required
/> />
<Autocomplete <Autocomplete
multiple multiple

View File

@@ -9,20 +9,17 @@ import IconButton from "@material-ui/core/IconButton";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import CloseIcon from "@material-ui/icons/Close"; import CloseIcon from "@material-ui/icons/Close";
import { useSelector, useDispatch } from "react-redux"; import { useSelector, useDispatch } from "react-redux";
import { import { setRegisterResult, showRegulamin } from "../../actions/toggles";
hideRegisterDialog,
showRegulaminDialog,
setRegisterResult,
showLoginDialog,
} from "../../actions/toggles";
import { tryRegister } from "../../actions"; import { tryRegister } from "../../actions";
import InputAdornment from "@material-ui/core/InputAdornment"; import InputAdornment from "@material-ui/core/InputAdornment";
import AccountCircle from "@material-ui/icons/AccountCircle"; import AccountCircle from "@material-ui/icons/AccountCircle";
import BusinessIcon from "@material-ui/icons/Business";
import EmailIcon from "@material-ui/icons/Email"; import EmailIcon from "@material-ui/icons/Email";
import LockIcon from "@material-ui/icons/Lock"; import LockIcon from "@material-ui/icons/Lock";
import Link from "@material-ui/core/Link"; import Link from "@material-ui/core/Link";
import validator from "validator"; import validator from "validator";
import CircularProgress from "@material-ui/core/CircularProgress"; import CircularProgress from "@material-ui/core/CircularProgress";
import { useHistory } from "react-router-dom";
export default function RegisterDialog(props) { export default function RegisterDialog(props) {
// SETUP // SETUP
@@ -30,17 +27,22 @@ export default function RegisterDialog(props) {
const initialFormData = { const initialFormData = {
firstname: "", firstname: "",
lastname: "", lastname: "",
companyName: "",
adress: "",
NIP: "",
email: "", email: "",
password: "", password: "",
repeatPassword: "", repeatPassword: "",
firstnameError: false, firstnameError: false,
lastnameError: false, lastnameError: false,
companyNameError: false,
adressError: false,
NIPError: false,
emailError: false, emailError: false,
passwordError: false, passwordError: false,
repeatPasswordError: false, repeatPasswordError: false,
}; };
const [formData, setFormData] = useState(initialFormData); const [formData, setFormData] = useState(initialFormData);
var registerDialog = useSelector((state) => state.data.dialogs.register);
var circularProgress = useSelector( var circularProgress = useSelector(
(state) => state.data.dialogs.registerCircularProgress (state) => state.data.dialogs.registerCircularProgress
); );
@@ -49,6 +51,7 @@ export default function RegisterDialog(props) {
(state) => state.data.dialogs.registerResult (state) => state.data.dialogs.registerResult
); );
const dispatch = useDispatch(); const dispatch = useDispatch();
const history = useHistory();
// STYLES // STYLES
@@ -88,7 +91,7 @@ export default function RegisterDialog(props) {
const handleRegulaminClick = (event) => { const handleRegulaminClick = (event) => {
event.preventDefault(); event.preventDefault();
dispatch(showRegulaminDialog()); dispatch(showRegulamin());
}; };
const validateForm = () => { const validateForm = () => {
@@ -96,6 +99,9 @@ export default function RegisterDialog(props) {
const validations = { const validations = {
firstname: !validator.isEmpty(formData.firstname), firstname: !validator.isEmpty(formData.firstname),
lastname: !validator.isEmpty(formData.lastname), lastname: !validator.isEmpty(formData.lastname),
companyName: !validator.isEmpty(formData.companyName),
adress: !validator.isEmpty(formData.adress),
NIP: !validator.isEmpty(formData.NIP),
email: validator.isEmail(formData.email), email: validator.isEmail(formData.email),
password: validator.isLength(formData.password, { password: validator.isLength(formData.password, {
min: 8, min: 8,
@@ -108,6 +114,9 @@ export default function RegisterDialog(props) {
...formData, ...formData,
firstnameError: !validations.firstname, firstnameError: !validations.firstname,
lastnameError: !validations.lastname, lastnameError: !validations.lastname,
companyNameError: !validations.companyName,
adressError: !validations.adress,
NIPError: !validations.NIP,
emailError: !validations.email, emailError: !validations.email,
passwordError: !validations.password, passwordError: !validations.password,
repeatPasswordError: !validations.repeatPassword, repeatPasswordError: !validations.repeatPassword,
@@ -117,7 +126,10 @@ export default function RegisterDialog(props) {
validations.lastname && validations.lastname &&
validations.email && validations.email &&
validations.password && validations.password &&
validations.repeatPassword; validations.repeatPassword &&
validations.companyName &&
validations.adress &&
validations.NIP;
return valid; return valid;
}; };
@@ -126,29 +138,24 @@ export default function RegisterDialog(props) {
if (validateForm()) { if (validateForm()) {
dispatch(tryRegister(form)); dispatch(tryRegister(form));
} else { } else {
dispatch(setRegisterResult("Proszę poprawić poprawić formularz.")); dispatch(setRegisterResult("Proszę poprawić formularz."));
} }
}; };
const openLogin = () => {
dispatch(hideRegisterDialog());
dispatch(showLoginDialog());
};
// CODE // CODE
return ( return (
<div> <div>
<Dialog <Dialog
className={loginClass.root} className={loginClass.root}
onClose={() => dispatch(hideRegisterDialog())} onClose={() => history.goBack()}
open={registerDialog} open={true}
aria-labelledby="login-title" aria-labelledby="login-title"
> >
<DialogTitle id="login-title">Rejestracja</DialogTitle> <DialogTitle id="login-title">Rejestracja</DialogTitle>
<IconButton <IconButton
className={loginClass.closeButton} className={loginClass.closeButton}
onClick={() => dispatch(hideRegisterDialog())} onClick={() => history.goBack()}
aria-label="close" aria-label="close"
> >
<CloseIcon /> <CloseIcon />
@@ -191,6 +198,59 @@ export default function RegisterDialog(props) {
error={formData.lastnameError} error={formData.lastnameError}
onChange={(event) => (formData.lastname = event.target.value)} 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 <TextField
className={loginClass.textInput} className={loginClass.textInput}
required required
@@ -270,7 +330,10 @@ export default function RegisterDialog(props) {
text="Zarejestruj" text="Zarejestruj"
/> />
) : ( ) : (
<ButtonSecondary onClick={() => openLogin()} text="Logowanie" /> <ButtonSecondary
onClick={() => history.push("/login")}
text="Logowanie"
/>
)} )}
</div> </div>
</div> </div>

View File

@@ -7,13 +7,13 @@ import Divider from "@material-ui/core/Divider";
import IconButton from "@material-ui/core/IconButton"; import IconButton from "@material-ui/core/IconButton";
import CloseIcon from "@material-ui/icons/Close"; import CloseIcon from "@material-ui/icons/Close";
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper";
import { useSelector, useDispatch } from "react-redux";
import { hideRegulaminDialog } from "../../actions/toggles";
import Regulamin from "./Regulamin"; import Regulamin from "./Regulamin";
import { useSelector, useDispatch } from "react-redux";
import { hideRegulamin } from "../../actions/toggles";
export default function RegulaminDialog(props) { export default function RegulaminDialog(props) {
var regulaminDialog = useSelector((state) => state.data.dialogs.regulamin);
const dispatch = useDispatch(); const dispatch = useDispatch();
const open = useSelector((state) => state.data.dialogs.regulamin);
const loginStyles = makeStyles((theme) => ({ const loginStyles = makeStyles((theme) => ({
root: { root: {
@@ -47,14 +47,14 @@ export default function RegulaminDialog(props) {
<div> <div>
<Dialog <Dialog
className={styles.root} className={styles.root}
open={regulaminDialog} open={open}
onClose={() => dispatch(hideRegulaminDialog())} onClose={() => dispatch(hideRegulamin())}
aria-labelledby="regulamin-title" aria-labelledby="regulamin-title"
> >
<DialogTitle id="regulamin-title">Regulamin</DialogTitle> <DialogTitle id="regulamin-title">Regulamin</DialogTitle>
<IconButton <IconButton
className={styles.closeButton} className={styles.closeButton}
onClick={() => dispatch(hideRegulaminDialog())} onClick={() => dispatch(hideRegulamin())}
aria-label="close" aria-label="close"
> >
<CloseIcon /> <CloseIcon />

View File

@@ -0,0 +1,184 @@
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 { useSelector, 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";
function useQuery() {
return new URLSearchParams(useLocation().search);
}
export default function ResetPassword(props) {
const initialData = {
email: "",
emailError: false,
password: "",
passwordError: false,
passwordRepeat: "",
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();
const token = query.get("token");
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 loginClass = loginStyles();
const validateLogin = () => {
var valid;
var validation = {
email: validator.isEmail(data.email),
password: validator.isLength(data.password, { min: 6 }),
passwordRepeat: data.passwordRepeat === data.password,
};
setData({
...data,
emailError: !validation.email,
passwordError: !validation.password,
passwordRepeatError: !validation.passwordRepeat,
});
valid =
validation.password && validation.passwordRepeat && validation.email;
return valid;
};
const handleReset = () => {
if (validateLogin()) {
dispatch(changePassword(data.email, data.password, token));
} else {
dispatch(setResetResult("Popraw dane."));
}
};
// CODE
return (
<div>
<Dialog
className={loginClass.root}
onClose={() => history.push("/")}
open={true}
aria-labelledby="login-title"
>
<DialogTitle id="login-title">Ustaw nowe hasło</DialogTitle>
<IconButton
className={loginClass.closeButton}
onClick={() => history.push("/")}
aria-label="close"
>
<CloseIcon />
</IconButton>
<Divider />
<DialogContent>
<p>Podaj nowe bezpieczne hasło do konta.</p>
<TextField
className={loginClass.textInput}
required
id="email"
label="Email"
type="email"
variant="outlined"
error={data.emailError}
onChange={(event) => (data.email = event.target.value)}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<AccountCircle color="primary" />
</InputAdornment>
),
}}
/>
<TextField
className={loginClass.textInput}
required
id="password"
label="Nowe hasło"
type="password"
variant="outlined"
error={data.passwordError}
onChange={(event) => (data.password = event.target.value)}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<AccountCircle color="primary" />
</InputAdornment>
),
}}
/>
<TextField
className={loginClass.textInput}
required
id="passwordRepeat"
label="Powtórz nowe hasło"
type="password"
variant="outlined"
error={data.passwordRepeatError}
onChange={(event) => (data.passwordRepeat = event.target.value)}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<AccountCircle color="primary" />
</InputAdornment>
),
}}
/>
<div>
{resetCircle && <CircularProgress />}
<p>{resetResult}</p>
</div>
<div className="login-dialog-buttons">
<ButtonSecondary onClick={() => handleReset()} text="Zmień hasło" />
</div>
</DialogContent>
</Dialog>
</div>
);
}

View File

@@ -1,8 +1,10 @@
import React, { useState } from "react"; import React, { useState } from "react";
import CircularProgress from "@material-ui/core/CircularProgress";
import axios from "axios"; import axios from "axios";
export default function ImageUpload() { export default function ImageUpload() {
const [imagePreviewURL, setPreviewURL] = useState(""); const [imagePreviewURL, setPreviewURL] = useState("");
let showCircle = false;
const handleInputChange = (event) => { const handleInputChange = (event) => {
let data = new FormData(); let data = new FormData();
@@ -31,7 +33,11 @@ export default function ImageUpload() {
reader.readAsDataURL(event.target.files[0]); reader.readAsDataURL(event.target.files[0]);
}; };
let imagePreview = <div className="image-preview">Proszę wybrać obraz.</div>; let imagePreview = (
<div className="image-preview">
{showCircle ? <CircularProgress /> : "Proszę wybrać obraz."}
</div>
);
if (imagePreviewURL) { if (imagePreviewURL) {
imagePreview = ( imagePreview = (
<div <div

View File

@@ -5,6 +5,7 @@ import { useDispatch } from "react-redux";
function extractTags(tags) { function extractTags(tags) {
var results = []; var results = [];
if (!tags) return results;
for (let [key, value] of Object.entries(tags)) { for (let [key, value] of Object.entries(tags)) {
if (value === true) { if (value === true) {
results.push(key); results.push(key);

View File

@@ -5,7 +5,6 @@ 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";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { setAppMode } from "../actions";
import Drawer from "@material-ui/core/Drawer"; import Drawer from "@material-ui/core/Drawer";
import List from "@material-ui/core/List"; import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem"; import ListItem from "@material-ui/core/ListItem";
@@ -15,11 +14,11 @@ 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 { showLoginDialog, showRegisterDialog } 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 "./Input/ButtonSecondary"; import ButtonSecondary from "./Input/ButtonSecondary";
import ListSubheader from "@material-ui/core/ListSubheader"; import ListSubheader from "@material-ui/core/ListSubheader";
import { useHistory } from "react-router-dom";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
paper: { paper: {
@@ -40,30 +39,28 @@ export default function TopBar() {
const classes = useStyles(); const classes = useStyles();
const loggedIn = useSelector((state) => state.data.loggedIn); const loggedIn = useSelector((state) => state.data.loggedIn);
const username = useSelector((state) => state.data.username); const username = useSelector((state) => state.data.username);
const history = useHistory();
const dispatch = useDispatch(); const dispatch = useDispatch();
const imgClick = () => {
dispatch(setAppMode("APP_INIT"));
};
const handleClick = (button) => { const handleClick = (button) => {
setState((state.menuOpen = false)); setState((state.menuOpen = false));
switch (button) { switch (button) {
case "menui": case "menui":
dispatch(setAppMode("APP_INIT")); history.push("/");
break; break;
case "logIn": case "logIn":
dispatch(showLoginDialog()); history.push("/login");
break; break;
case "register": case "register":
dispatch(showRegisterDialog()); history.push("/register");
break; break;
case "logOut": case "logOut":
dispatch(logout()); dispatch(logout());
break; break;
case "myRestaurant": case "myRestaurant":
dispatch(setAppMode("APP_RESTAURANT")); history.push("/restaurant");
break; break;
case "addDish": case "addDish":
dispatch(setAppMode("APP_ADD_DISH")); history.push("/");
break; break;
default: default:
return true; return true;
@@ -73,6 +70,9 @@ export default function TopBar() {
menuOpen: false, menuOpen: false,
}); });
const closeDrawer = () => {
setState({ menuOpen: false });
};
const toggleDrawer = (open) => (event) => { const toggleDrawer = (open) => (event) => {
if ( if (
event.type === "keydown" && event.type === "keydown" &&
@@ -91,7 +91,7 @@ export default function TopBar() {
src={logo} src={logo}
className="topBarLogo" className="topBarLogo"
alt="Menui logo" alt="Menui logo"
onClick={() => imgClick()} onClick={() => history.push("/")}
/> />
<div className="vertical-divider"></div> <div className="vertical-divider"></div>
<h5>Food guide</h5> <h5>Food guide</h5>
@@ -141,7 +141,9 @@ export default function TopBar() {
<ListItemText primary="Cennik" /> <ListItemText primary="Cennik" />
</ListItem> </ListItem>
<Divider /> <Divider />
<ListItem>{loggedIn && <UserMenu />}</ListItem> <ListItem>
{loggedIn && <UserMenu closeMenu={closeDrawer} />}
</ListItem>
</List> </List>
<div className="drawer-bottom"> <div className="drawer-bottom">
<div className="drawer-buttons"> <div className="drawer-buttons">

View File

@@ -11,6 +11,7 @@ import ExpandMore from "@material-ui/icons/ExpandMore";
import FastfoodIcon from "@material-ui/icons/Fastfood"; import FastfoodIcon from "@material-ui/icons/Fastfood";
import AddIcon from "@material-ui/icons/Add"; import AddIcon from "@material-ui/icons/Add";
import SettingsIcon from "@material-ui/icons/Settings"; import SettingsIcon from "@material-ui/icons/Settings";
import { useHistory } from "react-router-dom";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
root: { root: {
@@ -33,6 +34,11 @@ export default function UserMenu(props) {
const handleClick = () => { const handleClick = () => {
setOpen(!open); setOpen(!open);
}; };
const handleAddRestaurant = () => {
props.closeMenu();
history.push("/newRestaurant");
};
const history = useHistory();
return ( return (
<List <List
@@ -58,7 +64,7 @@ export default function UserMenu(props) {
</ListItem> </ListItem>
<Collapse in={open} timeout="auto" unmountOnExit> <Collapse in={open} timeout="auto" unmountOnExit>
<List component="div" disablePadding> <List component="div" disablePadding>
<ListItem> <ListItem button onClick={handleAddRestaurant}>
<ListItemIcon> <ListItemIcon>
<AddIcon /> <AddIcon />
</ListItemIcon> </ListItemIcon>

View File

@@ -5,23 +5,30 @@ import { Provider } from "react-redux";
import "./index.scss"; import "./index.scss";
import App from "./App"; import App from "./App";
import * as serviceWorker from "./serviceWorker"; import * as serviceWorker from "./serviceWorker";
import rootReducer from "./reducers";
import thunk from "redux-thunk"; import thunk from "redux-thunk";
import { createBrowserHistory } from "history";
import { createStore, applyMiddleware, compose } from "redux"; import { createStore, applyMiddleware, compose } from "redux";
import { routerMiddleware, ConnectedRouter } from "connected-react-router";
import rootReducer from "./reducers";
const history = createBrowserHistory();
ReactGA.initialize("G-SHB9LXPWWM"); ReactGA.initialize("G-SHB9LXPWWM");
ReactGA.pageview("/"); ReactGA.pageview("/");
const store = createStore( const store = createStore(
rootReducer, rootReducer(history),
compose( compose(
applyMiddleware(thunk), applyMiddleware(routerMiddleware(history), thunk),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
) )
); );
ReactDOM.render( ReactDOM.render(
<Provider store={store}> <Provider store={store}>
<App /> <ConnectedRouter history={history}>
<App history={history} />
</ConnectedRouter>
</Provider>, </Provider>,
document.getElementById("root") document.getElementById("root")
); );

View File

@@ -6,16 +6,15 @@ const initialState = {
userId: "", userId: "",
userEmail: "", userEmail: "",
dialogs: { dialogs: {
logIn: false,
register: false,
newRestaurant: true,
contact: false,
pricing: false,
regulamin: false,
registerCircularProgress: false, registerCircularProgress: false,
registerForm: true, registerForm: true,
registerResult: "", registerResult: "",
loginResult: "", loginResult: "",
regulamin: false,
reminderResult: "",
reminderCircularProgress: false,
resetResult: "",
resetCircularProgress: false,
}, },
tempData: {}, tempData: {},
}; };
@@ -26,8 +25,6 @@ const data = (state = initialState, action) => {
return (state = { ...state, showDishList: true }); return (state = { ...state, showDishList: true });
case "SET_DISHLIST_HIDDEN": case "SET_DISHLIST_HIDDEN":
return (state = { ...state, showDishList: false }); return (state = { ...state, showDishList: false });
case "DIALOG_LOGIN_VISIBLE":
return (state = { ...state, dialogs: { ...state.dialogs, logIn: true } });
case "SET_LOGGEDIN": case "SET_LOGGEDIN":
return (state = { return (state = {
...state, ...state,
@@ -46,36 +43,21 @@ const data = (state = initialState, action) => {
userEmail: "", userEmail: "",
userId: "", userId: "",
}); });
case "DIALOG_LOGIN_HIDDEN":
return (state = {
...state,
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": case "DIALOG_REGISTER_CIRCLE_SHOW":
return (state = { return (state = {
...state, ...state,
dialogs: { ...state.dialogs, registerCircularProgress: true }, dialogs: { ...state.dialogs, registerCircularProgress: true },
}); });
case "DIALOG_REGULAMIN_SHOW":
return (state = {
...state,
dialogs: { ...state.dialogs, regulamin: true },
});
case "DIALOG_REGULAMIN_HIDE":
return (state = {
...state,
dialogs: { ...state.dialogs, regulamin: false },
});
case "DIALOG_REGISTER_CIRCLE_HIDE": case "DIALOG_REGISTER_CIRCLE_HIDE":
return (state = { return (state = {
...state, ...state,
@@ -91,20 +73,40 @@ const data = (state = initialState, action) => {
...state, ...state,
dialogs: { ...state.dialogs, registerResult: action.payload }, dialogs: { ...state.dialogs, registerResult: action.payload },
}); });
case "DIALOG_REMINDER_SET_RESULT":
return (state = {
...state,
dialogs: { ...state.dialogs, reminderResult: action.payload },
});
case "DIALOG_REMINDER_CIRCLE_SHOW":
return (state = {
...state,
dialogs: { ...state.dialogs, reminderCircularProgress: true },
});
case "DIALOG_REMINDER_CIRCLE_HIDE":
return (state = {
...state,
dialogs: { ...state.dialogs, reminderCircularProgress: false },
});
case "DIALOG_RESET_CIRCLE_SHOW":
return (state = {
...state,
dialogs: { ...state.dialogs, resetCircularProgress: true },
});
case "DIALOG_RESET_CIRCLE_HIDE":
return (state = {
...state,
dialogs: { ...state.dialogs, resetCircularProgress: false },
});
case "DIALOG_LOGIN_SET_RESULT": case "DIALOG_LOGIN_SET_RESULT":
return (state = { return (state = {
...state, ...state,
dialogs: { ...state.dialogs, loginResult: action.payload }, dialogs: { ...state.dialogs, loginResult: action.payload },
}); });
case "DIALOG_NEWRESTAURANT_HIDE": case "DIALOG_RESET_SET_RESULT":
return (state = { return (state = {
...state, ...state,
dialogs: { ...state.dialogs, newRestaurant: false }, dialogs: { ...state.dialogs, resetResult: action.payload },
});
case "DIALOG_NEWRESTAURANT_SHOW":
return (state = {
...state,
dialogs: { ...state.dialogs, newRestaurant: true },
}); });
case "SET_TEMP_DATA": case "SET_TEMP_DATA":
return (state = { ...state, tempData: action.payload }); return (state = { ...state, tempData: action.payload });

View File

@@ -1,4 +1,5 @@
import { combineReducers } from "redux"; import { combineReducers } from "redux";
import { connectRouter } from "connected-react-router";
import autoCompleteReducer from "./autoComplete"; import autoCompleteReducer from "./autoComplete";
import searchResults from "./searchResults"; import searchResults from "./searchResults";
import appMode from "./appMode"; import appMode from "./appMode";
@@ -7,14 +8,16 @@ import restaurant from "./restaurant";
import dishes from "./dishes"; import dishes from "./dishes";
import data from "./data"; import data from "./data";
const rootReducer = combineReducers({ const rootReducer = (history) =>
autocomplete: autoCompleteReducer, combineReducers({
appMode: appMode, router: connectRouter(history),
searchResults: searchResults, autocomplete: autoCompleteReducer,
searchQuery: searchQuery, appMode: appMode,
restaurant: restaurant, searchResults: searchResults,
dishes: dishes, searchQuery: searchQuery,
data: data, restaurant: restaurant,
}); dishes: dishes,
data: data,
});
export default rootReducer; export default rootReducer;