Skip to content

Commit 4aeccef

Browse files
committed
refactor: improve favorite api
1 parent daee158 commit 4aeccef

File tree

10 files changed

+31
-116
lines changed

10 files changed

+31
-116
lines changed

backend/.env.sample

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
WEB_ORIGIN=http://localhost:5173
22
DATABASE_URL=""
3+
# DATABASE_URL=""
34
VITE_SUPABASE_URL=
45
VITE_SUPABASE_ANON_KEY=
56
VITE_GOO_API_KEY=

backend/src/app.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ app.post("/api/recipes/search", SearchController.searchRecipes)
157157
app.post("/api/recipes/search/some", SearchController.searchSomeRecipes)
158158

159159
app.get("/api/users/favorites", UserController.getFavorites)
160-
app.post("/api/users/favorites", UserController.addFavorite)
160+
app.post("/api/users/favorites/:id", UserController.addFavorite)
161161
app.delete("/api/users/favorites/:id", UserController.deleteFavorite)
162162
app.get("/api/users", UserController.getUser)
163163

backend/src/controllers/RecipeController.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,6 @@ class RecipeController {
494494
// // description: recipeData.description,
495495
// // totalCookingTime: this.convertTotalCookingTimeToMinutes(recipeData.totalTime),
496496
// // materials: recipeData.recipeIngredient,
497-
// // keywords: this.convertKeywords(recipeData.keywords),
498497
// // sourceUrl: sourceUrl,
499498
// // foodImageUrl: recipeData.image[0],
500499
// // dish: recipeData.recipeCategory,
@@ -522,10 +521,6 @@ class RecipeController {
522521
)
523522
}
524523

525-
private convertKeywords = (keywords: string): string[] => {
526-
return keywords.split(",").map((keyword) => keyword.trim())
527-
}
528-
529524
private convertTotalCookingTimeToMinutes = (totalTime: string): number => {
530525
if (!totalTime || totalTime.length < 2) {
531526
return -1

backend/src/controllers/SearchController.ts

Lines changed: 0 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -114,61 +114,6 @@ class SearchController {
114114
res.status(500).json({ error: "Internal server error" })
115115
}
116116
}
117-
118-
// TODO: type=keywords
119-
private searchRecipesWithKeywords = async (req: Request, res: Response): Promise<void> => {
120-
try {
121-
const { keywords } = req.body
122-
123-
const result = await elasticSearchClient.search({
124-
index: "recipes",
125-
body: {
126-
query: {
127-
bool: {
128-
should: [
129-
{ bool: { should: [{ terms: { title: keywords } }] } },
130-
{ bool: { should: [{ terms: { description: keywords } }] } },
131-
],
132-
},
133-
},
134-
},
135-
})
136-
137-
const hits = result.hits.hits
138-
if (hits.length === 0) {
139-
res.status(404).json({ error: "Not found" })
140-
return
141-
}
142-
const recipes = hits.map((hit) => hit._source)
143-
res.json(recipes)
144-
} catch (error) {
145-
console.error(error)
146-
res.status(500).json({ error: "Internal server error" })
147-
}
148-
}
149-
150-
// TODO: type=query
151-
private searchRecipesWithQuery = async (req: Request, res: Response): Promise<void> => {
152-
try {
153-
const { query } = req.body
154-
const result = await elasticSearchClient.search({
155-
index: "recipes",
156-
body: {
157-
query,
158-
},
159-
})
160-
161-
const hits = result.hits.hits
162-
if (hits.length === 0) {
163-
res.status(404).json({ error: "Not found" })
164-
return
165-
}
166-
const recipes = hits.map((hit) => hit._source)
167-
res.json(recipes)
168-
} catch (error) {
169-
res.status(500).json(error)
170-
}
171-
}
172117
}
173118

174119
export default new SearchController()

