diff --git a/front/src/app/NavigationRoutes.js b/front/src/app/NavigationRoutes.js
index 8ab17ec..495cfc2 100644
--- a/front/src/app/NavigationRoutes.js
+++ b/front/src/app/NavigationRoutes.js
@@ -12,6 +12,7 @@ import {MenuPage} from "./pages/cocktails/MenuPage";
import {EditIngredientPage} from "./pages/ingredients/EditIngredientPage";
import {EditCocktailPage} from "./pages/cocktails/EditCocktailPage";
import {useEffect, useState} from "react";
+import {BarChangePage} from "./pages/BarChangePage";
export function NavigationRoutes() {
const {auth} = useAuth();
@@ -64,6 +65,11 @@ const authPages = [
children: (),
exact: true,
},
+ {
+ path: paths.bar.list,
+ isPrivate: true,
+ children: (),
+ },
{
path: paths.bar.ingredients,
isPrivate: true,
diff --git a/front/src/app/pages/BarChangePage.js b/front/src/app/pages/BarChangePage.js
new file mode 100644
index 0000000..1ad3828
--- /dev/null
+++ b/front/src/app/pages/BarChangePage.js
@@ -0,0 +1,111 @@
+import Paper from "@mui/material/Paper";
+import React, {useEffect, useState} from "react";
+import {api} from "../../lib/clients/api";
+import {requests} from "../../requests";
+import {useAlert} from "../../hooks/useAlert";
+import {Card} from "@mui/material";
+import Stack from "@mui/material/Stack";
+import Typography from "@mui/material/Typography";
+import IconButton from "@mui/material/IconButton";
+import Box from "@mui/material/Box";
+import DeleteIcon from '@mui/icons-material/Delete';
+import ElectricalServicesIcon from '@mui/icons-material/ElectricalServices';
+import {getComparator} from "../../components/core/getComparator";
+import Toolbar from "@mui/material/Toolbar";
+import AddCircleIcon from '@mui/icons-material/AddCircle';
+import {BarCreateModal} from "../../components/BarCreateModal";
+import PowerIcon from '@mui/icons-material/Power';
+
+export function BarChangePage() {
+ const [bars, setBars] = useState([])
+ const [open, setOpen] = useState(false)
+ const {createError, createSuccess, createWarning} = useAlert();
+
+ useEffect(() => {
+ api().get(requests.bar.list)
+ .then((r) => {
+ setBars(r.data.sort(getComparator("name")))
+ })
+ .catch(() => {
+ createError("Ошибка получения списков")
+ })
+ }, []);
+ const changeHandler = (bar) => {
+ createWarning("Дождитесь окончания операции")
+ api().post(`${requests.bar.change}/${bar.id}`)
+ .then(() => createSuccess("Список изменен"))
+ .catch(() => createError("Ошибка изменения активного списка"))
+
+ const newState = bars.map((b) => {
+ if (b.active) {
+ return {
+ ...b, active: false
+ }
+ }
+ if (b.id === bar.id) {
+ return {
+ ...b, active: true
+ }
+ }
+ return b;
+ })
+ setBars(newState);
+ }
+ const deleteHandler = (bar) => {
+ if (bar.active) {
+ createError("Нельзя удалить активный бар!")
+ return;
+ }
+ api().delete(requests.bar.crud + bar.id)
+ .then(() => createSuccess("Список удален"))
+ .catch(() => createError("Ошибка удаления. Обновите страницу"))
+
+ setBars(bars.filter((b) => b.id !== bar.id));
+ }
+ const createHandler = (name) => {
+ api().post(requests.bar.crud + name)
+ .then((r) => {
+ createSuccess("Cписок создан");
+ let state = bars;
+ state.push(r.data);
+ setBars(state)
+ setOpen(false)
+ }).catch(() => createError("Ошибка создания списка"))
+ }
+
+ function closeHandler() {
+ setOpen(false)
+ }
+
+ return (<>
+
+
+
+ Списки ингредиентов (бары)
+ setOpen(true)}>
+
+
+
+ {bars.map((b) => {
+ return
+
+ {b.name}
+ {b.active &&
+
+ }
+ {!b.active &&
+ deleteHandler(b)}>
+
+
+ changeHandler(b)}>
+
+
+ }
+
+
+ })}
+
+ >
+
+ )
+}
\ No newline at end of file
diff --git a/front/src/components/BarCreateModal.js b/front/src/components/BarCreateModal.js
new file mode 100644
index 0000000..e6ab06b
--- /dev/null
+++ b/front/src/components/BarCreateModal.js
@@ -0,0 +1,39 @@
+import Dialog from "@mui/material/Dialog";
+import DialogTitle from "@mui/material/DialogTitle";
+import * as React from "react";
+import Typography from "@mui/material/Typography";
+import DialogContent from "@mui/material/DialogContent";
+import DialogActions from "@mui/material/DialogActions";
+import Button from "@mui/material/Button";
+import {useState} from "react";
+import TextField from "@mui/material/TextField";
+
+export function BarCreateModal({open, close, create}) {
+ const [value, setValue] = useState("");
+ return (
+
+ )
+}
\ No newline at end of file
diff --git a/front/src/components/cocktails/Cocktail.js b/front/src/components/cocktails/Cocktail.js
index 5fcd76c..ec82a54 100644
--- a/front/src/components/cocktails/Cocktail.js
+++ b/front/src/components/cocktails/Cocktail.js
@@ -11,6 +11,7 @@ import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete';
import LocalBarIcon from '@mui/icons-material/LocalBar';
import {paths} from "../../path";
+import {useAlert} from "../../hooks/useAlert";
function renderFavouriteBadge(handleFavourite, row) {
const childIcon = row.rating.favourite ? : ;
@@ -33,6 +34,7 @@ function renderRating(handleChangeRating, row) {
}
export function Cocktail({row, handleFavourite, handleChangeRating, handleSelect, deleteHandler}) {
+ const {notImplement} = useAlert();
return (
@@ -55,24 +57,23 @@ export function Cocktail({row, handleFavourite, handleChangeRating, handleSelect
image={row.image.includes("thecocktaildb") ? (row.image + "/preview") : row.image}
/>
-
+ notImplement()}>
{renderFavouriteBadge(handleFavourite, row)}
{renderRating(handleChangeRating, row)}
-
-
+
+
- deleteHandler(row)}>
-
+ deleteHandler(row)}>
+
{row.name}
-
diff --git a/front/src/hooks/useAlert.js b/front/src/hooks/useAlert.js
index 7cfdaf8..0599ea3 100644
--- a/front/src/hooks/useAlert.js
+++ b/front/src/hooks/useAlert.js
@@ -16,6 +16,10 @@ export function useAlert() {
createAlert("Данный функционал пока не реализован", {variant: 'warning'});
}
+ function createWarning(message) {
+ createAlert(message, {variant: 'warning'})
+ }
+
function createError(message) {
createAlert(message, {variant: "error"});
}
@@ -28,5 +32,5 @@ export function useAlert() {
createAlert(message, {variant: "success"});
}
- return {createAlert, notImplement, createError, getError, createSuccess}
+ return {createAlert, notImplement, createError, getError, createSuccess, createWarning}
}
\ No newline at end of file
diff --git a/front/src/navItems.js b/front/src/navItems.js
index 9515d23..d0e96a9 100644
--- a/front/src/navItems.js
+++ b/front/src/navItems.js
@@ -2,6 +2,7 @@ import {paths} from "./path";
export const navItems = [
{key: 'menu', title: 'Меню', href: paths.dashboard.overview, icon: 'menu'},
+ {key: 'barList', title: 'Список баров', href: paths.bar.list, icon: 'basket', forBarmen: true},
{key: 'ingredients', title: 'Список ингредиентов', href: paths.bar.ingredients, icon: 'basket', forBarmen: true},
{key: 'ingredientEdit', title: 'Ингредиенты', href: paths.bar.ingredientEdit, icon: 'ingredients', forAdmin: true},
{key: 'cocktailEdit', title: 'Коктейли', href: paths.bar.cocktailEdit, icon: 'cocktail', forAdmin: true}
diff --git a/front/src/requests.js b/front/src/requests.js
index c895fd9..810b4b9 100644
--- a/front/src/requests.js
+++ b/front/src/requests.js
@@ -36,7 +36,9 @@ export const requests = {
invite: routes.visitor + "/invite?"
},
bar: {
- list: routes.bar + "list",
+ list: routes.bar + "all",
+ change: routes.bar + "change",
+ crud: routes.bar,
addToMyList: routes.bar + "addToMyList",
enter: routes.bar + "enter?id=",
pay: routes.order + "?",
diff --git a/src/main/java/ru/kayashov/bar/controller/BarController.java b/src/main/java/ru/kayashov/bar/controller/BarController.java
index d9d02ad..15a091c 100644
--- a/src/main/java/ru/kayashov/bar/controller/BarController.java
+++ b/src/main/java/ru/kayashov/bar/controller/BarController.java
@@ -3,11 +3,15 @@ package ru.kayashov.bar.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import ru.kayashov.bar.controller.dto.VisitorResponseDto;
+import ru.kayashov.bar.controller.dto.bar.BarResponseDto;
import ru.kayashov.bar.controller.dto.cocktail.ReceiptResponseDto;
import ru.kayashov.bar.model.entity.Category;
import ru.kayashov.bar.model.entity.Glass;
@@ -52,6 +56,26 @@ public class BarController {
return sessionService.getReceiptList(id);
}
+ @PostMapping("/change/{id}")
+ public void changeActiveBar(@PathVariable Long id) {
+ sessionService.changeActiveBar(id);
+ }
+
+ @DeleteMapping("/{id}")
+ public void deleteBar(@PathVariable Long id) {
+ sessionService.deleteBar(id);
+ }
+
+ @PostMapping("/{name}")
+ public BarResponseDto createBar(@PathVariable String name) {
+ return sessionService.createBar(name);
+ }
+
+ @GetMapping("/all")
+ public List getAll() {
+ return sessionService.findAllBar();
+ }
+
@GetMapping("/getMe")
public VisitorResponseDto getMe() {
Visitor visitor = visitorService.getCurrentVisitor();
diff --git a/src/main/java/ru/kayashov/bar/controller/dto/bar/BarResponseDto.java b/src/main/java/ru/kayashov/bar/controller/dto/bar/BarResponseDto.java
index c0d9a52..584657c 100644
--- a/src/main/java/ru/kayashov/bar/controller/dto/bar/BarResponseDto.java
+++ b/src/main/java/ru/kayashov/bar/controller/dto/bar/BarResponseDto.java
@@ -2,13 +2,20 @@ package ru.kayashov.bar.controller.dto.bar;
import lombok.Getter;
import lombok.Setter;
+import ru.kayashov.bar.model.entity.BarEntity;
@Getter
@Setter
public class BarResponseDto {
private Long id;
private String name;
- private Boolean open;
- private Boolean enter;
- private String myRole;
+ private Boolean active;
+
+ public static BarResponseDto mapToDto(BarEntity barEntity) {
+ BarResponseDto barResponseDto = new BarResponseDto();
+ barResponseDto.setId(barEntity.getId());
+ barResponseDto.setName(barEntity.getName());
+ barResponseDto.setActive(barEntity.getActive());
+ return barResponseDto;
+ }
}
diff --git a/src/main/java/ru/kayashov/bar/model/entity/IngredientEntity.java b/src/main/java/ru/kayashov/bar/model/entity/IngredientEntity.java
index 67d8a2d..9e46e77 100644
--- a/src/main/java/ru/kayashov/bar/model/entity/IngredientEntity.java
+++ b/src/main/java/ru/kayashov/bar/model/entity/IngredientEntity.java
@@ -31,6 +31,7 @@ public class IngredientEntity {
private String enName;
private Boolean alcohol;
private Integer abv;
+ private Boolean isHave;
@Column(columnDefinition = "text")
private String description;
diff --git a/src/main/java/ru/kayashov/bar/service/CocktailService.java b/src/main/java/ru/kayashov/bar/service/CocktailService.java
index c870942..563cc5c 100644
--- a/src/main/java/ru/kayashov/bar/service/CocktailService.java
+++ b/src/main/java/ru/kayashov/bar/service/CocktailService.java
@@ -15,14 +15,12 @@ import ru.kayashov.bar.controller.dto.cocktail.CocktailModalDto;
import ru.kayashov.bar.controller.dto.cocktail.CocktailSimpleResponseDto;
import ru.kayashov.bar.controller.dto.cocktail.ReceiptResponseDto;
import ru.kayashov.bar.mapper.CocktailMapper;
-import ru.kayashov.bar.model.Ingredient;
import ru.kayashov.bar.model.entity.Alcoholic;
import ru.kayashov.bar.model.entity.Category;
import ru.kayashov.bar.model.entity.CocktailEntity;
import ru.kayashov.bar.model.entity.Glass;
import ru.kayashov.bar.model.entity.IngredientEntity;
import ru.kayashov.bar.model.entity.ReceiptEntity;
-import ru.kayashov.bar.model.entity.Visitor;
import ru.kayashov.bar.repository.CocktailRepository;
import ru.kayashov.bar.repository.IngredientRepository;
import ru.kayashov.bar.repository.ReceiptRepository;
@@ -37,7 +35,6 @@ import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;
-import javax.print.attribute.standard.MediaSize;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -82,7 +79,8 @@ public class CocktailService {
criteriaQuery.distinct(true);
if (!dto.getAll()) {
- List cocktailIds = findICountCocktailIds(0, new ArrayList<>());
+// List cocktailIds = findICountCocktailIds(0, new ArrayList<>());
+ List cocktailIds = findCocktailByCountNotHaveIngredient();
Predicate pr = root.get("id").in(cocktailIds);
predicates.add(pr);
}
@@ -225,35 +223,18 @@ public class CocktailService {
group by r.cocktail_id) as cifc
where false_count = 0
*/
- private List findCocktailByCountNotHaveIngredient(Integer notHaveCount, List currentIngredient) {
- CriteriaBuilder cb = entityManager.getCriteriaBuilder();
- List predicates = new ArrayList<>();
- CriteriaQuery mainQuery = cb.createQuery(Long.class);
+ //todo: так и не придумал я нормальный запрос
+ private List findCocktailByCountNotHaveIngredient() {
+ String sql = "SELECT cifc.cocktail_id" +
+ " FROM (SELECT r.cocktail_id," +
+ " COUNT(CASE WHEN i.is_have = false THEN 1 END) AS false_count" +
+ " FROM receipt r" +
+ " LEFT JOIN public.ingredient i ON i.id = r.ingredient_id" +
+ " GROUP BY r.cocktail_id) AS cifc" +
+ " WHERE false_count = 0";
- // Создаем корневые сущности
- Subquery sq = mainQuery.subquery(Long.class);
- Root rsq = sq.from(ReceiptEntity.class);
- Join ingredientJoin = rsq.join("ingredient", JoinType.LEFT);
- // Создаем подзапрос
- sq.select(cb.count(cb.selectCase(cb.isFalse(ingredientJoin.get("isHave")))))
- .groupBy(rsq.get("cocktail"))
- .having(cb.equal(sq.getSelection(), notHaveCount));
-
- // Создаем внешний запрос
- mainQuery.select(rsq.get("cocktail"));
- Predicate predicate = cb.exists(sq);
- predicates.add(predicate);
-// mainQuery.where(cb.exists(sq));
-
- if(!currentIngredient.isEmpty()) {
- predicates.add(rsq.get("ingredient").in(currentIngredient));
- }
-
- mainQuery.where(predicates.toArray(new Predicate[0]));
-
- // Выполняем запрос
- return entityManager.createQuery(mainQuery)
- .getResultList();
+ javax.persistence.Query query = entityManager.createNativeQuery(sql);
+ return query.getResultList();
}
public CocktailForListResponseDto findById(Long id) {
diff --git a/src/main/java/ru/kayashov/bar/service/IngredientService.java b/src/main/java/ru/kayashov/bar/service/IngredientService.java
index 17f69c6..9a9263e 100644
--- a/src/main/java/ru/kayashov/bar/service/IngredientService.java
+++ b/src/main/java/ru/kayashov/bar/service/IngredientService.java
@@ -25,6 +25,7 @@ public class IngredientService {
private final IngredientRepository repository;
private final IngredientMapper mapper;
private final BarEntityRepository barEntityRepository;
+ private final IngredientRepository ingredientRepository;
private TypeEntity findTypeByName(String name) {
return typeRepository.findByName(name)
@@ -57,6 +58,8 @@ public class IngredientService {
} else {
bar.getIngredients().remove(ingredientEntity);
}
+ ingredientEntity.setIsHave(isHave);
+ ingredientRepository.save(ingredientEntity);
barEntityRepository.save(bar);
}
diff --git a/src/main/java/ru/kayashov/bar/service/SessionService.java b/src/main/java/ru/kayashov/bar/service/SessionService.java
index ce1d8a3..2b63bb6 100644
--- a/src/main/java/ru/kayashov/bar/service/SessionService.java
+++ b/src/main/java/ru/kayashov/bar/service/SessionService.java
@@ -4,14 +4,20 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
+import ru.kayashov.bar.controller.dto.bar.BarResponseDto;
import ru.kayashov.bar.controller.dto.cocktail.ReceiptResponseDto;
import ru.kayashov.bar.controller.dto.ingredient.IngredientSimpleResponseDto;
+import ru.kayashov.bar.model.entity.BarEntity;
import ru.kayashov.bar.model.entity.CocktailEntity;
+import ru.kayashov.bar.model.entity.IngredientEntity;
import ru.kayashov.bar.model.entity.Visitor;
+import ru.kayashov.bar.repository.BarEntityRepository;
import ru.kayashov.bar.repository.CocktailRepository;
+import ru.kayashov.bar.repository.IngredientRepository;
import ru.kayashov.bar.repository.VisitorsRepository;
import java.util.List;
+import java.util.concurrent.ExecutorService;
@Slf4j
@Service
@@ -20,6 +26,8 @@ public class SessionService {
private final VisitorsRepository visitorsRepository;
private final CocktailRepository cocktailRepository;
+ private final BarEntityRepository barEntityRepository;
+ private final IngredientRepository ingredientRepository;
public Visitor getVisitor() {
Long id = ((Visitor) SecurityContextHolder.getContext()
@@ -40,4 +48,42 @@ public class SessionService {
.build())
.toList();
}
+
+ public void changeActiveBar(Long id) {
+ BarEntity lastBar = barEntityRepository.findByActiveTrue().orElseThrow();
+ lastBar.setActive(false);
+ barEntityRepository.save(lastBar);
+
+ lastBar.getIngredients().stream()
+ .peek(i -> i.setIsHave(false))
+ .forEach(ingredientRepository::save);
+
+ BarEntity barEntity = barEntityRepository.findById(id).orElseThrow();
+ barEntity.setActive(true);
+
+ barEntity.getIngredients().stream()
+ .peek(i -> i.setIsHave(true))
+ .forEach(ingredientRepository::save);
+
+ barEntityRepository.save(barEntity);
+ }
+
+ public List findAllBar() {
+ return barEntityRepository.findAll().stream()
+ .map(BarResponseDto::mapToDto)
+ .toList();
+ }
+
+ public void deleteBar(Long id) {
+ barEntityRepository.deleteById(id);
+ }
+
+ public BarResponseDto createBar(String name) {
+ BarEntity bar = new BarEntity();
+ bar.setName(name);
+ bar.setActive(false);
+ bar = barEntityRepository.save(bar);
+
+ return BarResponseDto.mapToDto(bar);
+ }
}