Files
my-bar/front/src/app/pages/cocktails/CocktailsPageContent.js
2025-03-12 17:54:16 +04:00

333 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;