Initial commit
This commit is contained in:
169
front/src/app/pages/ingredients/EditIngredientPage.js
Normal file
169
front/src/app/pages/ingredients/EditIngredientPage.js
Normal file
@@ -0,0 +1,169 @@
|
||||
import Box from "@mui/material/Box";
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import * as React from "react";
|
||||
import {useEffect, useState} from "react";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import {Autocomplete, FormControl, FormControlLabel, InputLabel} from "@mui/material";
|
||||
import {api} from "../../../lib/clients/api";
|
||||
import {requests} from "../../../requests";
|
||||
import {useAlert} from "../../../hooks/useAlert";
|
||||
import {useSearchParams} from "react-router-dom";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Switch from "@mui/material/Switch";
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Button from "@mui/material/Button";
|
||||
import Select from "@mui/material/Select";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import {getComparator} from "../../../components/core/getComparator";
|
||||
|
||||
const emptyIngredient = {
|
||||
id: null,
|
||||
name: "",
|
||||
enName: "",
|
||||
have: false,
|
||||
image: null,
|
||||
type: "",
|
||||
alcohol: false,
|
||||
abv: null,
|
||||
description: null
|
||||
}
|
||||
|
||||
export function EditIngredientPage() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const [ingredients, setIngredients] = useState([]);
|
||||
const [types, setTypes] = useState([]);
|
||||
const [ingredient, setIngredient] = useState(emptyIngredient)
|
||||
const {createError, createSuccess} = useAlert();
|
||||
useEffect(() => {
|
||||
api().get(requests.bar.ingredientList)
|
||||
.then((r) => {
|
||||
const arr = r.data.sort(getComparator("asc", "name"));
|
||||
setIngredients(arr)
|
||||
|
||||
const currentId = searchParams.get("id");
|
||||
if (!currentId) {
|
||||
return;
|
||||
}
|
||||
const currentIngredient = arr.find((r) => r.id === (currentId * 1));
|
||||
if (!currentIngredient) {
|
||||
return;
|
||||
}
|
||||
setIngredient(currentIngredient);
|
||||
})
|
||||
.catch(() => createError("Ошибка получения данных"))
|
||||
|
||||
api().get(requests.bar.type)
|
||||
.then((r) => setTypes(r.data.sort(getComparator("asc", "name"))))
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const changeIngredientValue = (name, value) => {
|
||||
setIngredient((prev) => ({
|
||||
...prev,
|
||||
[name]: value
|
||||
}))
|
||||
}
|
||||
const saveIngredientHandler = () => {
|
||||
api().patch(requests.bar.ingredient, ingredient)
|
||||
.then(() => createSuccess("Ингредиент сохранен"))
|
||||
.catch(() => createError("Ошибка сохранения"))
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{/*Заголовок*/}
|
||||
<Toolbar>
|
||||
<Typography variant="h6" component="div" sx={{flexGrow: 1}}>Ингредиенты</Typography>
|
||||
</Toolbar>
|
||||
{/*Поиск*/}
|
||||
<Paper elevation={6} sx={{my: 2, display: 'grid', p: 2}}>
|
||||
<Autocomplete
|
||||
disablePortal
|
||||
options={ingredients}
|
||||
|
||||
defaultChecked={emptyIngredient}
|
||||
onChange={(e, v) => {
|
||||
console.log(v);
|
||||
return !v ? setIngredient(emptyIngredient) : setIngredient(v)
|
||||
}}
|
||||
isOptionEqualToValue={(selected, value) => selected.id === value.id}
|
||||
getOptionKey={(selected) => selected.id}
|
||||
getOptionLabel={(selected) => selected.name}
|
||||
renderInput={(params) => <TextField {...params} label="Ингредиенты"/>}
|
||||
/>
|
||||
</Paper>
|
||||
{/*Форма ингредиента*/}
|
||||
<Paper elevation={6} sx={{my: 2, display: 'grid', p: 1, pb: 2}}>
|
||||
<Stack>
|
||||
<Box display={'flex'} justifyContent={'flex-end'} pr={2}>
|
||||
<FormControlLabel control={
|
||||
<Switch
|
||||
checked={ingredient.have}
|
||||
onChange={() => changeIngredientValue("have", !ingredient.have)}
|
||||
/>}
|
||||
label={"Наличие"} labelPlacement={'start'}/>
|
||||
</Box>
|
||||
<Box>
|
||||
<img src={ingredient.image} alt={""} loading={'eager'}/>
|
||||
</Box>
|
||||
<Box m={1}>
|
||||
<TextField sx={{mr: 1, mb: 2, minWidth: 330}}
|
||||
variant="outlined" label={"Название"}
|
||||
value={ingredient.name}
|
||||
onChange={(e) => changeIngredientValue("name", e.target.value)}/>
|
||||
<TextField sx={{mr: 1, mb: 2, minWidth: 330}}
|
||||
label="Английское название" variant="outlined"
|
||||
value={ingredient.enName}
|
||||
onChange={(e) => changeIngredientValue("enName", e.target.value)}/>
|
||||
</Box>
|
||||
|
||||
<Box height={70} mt={1} ml={1}>
|
||||
<FormControlLabel sx={{pt: 1}}
|
||||
control={
|
||||
<Switch
|
||||
checked={ingredient.alcohol}
|
||||
onChange={() => changeIngredientValue("alcohol", !ingredient.alcohol)}
|
||||
/>}
|
||||
label="Алкогольный"/>
|
||||
{ingredient.alcohol && (
|
||||
<TextField sx={{width: 100}}
|
||||
variant='outlined' label='Градус'
|
||||
value={!ingredient.abv ? "" : ingredient.abv}
|
||||
onChange={(e) => changeIngredientValue("abv", e.target.value)}/>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Box mb={2} ml={1}>
|
||||
<FormControl sx={{width: 330}}>
|
||||
<InputLabel id="select-label">Категория</InputLabel>
|
||||
<Select
|
||||
id="select-label"
|
||||
autoWidth
|
||||
label={"Категория"}
|
||||
value={!ingredient.type ? "" : ingredient.type}
|
||||
onChange={(e) => changeIngredientValue("type", e.target.value)}
|
||||
>
|
||||
<MenuItem value="">
|
||||
<em>None</em>
|
||||
</MenuItem>
|
||||
{types.map((c) => {
|
||||
return (<MenuItem key={c.id} value={c.name}>{c.name}</MenuItem>)
|
||||
})}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Box>
|
||||
|
||||
<Box pr={2} ml={1}>
|
||||
<TextField sx={{width: '100%'}}
|
||||
label={"Описание"} variant='outlined' multiline
|
||||
value={!ingredient.description ? "" : ingredient.description}/>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
<Box display={'flex'} justifyContent={'flex-end'}>
|
||||
<Button variant='contained' onClick={() => saveIngredientHandler()}>Сохранить</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
153
front/src/app/pages/ingredients/IngredientsPage.js
Normal file
153
front/src/app/pages/ingredients/IngredientsPage.js
Normal file
@@ -0,0 +1,153 @@
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import {Fab, FormControl, FormControlLabel, InputAdornment, InputLabel, OutlinedInput, Tabs} from "@mui/material";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import SearchIcon from "@mui/icons-material/Search";
|
||||
import * as React from "react";
|
||||
import {useEffect, useMemo, useState} from "react";
|
||||
import {Loading} from "../../../components/core/Loading";
|
||||
import {requests} from "../../../requests";
|
||||
import {useAlert} from "../../../hooks/useAlert";
|
||||
import {IngredientInfoModal} from "../../../components/Ingredients/IngredientInfoModal";
|
||||
import {api} from "../../../lib/clients/api";
|
||||
import Tab from "@mui/material/Tab";
|
||||
import {a11yProps} from "../../../components/core/tabProps";
|
||||
import {CustomTabPanel} from "../../../components/core/TabPanel";
|
||||
import {IngredientList} from "../../../components/Ingredients/IngredientList";
|
||||
import {blue} from "@mui/material/colors";
|
||||
import UpIcon from "@mui/icons-material/KeyboardArrowUp";
|
||||
import Switch from "@mui/material/Switch";
|
||||
|
||||
export function IngredientsPage() {
|
||||
const [value, setValue] = React.useState(0);
|
||||
const [grouping, setGrouping] = useState(true);
|
||||
const handleChange = (event, newValue) => setValue(newValue);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [findString, setFindString] = useState("");
|
||||
const [ingredients, setIngredients] = useState([]);
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const [selectedInfo, setSelectedInfo] = useState(null);
|
||||
const {createError} = useAlert();
|
||||
|
||||
useEffect(() => {
|
||||
api().get(requests.bar.ingredientList)
|
||||
.then((r) => {
|
||||
setIngredients(r.data)
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(() => {
|
||||
createError("Ошибка получения списка ингредиентов");
|
||||
setLoading(false);
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const visibleIngredients = useMemo(() => {
|
||||
if (findString.length === 0) {
|
||||
return ingredients;
|
||||
}
|
||||
const reg = new RegExp("(.*?)" + findString + "(.*?)", "i");
|
||||
return ingredients.filter((ingredient) => ingredient.name.match(reg) !== null);
|
||||
}, [findString, ingredients]);
|
||||
const ingredientsToAdd = visibleIngredients.filter((ingredient) => !ingredient.have);
|
||||
const ingredientsInBar = visibleIngredients.filter((ingredient) => ingredient.have);
|
||||
|
||||
const changeHandler = (row, value) => {
|
||||
const newState = ingredients.map((ingredient) => {
|
||||
if (ingredient.id === row.id) {
|
||||
return {
|
||||
...ingredient,
|
||||
have: value
|
||||
}
|
||||
} else {
|
||||
return ingredient;
|
||||
}
|
||||
})
|
||||
const url = `${requests.bar.ingredient}?id=${row.id}`;
|
||||
const request = value ? api().put(url) : api().delete(url);
|
||||
request
|
||||
.then(() => {
|
||||
setIngredients(newState);
|
||||
})
|
||||
.catch(() => {
|
||||
createError("Ошибка изменения ингредиента");
|
||||
});
|
||||
}
|
||||
const handleOpenModal = (i) => {
|
||||
setOpenModal(true);
|
||||
setSelectedInfo(i);
|
||||
}
|
||||
const handleCloseModal = () => {
|
||||
setSelectedInfo(null);
|
||||
setOpenModal(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{/*Заголовок*/}
|
||||
<Toolbar>
|
||||
<Typography variant="h6" component="div" sx={{flexGrow: 1}}>Ингредиенты бара</Typography>
|
||||
</Toolbar>
|
||||
{/*Поиск*/}
|
||||
<Paper elevation={6} sx={{my: 2}}>
|
||||
<FormControl sx={{m: 1, width: 'calc(100% - 20px'}}>
|
||||
<InputLabel htmlFor="outlined-adornment-amount">Поиск</InputLabel>
|
||||
<OutlinedInput
|
||||
onChange={(e) => setFindString(e.target.value)}
|
||||
label="With normal TextField"
|
||||
startAdornment={
|
||||
<InputAdornment position="start">
|
||||
<IconButton edge="end">
|
||||
<SearchIcon/>
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControlLabel sx={{ml: '2px'}}
|
||||
control={<Switch defaultChecked/>}
|
||||
onClick={() => setGrouping(!grouping)}
|
||||
label="Группировать"
|
||||
labelPlacement="end"/>
|
||||
</Paper>
|
||||
{/*Рабочее поле ингредиентов*/}
|
||||
<Box>
|
||||
<Tabs value={value} onChange={handleChange} aria-label="basic tabs example">
|
||||
<Tab label="В баре" {...a11yProps(0)} />
|
||||
<Tab label="Список" {...a11yProps(1)} />
|
||||
</Tabs>
|
||||
</Box>
|
||||
<Box>
|
||||
<CustomTabPanel value={value} index={0}>
|
||||
<IngredientList rows={ingredientsInBar} value={false} changeHandler={changeHandler}
|
||||
infoHandler={handleOpenModal} grouping={grouping}/>
|
||||
</CustomTabPanel>
|
||||
<CustomTabPanel value={value} index={1}>
|
||||
<IngredientList rows={ingredientsToAdd} value={true} changeHandler={changeHandler}
|
||||
infoHandler={handleOpenModal} grouping={grouping}/>
|
||||
</CustomTabPanel>
|
||||
</Box>
|
||||
<Fab sx={{
|
||||
alpha: '30%',
|
||||
position: 'sticky',
|
||||
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>
|
||||
{/*Загрузчик*/}
|
||||
<Loading loading={loading}/>
|
||||
{/*Модальное окно информации об ингредиенте*/}
|
||||
<IngredientInfoModal ingredient={selectedInfo} open={openModal} closeHandler={handleCloseModal}/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user