333 lines
12 KiB
JavaScript
333 lines
12 KiB
JavaScript
import Grid from "@mui/material/Grid";
|
||
import {useAlert} from "../../../hooks/useAlert";
|
||
import * as React from "react";
|
||
import {useCallback, useEffect, useState} from "react";
|
||
import {Cocktail} from "../../../components/cocktails/Cocktail";
|
||
import {Fab, Skeleton} from "@mui/material";
|
||
import Box from "@mui/material/Box";
|
||
import {requests} from "../../../requests";
|
||
import {NoResult} from "../../../components/cocktails/NoResult";
|
||
import {FilterBlock} from "../../../components/cocktails/FilterBlock";
|
||
import {api} from "../../../lib/clients/api";
|
||
import {CocktailInfoModal} from "../../../components/cocktails/CocktailInfoModal";
|
||
import {useUser} from "../../../hooks/useUser";
|
||
import {blue} from "@mui/material/colors";
|
||
import UpIcon from "@mui/icons-material/KeyboardArrowUp";
|
||
import {sortList} from "../../../components/cocktails/sortingList";
|
||
import {getComparator} from "../../../components/core/getComparator";
|
||
import Button from "@mui/material/Button";
|
||
import Paper from "@mui/material/Paper";
|
||
import CheckMarks from "../../../components/cocktails/CheckMarks";
|
||
|
||
const filterList = (rows, filter, allowIngredients) => {
|
||
let regExp = new RegExp("(.*?)" + filter.search + "(.*?)", "i");
|
||
const sortingObj = sortList.find((s) => s.name === filter.sorting);
|
||
const sortingValues = sortingObj.id.split("|");
|
||
return rows
|
||
.filter((row) => {
|
||
const nameReg = row.name.split(" ").map((n) => n.match(regExp) !== null).includes(true);
|
||
const ingredientReg = row.components
|
||
.split(", ")
|
||
.map((r) => r.match(regExp) !== null)
|
||
.includes(true);
|
||
return nameReg || ingredientReg;
|
||
})
|
||
.filter((row) => filter.onlyFavourite ? row.rating.favourite : true)
|
||
.filter((row) => filter.glass.length === 0 || filter.glass.includes(row.glass))
|
||
.filter((row) => filter.category.length === 0 || filter.category.includes(row.category))
|
||
.filter((row) => filter.alcohol.length === 0 || filter.alcohol.includes(row.alcoholic))
|
||
.filter((row) => {
|
||
if (filter.tags.length === 0) {
|
||
return true;
|
||
}
|
||
|
||
if (row.tags.length === 0) {
|
||
return false;
|
||
}
|
||
return row.tags.split(",").find((tag) => filter.tags.includes(tag))
|
||
})
|
||
.filter((row) => {
|
||
if (filter.iCount.length === 0) {
|
||
return true;
|
||
}
|
||
const arr = row.components.split(", ");
|
||
const count = arr.filter((n) => !allowIngredients.includes(n)).length;
|
||
const filt = filter.ingredient.length === 0 || arr.filter((n) => filter.ingredient.includes(n)).length > 0;
|
||
|
||
return filter.iCount === count && filt;
|
||
})
|
||
.filter((row) => {
|
||
if (filter.inMenu === "") {
|
||
return row;
|
||
}
|
||
const filterValue = filter.inMenu === "Есть в меню";
|
||
return filterValue === row.inMenu;
|
||
})
|
||
.sort(getComparator(sortingValues[1], sortingValues[0], "name"))
|
||
}
|
||
|
||
const emptyFilter = {
|
||
search: "",
|
||
hidden: true,
|
||
onlyFavourite: false,
|
||
glass: [],
|
||
category: [],
|
||
alcohol: [],
|
||
tags: [],
|
||
iCount: [],
|
||
ingredient: [],
|
||
inMenu: "",
|
||
sorting: "Название по возрастанию"
|
||
}
|
||
|
||
const CocktailsPageContent = ({all}) => {
|
||
const {user} = useUser();
|
||
const {createError, createSuccess} = useAlert();
|
||
const [allowIngredients, setAllowIngredients] = useState([])
|
||
const [rows, setRows] = useState([]);
|
||
const [filter, setFilter] = useState(emptyFilter)
|
||
const [open, setOpen] = useState(false);
|
||
const [selectedCocktail, setSelectedCocktail] = useState(null)
|
||
const [chips, setChips] = useState([])
|
||
const [page, setPage] = useState(-1);
|
||
const [load, setLoad] = useState(false);
|
||
const [isEnd, setIsEnd] = useState(false);
|
||
const [isNew, setIsNew] = useState(true);
|
||
|
||
const loading = useCallback(() => {
|
||
const size = Math.floor((window.innerWidth) / 350) * 5;
|
||
if (load || (!isNew && isEnd)) {
|
||
return false;
|
||
}
|
||
setLoad(true);
|
||
const request = {
|
||
...filter,
|
||
all: all,
|
||
sort: sortList.find((s) => s.name === filter.sorting).id,
|
||
page: page + 1,
|
||
size: size,
|
||
iCount: Array.isArray(filter.iCount) ? null : filter.iCount
|
||
}
|
||
api().post(requests.cocktails.menu, request)
|
||
.then((r) => {
|
||
if (r.data.length === 0) {
|
||
if(isNew) {
|
||
setRows([]);
|
||
}
|
||
setIsEnd(true);
|
||
setLoad(false);
|
||
return;
|
||
}
|
||
const cocktails = isNew ? r.data : rows.concat(r.data);
|
||
setRows(cocktails);
|
||
setIsNew(false);
|
||
setPage(page + 1);
|
||
setLoad(false);
|
||
})
|
||
.catch((r) => {
|
||
setLoad(false);
|
||
createError("Ошибка загрузки данных от сервера Status:" + r.status)
|
||
})
|
||
// eslint-disable-next-line
|
||
}, [load, isEnd, page]);
|
||
useEffect(() => {
|
||
const handleScroll = () => {
|
||
const {scrollTop, scrollHeight, clientHeight} = document.documentElement;
|
||
if (scrollTop + clientHeight >= scrollHeight - 100) {
|
||
loading();
|
||
}
|
||
}
|
||
window.addEventListener('scroll', handleScroll);
|
||
return () => window.removeEventListener('scroll', handleScroll);
|
||
}, [loading]);
|
||
useEffect(() => {
|
||
api().get(requests.bar.ingredientSimple)
|
||
.then((r) => {
|
||
const arr = r.data.filter((i) => i.isHave)
|
||
.map((i) => i.name)
|
||
setAllowIngredients(arr)
|
||
})
|
||
.catch(() => createError("Ошибка получения ингредиентов"))
|
||
// eslint-disable-next-line
|
||
}, [])
|
||
useEffect(() => {
|
||
loading();
|
||
}, [filter])
|
||
useEffect(() => {
|
||
if (!all) {
|
||
return;
|
||
}
|
||
const ingredients = new Set();
|
||
rows.map((c) => c.components)
|
||
.map((c) => c.split(", "))
|
||
.map((c) => c.filter((i) => !allowIngredients.includes(i)))
|
||
.filter((nhc) => nhc.length === 1)
|
||
.map((fc) => fc[0])
|
||
.forEach((i) => ingredients.add(i))
|
||
setChips(Array.from(ingredients).sort(getComparator()));
|
||
}, [rows, allowIngredients])
|
||
|
||
const renderSkeleton = () => {
|
||
return Array.from({length: 3}, () => null)
|
||
.map((v, index) => <Skeleton sx={{m: 2}}
|
||
key={index}
|
||
variant="rounded"
|
||
width={350}
|
||
height={690}/>);
|
||
}
|
||
const handleChangeRating = (row, value) => {
|
||
const newState = rows.map((r) => {
|
||
if (row.id === r.id) {
|
||
let newRating = r.rating;
|
||
newRating.rating = value
|
||
return {
|
||
...r,
|
||
rating: newRating
|
||
}
|
||
}
|
||
return r;
|
||
})
|
||
api().post(`${requests.cocktails.rating}${row.id}&rating=${value}`)
|
||
.then(() => {
|
||
setRows(newState);
|
||
createSuccess("Спасибо за оценку!")
|
||
}).catch(() => createError("Ошибка сохранения"))
|
||
|
||
}
|
||
const handleFilterChange = (filterName, value) => {
|
||
const newState = {
|
||
...filter,
|
||
[filterName]: value
|
||
}
|
||
setFilter(newState)
|
||
setIsNew(true);
|
||
setIsEnd(false);
|
||
setPage(-1);
|
||
}
|
||
const handleFavourite = (row) => {
|
||
const value = !row.rating.favourite;
|
||
const newState = rows.map((r) => {
|
||
if (r.id === row.id) {
|
||
let newRating = r.rating;
|
||
newRating.favourite = value;
|
||
return {
|
||
...r,
|
||
rating: newRating
|
||
}
|
||
}
|
||
return r;
|
||
});
|
||
let url = `${requests.cocktails.favourite}${row.id}`;
|
||
let request = value ? api().put(url) : api().delete(url);
|
||
|
||
request
|
||
.then(() => {
|
||
setRows(newState);
|
||
createSuccess("Спасибо за оценку!")
|
||
}).catch(() => createError("Ошибка сохранения"))
|
||
}
|
||
const handleFilterClear = () => {
|
||
setFilter(emptyFilter);
|
||
}
|
||
const handleSelectCocktail = (row) => {
|
||
setSelectedCocktail(row.id)
|
||
setOpen(true)
|
||
}
|
||
const handleCloseCocktailModal = () => {
|
||
setOpen(false);
|
||
setSelectedCocktail(null);
|
||
}
|
||
const handleEditMenu = (row, value) => {
|
||
const newState = rows.map((r) => {
|
||
if (r.id !== row.id) {
|
||
return r;
|
||
}
|
||
if (all) {
|
||
return {
|
||
...r,
|
||
inMenu: value
|
||
}
|
||
}
|
||
return null
|
||
}).filter((r) => r !== null);
|
||
|
||
api().post(`${requests.cocktails.menu}?id=${row.id}&value=${value}`)
|
||
.then(() => setRows(newState))
|
||
.catch(() => createError("Ошибка сохранения данных"))
|
||
}
|
||
const editMenuBlock = (row) => {
|
||
if (user.role === "USER" || user.role === "ADMIN_NOT_BARMEN") {
|
||
return null;
|
||
}
|
||
return (
|
||
<Button color={row.inMenu ? 'error' : 'success'} variant='contained'
|
||
onClick={() => handleEditMenu(row, !row.inMenu)}>
|
||
{(row.inMenu ? 'Удалить из' : 'Добавить в') + ' меню'}
|
||
</Button>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<Box>
|
||
{/*<Loading loading={load}/>*/}
|
||
{/*Модальное окно информации о коктейле*/}
|
||
<CocktailInfoModal row={selectedCocktail} open={open}
|
||
closeHandler={handleCloseCocktailModal}/>
|
||
{/*Блок фильтров*/}
|
||
<FilterBlock
|
||
filter={filter}
|
||
handleFilterChange={handleFilterChange}
|
||
handleClearFilter={handleFilterClear}
|
||
barmen={user.role !== 'USER'}
|
||
all={all}
|
||
/>
|
||
|
||
{/*todo: доделать фильтр по количеству недостающих ингредиентов*/}
|
||
{/*{*/}
|
||
{/* (all && filter.iCount === 1) && (*/}
|
||
{/* <Paper sx={{mt: 1}}>*/}
|
||
{/* <CheckMarks rows={chips} name={"Выбор ингредиента"} filterName={"ingredient"}*/}
|
||
{/* filterValue={filter.ingredient}*/}
|
||
{/* handleChange={handleFilterChange}*/}
|
||
{/* identity*/}
|
||
{/* />*/}
|
||
{/* </Paper>*/}
|
||
{/* )*/}
|
||
{/*}*/}
|
||
<Box>
|
||
{/*Основное содержимое*/}
|
||
<Grid container rowSpacing={2} columnSpacing={{xs: 1, sm: 1, md: 2}} sx={{m: 1}}>
|
||
{rows.length > 0 && rows.map((row) => {
|
||
return (
|
||
<Cocktail key={row.id} row={row} handleFavourite={handleFavourite}
|
||
handleChangeRating={handleChangeRating}
|
||
handleSelect={handleSelectCocktail}
|
||
editMenuBlock={editMenuBlock}
|
||
/>
|
||
)
|
||
})}
|
||
{load && renderSkeleton()}
|
||
{rows.length === 0 && (<NoResult/>)}
|
||
</Grid>
|
||
</Box>
|
||
<Fab sx={{
|
||
alpha: '30%',
|
||
position: 'sticky',
|
||
left: 'calc(100% - 16px)',
|
||
bottom: '16px',
|
||
color: 'common.white',
|
||
bgcolor: blue[600],
|
||
'&:hover': {
|
||
bgcolor: blue[600],
|
||
},
|
||
}}
|
||
onClick={() => window.window.scrollTo(0, 0)}
|
||
aria-label='Expand'
|
||
color='inherit'>
|
||
<UpIcon/>
|
||
</Fab>
|
||
</Box>
|
||
);
|
||
}
|
||
|
||
export default CocktailsPageContent; |