Skip to content

Commit bd59eb4

Browse files
authored
Merge pull request #403 from ut-code/refactor/remove-unnecessary-api
refactor: Improve favorite api endpoints
2 parents daee158 + 14306ba commit bd59eb4

File tree

11 files changed

+74
-147
lines changed

11 files changed

+74
-147
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: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,20 @@ app.use(
5555
if (url === "/") {
5656
return
5757
}
58-
await client.logs.create({
59-
data: {
60-
requestedAt: new Date(requestedAt.replace(/\[|\]/g, "")),
61-
method,
62-
url,
63-
status: parseInt(status),
64-
logType,
65-
message: logMessage,
66-
},
67-
})
58+
try {
59+
await client.logs.create({
60+
data: {
61+
requestedAt: new Date(requestedAt.replace(/\[|\]/g, "")),
62+
method,
63+
url,
64+
status: parseInt(status),
65+
logType,
66+
message: logMessage,
67+
},
68+
})
69+
} catch (error) {
70+
console.error(error)
71+
}
6872
},
6973
},
7074
})
@@ -80,16 +84,20 @@ app.use(
8084
if (url !== "/api/recipes/search") {
8185
return
8286
}
83-
await client.logs.create({
84-
data: {
85-
requestedAt: new Date(requestedAt.replace(/\[|\]/g, "")),
86-
method,
87-
url,
88-
status: parseInt(status),
89-
logType,
90-
message: logMessage,
91-
},
92-
})
87+
try {
88+
await client.logs.create({
89+
data: {
90+
requestedAt: new Date(requestedAt.replace(/\[|\]/g, "")),
91+
method,
92+
url,
93+
status: parseInt(status),
94+
logType,
95+
message: logMessage,
96+
},
97+
})
98+
} catch (error) {
99+
console.error(error)
100+
}
93101
},
94102
},
95103
}
@@ -106,16 +114,20 @@ app.use(
106114
if (url !== "/api/recipes/search") {
107115
return
108116
}
109-
await client.logs.create({
110-
data: {
111-
requestedAt: new Date(requestedAt.replace(/\[|\]/g, "")),
112-
method,
113-
url,
114-
status: parseInt(status),
115-
logType,
116-
message: logMessage,
117-
},
118-
})
117+
try {
118+
await client.logs.create({
119+
data: {
120+
requestedAt: new Date(requestedAt.replace(/\[|\]/g, "")),
121+
method,
122+
url,
123+
status: parseInt(status),
124+
logType,
125+
message: logMessage,
126+
},
127+
})
128+
} catch (error) {
129+
console.error(error)
130+
}
119131
},
120132
},
121133
}
@@ -157,7 +169,7 @@ app.post("/api/recipes/search", SearchController.searchRecipes)
157169
app.post("/api/recipes/search/some", SearchController.searchSomeRecipes)
158170

159171
app.get("/api/users/favorites", UserController.getFavorites)
160-
app.post("/api/users/favorites", UserController.addFavorite)
172+
app.post("/api/users/favorites/:id", UserController.addFavorite)
161173
app.delete("/api/users/favorites/:id", UserController.deleteFavorite)
162174
app.get("/api/users", UserController.getUser)
163175

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/NewRecipe/NewRecipe.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ export const NewRecipe = () => {
165165
foodImageUrl: foodImageUrl,
166166
dish: dish,
167167
}}
168-
favoriteRecipes={[]} // Replace with the actual favoriteRecipes value
168+
isFavorited={false} // Replace with the actual favoriteRecipes value
169169
toggleFavorite={() => {}} // Replace with the actual toggleFavorite value
170170
/>
171171
</div>

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>

0 commit comments

Comments
 (0)