Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
add ingredient filtering
  • Loading branch information
TsundereKurisu committed Apr 26, 2022
commit 94886e9aedca902de12a332819e29ca5eb26a726
13 changes: 12 additions & 1 deletion frontend/json-mock-api/src/db.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,15 @@
"bardzo biedny"
]
}
]}
],
"ingredients": {
"ingredients":[{
"id": "0",
"name": "piwo"
},
{
"id": "1",
"name": "piwo"
}]}

}
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"react-dom": "^17.0.2",
"react-router-dom": "^6.2.2",
"react-scripts": "^5.0.0",
"react-toastify": "^8.2.0",
"typescript": "^4.6.2",
"web-vitals": "^2.1.4"
},
Expand Down
80 changes: 74 additions & 6 deletions frontend/src/api/api.api.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
import axios from 'axios';
import { toast } from 'react-toastify';
import { server } from '../constants/constants'
import { RecipeDetails } from '../models/models';

export async function getIngredientsApi(page: number, limit: number) {
const key = "" + sessionStorage.getItem("key");
try {
const response = await axios.get(`${server}/ingredients?page=${page}&limit=${limit}`, {
const response = await axios.post(`${server}/ingredients/all?page=${page}&limit=${limit}`, {
},{
headers: {
'security_header': key
}});

return response.data;
} catch (error) {
toast.error(String(error));
throw error;
}
}
export async function getFilteredIngredientsApi(page: number, limit: number, filter: string) {
if(filter === "" || filter===undefined)return getIngredientsApi(page,limit);

const key = "" + sessionStorage.getItem("key");
try {
const response = await axios.post(`${server}/ingredients/all?page=${page}&limit=${limit}`, {
"name": filter,
},{
headers: {
'security_header': key
}})
return response.data;
} catch (error) {
throw error;
Expand All @@ -18,18 +39,17 @@ export async function addIngredientApi(name: string) {
const key = "" + sessionStorage.getItem("key");
try {
await axios.post(`${server}/ingredients`, {
id: 0,
name: name
},{
headers: {
'security_header': key
}})
} catch (error) {
toast.error(String(error));
throw error;
}
}


export async function deleteIngredientApi(id: number) {
const key = "" + sessionStorage.getItem("key");
try {
Expand All @@ -38,39 +58,87 @@ export async function deleteIngredientApi(id: number) {
'security_header': key
}})
} catch (error) {
toast.error(String(error));
throw error;
}
}

export async function editIngredientApi(name: string, id: number) {
const key = "" + sessionStorage.getItem("key");
try {
await axios.put(`${server}/ingredients/${id}`, {
await axios.put(`${server}/ingredients`, {
id: id,
name: name
},{
headers: {
'security_header': key
}})
} catch (error) {
toast.error(String(error));
throw error;
}
}

export async function deleteRecipeApi(id: number) {
const key = "" + sessionStorage.getItem("key");
try {
await axios.delete(`${server}/recipes/${id}`)
await axios.delete(`${server}/recipes/${id}`, {
headers: {
'security_header': key
}})
} catch (error) {
toast.error(String(error));
throw error;
}
}

export async function getRecipesApi(page: number, limit: number) {
const key = "" + sessionStorage.getItem("key");
try {
const response = await axios.post(`${server}/recipes/all?page=${page}&limit=${limit}`, {
},{
headers: {
'security_header': key
}});
return response.data;

} catch (error) {
toast.error(String(error));
throw error;
}
}

export async function editRecipeApi(recipe: RecipeDetails, id: number) {
const key = "" + sessionStorage.getItem("key");
try {
const response = await axios.get(`${server}/recipes?page=${page}&limit=${limit}`);
const response = await axios.put(`${server}/recipes/${id}`,{
id:recipe.id,
name:recipe.name,
instructions:recipe.instructions,
ingredients:recipe.ingredients,
tags:recipe.tags

},{
headers: {
'security_header': key
}})
return response.data;

} catch (error) {
toast.error(String(error));
throw error;
}
}

export async function registerApi(username: string, password: string) {
try {
const response = await axios.post(`${server}/register`, {
username: username,
password: password
})
return response;
} catch (error) {
toast.error(String(error));
throw error;
}
}
38 changes: 32 additions & 6 deletions frontend/src/api/api.test.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import axios from "axios";
import {getIngredientsApi, addIngredientApi, deleteIngredientApi, deleteRecipeApi, editIngredientApi} from "./api.api";
import {getIngredientsApi, addIngredientApi, deleteIngredientApi, deleteRecipeApi, editIngredientApi, editRecipeApi, registerApi} from "./api.api";
import data from "../constants/ingredient-table.data.json";

describe("Api tests", () => {
jest.mock("axios");

it("should get ingredients", async () => {
const payload = { data: data };
axios.get = jest.fn().mockImplementationOnce(() => Promise.resolve(payload));
axios.post = jest.fn().mockImplementationOnce(() => Promise.resolve(payload));
await expect(getIngredientsApi(0, 100)).resolves.toEqual(data);
});

it("should thorw error while getting ingredients", async () => {
const errorMessage = new Error("rejected");
axios.get = jest.fn().mockImplementationOnce(() => Promise.reject(errorMessage));
axios.post = jest.fn().mockImplementationOnce(() => Promise.reject(errorMessage));
await expect(getIngredientsApi(0, 100)).rejects.toThrow(errorMessage);
});

Expand Down Expand Up @@ -47,17 +47,43 @@ describe("Api tests", () => {
it("should throw error while deleting recipe", async () => {
const errorMessage = new Error("rejected");
axios.delete = jest.fn().mockImplementationOnce(() => Promise.reject(errorMessage));
await expect(deleteRecipeApi(0)).rejects.toThrow(errorMessage);
await expect(deleteRecipeApi(0)).rejects.toThrow();
});

it("edit recipe", async () => {
it("edit ingredient", async () => {
axios.put = jest.fn().mockImplementationOnce(() => Promise.resolve());
await expect(editIngredientApi("piwo",0)).resolves.toBeUndefined();
});

it("should throw error while editing recipe", async () => {
it("should throw error while editing ingredient", async () => {
const errorMessage = new Error("rejected");
axios.put = jest.fn().mockImplementationOnce(() => Promise.reject(errorMessage));
await expect(editIngredientApi("piwo",0)).rejects.toThrow(errorMessage);
});

it("should throw error while editing recipe", async () => {
const errorMessage = new Error("rejected");
axios.put = jest.fn().mockImplementationOnce(() => Promise.reject(errorMessage));
await expect(editRecipeApi({
"id": 0,
"name": "super jedzenie",
"instructions": "fajny nowy opis",
"ingredients": [
{
"ingredient": {
"id": 2,
"name": "piwo2"
},
"quantity": 1
}
],
"tags": [
"pyszne",
"student",
"biedny"
]
},0)).rejects.toThrow(errorMessage);
});


});
Original file line number Diff line number Diff line change
@@ -1,26 +1,58 @@
import { useEffect, useState } from 'react';

import CircularProgress from '@mui/material/CircularProgress';
import { getIngredientsApi, addIngredientApi, deleteIngredientApi, editIngredientApi } from '../../api/api.api';
import { getIngredientsApi, addIngredientApi, deleteIngredientApi, editIngredientApi, getFilteredIngredientsApi } from '../../api/api.api';
import Box from '@mui/system/Box';
import IngredientsTable from '../ingredients-table/ingredients-table.component';
import { IngredientDetails } from '../../models/models';
import AddIngredient from '../ingredients-table-add-ingredient/add-ingredient.component';

import { Button, MenuItem, TextField } from '@mui/material';
import { CollectionsBookmarkRounded } from '@mui/icons-material';
import styled from '@emotion/styled';
export default function IngredientsTableAdminView() {
const [ingredients, setIngredients] = useState<IngredientDetails[]>([]);
const [isFetching, setIsFetching] = useState<boolean>(true);
const [page, setPage] = useState<number>(0);
const [limit, setLimit] = useState<number>(10);
const [count, setCount] = useState<number>(0);
const [filter, setFilter] = useState('Name');
const [filterContent, setFilterContent] = useState('');
const filters = [
{value: "Name"},
{value: "?"}
]


const handleChangeFilter = (event: React.ChangeEvent<HTMLInputElement>) => {
setFilter(event.target.value);
};
const handleFilterContentChange= (event: React.ChangeEvent<HTMLInputElement>) => {
setFilterContent(event.target.value);
};
const handleFilterClick= (event:any) => {
getFilteredIngredients();
};
const getAllIngredients = () => {
getIngredientsApi(page, limit).then((response) => {
setIsFetching(true);
if(!!response.ingredients){
setIngredients(response.ingredients);
setCount(response.total_ingredients);
setCount(10); //change total_ingredients
};
setIsFetching(false);

});
};
const getFilteredIngredients = () => {
if(filter === "?")return;
getFilteredIngredientsApi(page, limit,filterContent).then((response) => {
setIsFetching(true);
if(!!response.ingredients){
setIngredients(response.ingredients);
setCount(10); //change total_ingredients
};
setIsFetching(false);

});
};

Expand Down Expand Up @@ -56,15 +88,28 @@ export default function IngredientsTableAdminView() {
}

useEffect(() => {
getAllIngredients();
getFilteredIngredients();
}, [limit, page])




return isFetching ? (
<Box sx={{ display: 'flex', justifyContent: 'center',}}>
<CircularProgress />
</Box>
) : (
<>
<SearchBar>
<TextField id="outlined-basic" variant="standard" value={filterContent} onChange={handleFilterContentChange}></TextField>
<TextField select value={filter} onChange={handleChangeFilter} variant = "standard">
{filters.map((option: any) => (
<MenuItem key={option.value} value={option.value} autoFocus={false}>
{option.value}
</MenuItem>
))}
</TextField>
<Button variant="contained" onClick = {handleFilterClick}>Search</Button>
</SearchBar>
<AddIngredient addIngredient={addIngredient}/>
<IngredientsTable
ingredients={ingredients}
Expand All @@ -77,5 +122,15 @@ export default function IngredientsTableAdminView() {
editIngredient={editIngredient}
/>
</>

);
}

}
const SearchBar = styled.div`
width: 30vw;
margin: 10px 10px 10px 0px;
display: flex;
flex-direction: row;
justify-content: space-between;
`;

Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import Paper from '@mui/material/Paper';
import IconButton from '@mui/material/IconButton'
import DeleteIcon from '@mui/icons-material/Delete';
import { IngredientDetails } from '../../models/models';
import { TextField } from '@mui/material';
import { TableFooter } from '@mui/material';
import { TablePagination } from '@mui/material';
import EditIngredient from '../ingredients-table-edit-ingredient/edit-ingredient.component';
import { Filter2Sharp } from '@mui/icons-material';
interface IngredientTableProps {
ingredients: IngredientDetails[];
page: number;
Expand All @@ -22,6 +24,7 @@ interface IngredientTableProps {
editIngredient: (name: string, id: number) => void;
}


export default function IngredientsTable({ingredients, page, limit, count, handleChangeRowsPerPage, handleChangePage, deleteIngredient, editIngredient}: IngredientTableProps) {
return (
<>
Expand Down