backend/src/controllers/UserController.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class UserController {
6464

6565
addFavorite = async (req: Request, res: Response): Promise<void> => {
6666
try {
67-
const { recipeId } = req.body
67+
const recipeId = Number(req.params.id)
6868
const userFromRequest = await extractUserFromRequest(req)
6969
if (!userFromRequest) {
7070
res.status(401).json({ error: "Not authorized" })

frontend/src/components/RecipeCard/RecipeCard.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ type Recipe = components["schemas"]["Recipe"]
1212

1313
interface Props {
1414
recipe: Recipe
15-
favoriteRecipes: Recipe[] | undefined
15+
isFavorited: boolean
1616
toggleFavorite: (recipeId: number) => void
1717
}
1818

19-
export const RecipeCard = ({ recipe, favoriteRecipes, toggleFavorite }: Props) => {
19+
export const RecipeCard = ({ recipe, isFavorited, toggleFavorite }: Props) => {
2020
const { session } = useContext(UserContext)
2121
const textRef = useRef<HTMLDivElement>(null)
2222
const [textHeight, setTextHeight] = useState<number>(0)
@@ -28,10 +28,6 @@ export const RecipeCard = ({ recipe, favoriteRecipes, toggleFavorite }: Props) =
2828
}, [textRef.current])
2929

3030
const materialsConverted = recipe.materials.join("・")
31-
32-
const isFavorited: boolean =
33-
favoriteRecipes !== undefined && favoriteRecipes.some((favoriteRecipe) => favoriteRecipe.id === recipe.id)
34-
3531
const onClickHandler = (recipeId: number, event: React.MouseEvent<HTMLDivElement>) => {
3632
if (!session?.access_token) return
3733
// NOTE: LinkとonClickは別メソッド?なので event.stopPropagation() だとうまく行かなかった

frontend/src/pages/Favorite/Favorite.module.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.root {
2-
padding: 32px 16px;
2+
padding: 16px 16px 80px 16px;
33
display: inline-flex;
44
flex-direction: column;
55
align-items: flex-start;

frontend/src/pages/Favorite/Favorite.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,9 @@ export const Favorite = () => {
8383
const onClickAddFavorite = useMutation({
8484
mutationFn: async (recipeId: number) => {
8585
if (!session?.access_token) return []
86-
const response = await fetch(postUserFavoritesApi(), {
86+
const response = await fetch(postUserFavoritesApi(recipeId), {
8787
method: "POST",
8888
headers: { "Content-Type": "application/json", Authorization: `Bearer ${session?.access_token}` },
89-
body: JSON.stringify({ recipeId: recipeId }),
9089
})
9190
if (!response.ok) throw new Error("お気に入りの追加に失敗しました")
9291
},
@@ -183,7 +182,10 @@ export const Favorite = () => {
183182
<RecipeCard
184183
key={recipe.id}
185184
recipe={recipe}
186-
favoriteRecipes={favoriteRecipes}
185+
isFavorited={
186+
favoriteRecipes != null &&
187+
favoriteRecipes.some((favorite) => favorite.id.toString() === recipe.id.toString())
188+
}
187189
toggleFavorite={toggleFavorite}
188190
/>
189191
))

frontend/src/pages/Result/Result.tsx

Lines changed: 18 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { BackButton } from "../../components/BackButton"
99
import { BorderButton } from "../../components/BorderButton"
1010
import {
1111
postSearchRecipesApi,
12-
postSearchRecipesKeywordsApi,
1312
getUserFavoritesApi,
1413
postUserFavoritesApi,
1514
deleteUserFavoritesApi,
@@ -44,7 +43,11 @@ export const Result = () => {
4443
[...searchInfo.ingredients, searchInfo.dish, searchInfo.cookingTime].join(" ")
4544
)
4645

47-
const { data: recipes, isLoading: isLoadingRecipes } = useQuery({
46+
const {
47+
data: recipes,
48+
isLoading: isLoadingRecipes,
49+
refetch,
50+
} = useQuery({
4851
queryKey: ["recipes"],
4952
queryFn: async () => {
5053
const response = await fetch(postSearchRecipesApi(), {
@@ -85,10 +88,9 @@ export const Result = () => {
8588
const addFavorite = useMutation({
8689
mutationFn: async (recipeId: number) => {
8790
if (!session?.access_token) return []
88-
const response = await fetch(postUserFavoritesApi(), {
91+
const response = await fetch(postUserFavoritesApi(recipeId), {
8992
method: "POST",
9093
headers: { "Content-Type": "application/json", Authorization: `Bearer ${session?.access_token}` },
91-
body: JSON.stringify({ recipeId: recipeId }),
9294
})
9395
if (!response.ok) throw new Error("お気に入りの追加に失敗しました")
9496
},
@@ -113,39 +115,22 @@ export const Result = () => {
113115
})
114116

115117
const toggleFavorite = (recipeId: number) => {
116-
if (!favoriteRecipes) return
117-
if (favoriteRecipes.some((recipe) => recipe.id === recipeId)) {
118+
if (isFavorited(recipeId)) {
118119
deleteFavorite.mutate(recipeId)
119120
} else {
120121
addFavorite.mutate(recipeId)
121122
}
122123
}
123124

124-
//----------------------------------------------------------------
125-
// フリーワード検索機能
126-
//----------------------------------------------------------------
127-
const searchRecipesKeywords = useMutation({
128-
mutationFn: async () => {
129-
const response = await fetch(postSearchRecipesKeywordsApi(), {
130-
method: "POST",
131-
headers: { "Content-Type": "application/json" },
132-
body: JSON.stringify({ keywords: inputContent }),
133-
})
134-
if (!response.ok) throw new Error("レシピの取得に失敗しました")
135-
const recipes: Recipe[] = await response.json()
136-
return recipes
137-
},
138-
onSuccess: (recipes) => {
139-
queryClient.setQueryData(["recipes"], recipes)
140-
},
141-
})
142-
143-
const onClickSearchRecipesKeywords = () => {
144-
searchRecipesKeywords.mutate()
125+
const isFavorited = (recipeId: number | string) => {
126+
if (!favoriteRecipes) return false
127+
return favoriteRecipes.some((favorite) => favorite.id.toString() === recipeId.toString())
145128
}
146129

147-
const onChangeHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
148-
setInputContent(e.target.value)
130+
const onClickSearch = () => {
131+
searchInfo.ingredients = inputContent.split(" ")
132+
localStorage.setItem("ingredients", JSON.stringify(searchInfo.ingredients))
133+
refetch()
149134
}
150135

151136
if (isLoadingRecipes || isLoadingFavoriteRecipes) return <Loading />
@@ -154,8 +139,8 @@ export const Result = () => {
154139
<div className={styles.header}>
155140
<BackButton onClick={() => navigate("/questions?reset=true")} />
156141
<Searchbox
157-
onClickHandler={onClickSearchRecipesKeywords}
158-
onChange={onChangeHandler}
142+
onClickHandler={onClickSearch}
143+
onChange={(e) => setInputContent(e.target.value)}
159144
inputContent={inputContent}
160145
placeholder=""
161146
/>
@@ -171,20 +156,15 @@ export const Result = () => {
171156
<div className={styles.cards}>
172157
{recipes.map((recipe) => (
173158
<Fragment key={recipe.id}>
174-
<RecipeCard recipe={recipe} favoriteRecipes={favoriteRecipes} toggleFavorite={toggleFavorite} />
159+
<RecipeCard recipe={recipe} isFavorited={isFavorited(recipe.id)} toggleFavorite={toggleFavorite} />
175160
</Fragment>
176161
))}
177162
</div>
178163
</div>
179164
) : (
180165
<div className={styles.emptyResults}>
181166
<EmptyResults />
182-
<BorderButton
183-
onClick={() => {
184-
navigate("/questions?reset=true")
185-
}}
186-
disabled={false}
187-
>
167+
<BorderButton onClick={() => refetch()} disabled={false}>
188168
<h3>再検索する</h3>
189169
</BorderButton>
190170
</div>

frontend/src/utils/apiUtils.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,12 @@ export const postSearchRecipesApi = (): string => {
44
return `${API_BASE_URL}/api/recipes/search`
55
}
66

7-
export const postSearchRecipesKeywordsApi = (): string => {
8-
return `${API_BASE_URL}/api/recipes/search/keywords`
9-
}
10-
117
export const getUserFavoritesApi = (): string => {
128
return `${API_BASE_URL}/api/users/favorites`
139
}
1410

15-
export const postUserFavoritesApi = (): string => {
16-
return `${API_BASE_URL}/api/users/favorites`
11+
export const postUserFavoritesApi = (id: number): string => {
12+
return `${API_BASE_URL}/api/users/favorites/${id}`
1713
}
1814

1915
export const deleteUserFavoritesApi = (id: number): string => {

0 commit comments

Comments
 (0)