Search query / results cards

This commit is contained in:
2020-07-22 12:45:34 +02:00
parent 2bcbfef8ba
commit d8173a9d7f
14 changed files with 188 additions and 12 deletions

View File

@@ -1,10 +1,12 @@
import React from "react"; import React from "react";
import logo from "./public/logo_mint.svg";
import "./App.scss"; import "./App.scss";
import TopBar from "./components/TopBar"; import TopBar from "./components/TopBar";
import LogoMain from "./components/logoMain";
import Footer from "./components/Footer"; import Footer from "./components/Footer";
import SearchPanel from "./components/SearchPanel"; import SearchPanel from "./components/SearchPanel";
import SearchResults from "./components/SearchResults";
import { createMuiTheme, ThemeProvider } from "@material-ui/core/styles"; import { createMuiTheme, ThemeProvider } from "@material-ui/core/styles";
import { useSelector } from "react-redux";
const theme = createMuiTheme({ const theme = createMuiTheme({
palette: { palette: {
@@ -15,16 +17,21 @@ const theme = createMuiTheme({
}); });
function App() { function App() {
const appMode = useSelector((store) => store.appMode);
return ( return (
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<div className="App"> <div className="App">
<TopBar /> <TopBar />
<div className="main-container"> <div className="main-container">
<img src={logo} className="logo" alt="Menui logo" /> <LogoMain />
<SearchPanel /> <SearchPanel />
<p className="darkParagraph"> {appMode === "init" && (
Sprawdź co serwuje Twoja ulubiona restauracja. <p className="darkParagraph">
</p> Sprawdź co serwuje Twoja ulubiona restauracja.
</p>
)}
<SearchResults />
</div> </div>
<Footer /> <Footer />
</div> </div>

View File

@@ -2,6 +2,7 @@
@import "./styles/TopBar.scss"; @import "./styles/TopBar.scss";
@import "./styles/SearchPanel.scss"; @import "./styles/SearchPanel.scss";
@import "./styles/Footer.scss"; @import "./styles/Footer.scss";
@import "./styles/SearchResults.scss";
.App { .App {
padding: 0; padding: 0;
@@ -38,4 +39,5 @@ p {
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
text-align: center; text-align: center;
align-items: center;
} }

View File

@@ -16,7 +16,9 @@ export const clearAutocomplete = () => {
export const fetchAutocomplete = (input) => { export const fetchAutocomplete = (input) => {
return function (dispatch) { return function (dispatch) {
axios axios
.get("http://localhost:4000/search/autocomplete?string=" + input) .get(
"http://localhost:4000/search/autocomplete?string=" + encodeURI(input)
)
.then((response) => { .then((response) => {
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);
@@ -30,6 +32,37 @@ export const fetchAutocomplete = (input) => {
}; };
}; };
export const fetchSearch = (input) => {
return function (dispatch) {
axios
.get("http://localhost:4000/search?string=" + encodeURI(input))
.then((response) => {
const data = response.data;
if (Object.keys(data).length > 0) {
dispatch(setSearchResults(data));
dispatch(setAppMode("APP_SEARCH_RESULTS"));
}
})
.catch((err) => {
console.log(err);
});
};
};
const setSearchResults = (input) => {
return {
type: "SEARCH_RESULTS",
payload: input,
};
};
export const setSearchQuery = (input) => {
return {
type: "SEARCH_QUERY_SET",
payload: input,
};
};
export const setAppMode = (mode) => { export const setAppMode = (mode) => {
return { return {
type: mode, type: mode,

View File

@@ -19,5 +19,5 @@ const StylizedButton = withStyles({
})(Button); })(Button);
export default function ButtonSecondary(props) { export default function ButtonSecondary(props) {
return <StylizedButton>{props.text}</StylizedButton>; return <StylizedButton onClick={props.onClick}>{props.text}</StylizedButton>;
} }

View File

@@ -0,0 +1,18 @@
import React from "react";
export default function CardRestaurant(props) {
return (
<div className="card-restaurant">
<div className="card-img"></div>
<div className="card-info">
<h1>{props.name}</h1>
<hr />
<h3>Miasto: {props.city}</h3>
<h3>Godziny otwarcia: {props.hours}</h3>
<p>
Jakiś krótki opis restauracji. Coś tam że jest przytulnie i elegancko.
</p>
</div>
</div>
);
}

View File

@@ -3,10 +3,11 @@ import ButtonSecondary from "./ButtonSecondary";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
import Autocomplete from "@material-ui/lab/Autocomplete"; import Autocomplete from "@material-ui/lab/Autocomplete";
import { useSelector, useDispatch } from "react-redux"; import { useSelector, useDispatch } from "react-redux";
import { fetchAutocomplete } from "../actions"; import { fetchAutocomplete, setSearchQuery, fetchSearch } from "../actions";
export default function SearchPanel() { export default function SearchPanel() {
let options = useSelector((store) => store.autocomplete); let options = useSelector((store) => store.autocomplete);
let searchQuery = useSelector((store) => store.searchQuery);
const dispatch = useDispatch(); const dispatch = useDispatch();
return ( return (
@@ -15,18 +16,23 @@ export default function SearchPanel() {
options={options} options={options}
style={{ width: 300 }} style={{ width: 300 }}
noOptionsText="Brak podpowiedzi" noOptionsText="Brak podpowiedzi"
onChange={(event) => dispatch(setSearchQuery(event.target.textContent))}
renderInput={(params) => ( renderInput={(params) => (
<TextField <TextField
{...params} {...params}
label="Miasto, Nazwa lokalu, ..." label="Miasto, Nazwa lokalu, ..."
variant="outlined" variant="outlined"
onChange={(event) => dispatch(setSearchQuery(event.target.value))}
onInput={(event) => dispatch(fetchAutocomplete(event.target.value))} onInput={(event) => dispatch(fetchAutocomplete(event.target.value))}
/> />
)} )}
/> />
<div className="btnContainer"> <div className="btnContainer">
<ButtonSecondary text="Szukaj" /> <ButtonSecondary
onClick={() => dispatch(fetchSearch(searchQuery))}
text="Szukaj"
/>
</div> </div>
</div> </div>
); );

View File

@@ -0,0 +1,28 @@
import React from "react";
import CardRestaurant from "./CardRestaurant";
import { useSelector } from "react-redux";
export default function SearchResults() {
var results = useSelector((store) => store.searchResults);
return (
<div className="search-results">
<CardRestaurant
name="Kuchnie Świata"
city="Mikołajki"
hours="7:00 - 23:00"
/>
<CardRestaurant
name="Grzmiące Patyki"
city="Ciechanów"
hours="7:00 - 23:00"
/>
<CardRestaurant name="Naruto Sushi" city="Tokio" hours="7:00 - 23:00" />
<CardRestaurant
name="Gówno"
city="Dąbrowa górnicza"
hours="7:00 - 23:00"
/>
</div>
);
}

View File

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

View File

@@ -6,9 +6,15 @@ import App from "./App";
import * as serviceWorker from "./serviceWorker"; import * as serviceWorker from "./serviceWorker";
import rootReducer from "./reducers"; import rootReducer from "./reducers";
import thunk from "redux-thunk"; import thunk from "redux-thunk";
import { createStore, applyMiddleware } from "redux"; import { createStore, applyMiddleware, compose } from "redux";
const store = createStore(rootReducer, applyMiddleware(thunk)); const store = createStore(
rootReducer,
compose(
applyMiddleware(thunk),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
ReactDOM.render( ReactDOM.render(
<Provider store={store}> <Provider store={store}>

BIN
src/public/cat.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,4 +1,4 @@
const appModeReducer = (state = "init", action) => { const appModeReducer = (state = "search results", action) => {
switch (action.type) { switch (action.type) {
case "APP_INIT": case "APP_INIT":
return (state = "init"); return (state = "init");

View File

@@ -2,11 +2,13 @@ import { combineReducers } from "redux";
import autoCompleteReducer from "./autoComplete"; import autoCompleteReducer from "./autoComplete";
import searchResults from "./searchResults"; import searchResults from "./searchResults";
import appMode from "./appMode"; import appMode from "./appMode";
import searchQuery from "./searchQuery";
const rootReducer = combineReducers({ const rootReducer = combineReducers({
autocomplete: autoCompleteReducer, autocomplete: autoCompleteReducer,
appMode: appMode, appMode: appMode,
searchResults: searchResults, searchResults: searchResults,
searchQuery: searchQuery,
}); });
export default rootReducer; export default rootReducer;

View File

@@ -0,0 +1,12 @@
const searchQuery = (state = {}, action) => {
switch (action.type) {
case "SEARCH_QUERY_SET":
return (state = action.payload);
case "SEARCH_QUERY_CLEAR":
return (state = "");
default:
return state;
}
};
export default searchQuery;

View File

@@ -0,0 +1,49 @@
@import "./Design.scss";
.search-results {
}
.card-restaurant {
background-color: #f7f7f7;
margin: 14px;
color: $secondary-color;
border-radius: 10px;
min-width: 70vw;
min-height: 200px;
display: flex;
cursor: pointer;
:hover {
background-color: #ebebeb;
}
}
.card-img {
width: 180px;
height: 180px;
margin: 10px;
border-radius: 8px;
background-image: url("../public/cat.jpg");
background-size: cover;
}
.card-info {
text-align: start;
flex-grow: 6;
h1 {
color: $main-color;
font-size: 1.6rem;
font-weight: 300;
margin-bottom: 4px;
}
h3 {
font-weight: 400;
margin-top: 2px;
margin-bottom: 2px;
font-size: 0.8rem;
}
hr {
color: $main-color;
border: solid 1px;
}
}