From 2779e85226c6532a0ac63c9ff39b9f9e1a7f27a2 Mon Sep 17 00:00:00 2001 From: "Masaharu.Kato" Date: Mon, 16 Jun 2025 10:52:04 +0900 Subject: [PATCH 01/16] =?UTF-8?q?=E6=96=99=E7=90=86=E5=90=8D=E3=82=92?= =?UTF-8?q?=E7=A9=BA=E6=AC=84=E3=81=AB=E3=81=97=E3=81=9F=E6=99=82=E3=81=AE?= =?UTF-8?q?=E4=B8=8D=E5=85=B7=E5=90=88=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/AddRecipe.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/AddRecipe.tsx b/frontend/src/pages/AddRecipe.tsx index 2618fda..e96a779 100644 --- a/frontend/src/pages/AddRecipe.tsx +++ b/frontend/src/pages/AddRecipe.tsx @@ -43,6 +43,8 @@ const AddRecipe: React.FC = () => { const navigate = useNavigate(); + // 編集時,既存情報を読み込んだかどうか + const [recipeLoaded, setRecipeLoaded] = useState(false); // 料理名,説明 const [recipeName, setRecipeName] = useState(''); const [recipeSummary, setRecipeSummary] = useState(''); @@ -60,12 +62,13 @@ const AddRecipe: React.FC = () => { const [editingItemIdx, setEditingItemIdx] = useState(0); const loadRecipe = async () => { - if (recipeId && !recipeName) { + if (recipeId && !recipeLoaded) { const recipe = await recipeApi.getById(recipeId); console.log('loaded recipe=', recipe) setRecipeName(recipe.recipeName) setRecipeSummary(recipe.summary) setItems(recipe.stuffAndAmountArray) + setRecipeLoaded(true) } } @@ -122,7 +125,7 @@ const AddRecipe: React.FC = () => { }, []); return ( - (recipeId && !recipeName) + (recipeId && !recipeLoaded) ?

読み込み中...

: From 54b9539c0f3de3cf13500d56e5c0b20b204ced70 Mon Sep 17 00:00:00 2001 From: "Masaharu.Kato" Date: Mon, 16 Jun 2025 11:17:58 +0900 Subject: [PATCH 02/16] =?UTF-8?q?=E3=83=AC=E3=82=B7=E3=83=94=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0=E6=99=82=E3=81=AE=E3=82=A8=E3=83=A9=E3=83=BC=E3=83=A1?= =?UTF-8?q?=E3=83=83=E3=82=BB=E3=83=BC=E3=82=B8=E3=82=92=E8=A1=A8=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/AddRecipe.tsx | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/frontend/src/pages/AddRecipe.tsx b/frontend/src/pages/AddRecipe.tsx index e96a779..72ca041 100644 --- a/frontend/src/pages/AddRecipe.tsx +++ b/frontend/src/pages/AddRecipe.tsx @@ -84,23 +84,28 @@ const AddRecipe: React.FC = () => { return false; } - if (!recipeId) { - // 新規追加 - const response = await recipeApi.addRecipe({ + try { + if (!recipeId) { + // 新規追加 + const response = await recipeApi.addRecipe({ + recipeName, + summary: recipeSummary, + stuffAndAmountArray: items, + }) + return response.recipeId; + } + + const response = await recipeApi.updateRecipe({ + recipeId, recipeName, summary: recipeSummary, stuffAndAmountArray: items, }) - return response.recipeId; + } catch { + alert('レシピの送信に失敗しました。既に同じ名前の料理が存在する可能性があります。'); + return false; } - const response = await recipeApi.updateRecipe({ - recipeId, - recipeName, - summary: recipeSummary, - stuffAndAmountArray: items, - }) - return recipeId; } From 34d2487df86c12738be8b1449647e3c90d00720f Mon Sep 17 00:00:00 2001 From: "akito.nishiwaki" Date: Fri, 13 Jun 2025 11:47:24 +0900 Subject: [PATCH 03/16] =?UTF-8?q?=E5=9C=A8=E5=BA=AB=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=E3=81=AE=E3=82=AB=E3=83=AC=E3=83=B3=E3=83=80=E3=83=BC=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../todoapp/service/StocksService.java | 4 ---- .../todoapp/service/ToBuysService.java | 22 +++++++++++++------ frontend/src/pages/StockPage.tsx | 12 ++++++++-- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/backend/src/main/java/com/example/todoapp/service/StocksService.java b/backend/src/main/java/com/example/todoapp/service/StocksService.java index bd2dc19..51a142f 100644 --- a/backend/src/main/java/com/example/todoapp/service/StocksService.java +++ b/backend/src/main/java/com/example/todoapp/service/StocksService.java @@ -1,6 +1,5 @@ package com.example.todoapp.service; -import com.example.todoapp.dto.StockDTO; import com.example.todoapp.dto.AddStocksDTO; import com.example.todoapp.dto.UpdateStockRequest; import com.example.todoapp.model.Stocks; @@ -57,9 +56,6 @@ public class StocksService { } else { // 材料情報を取得 Optional existstuffs = stuffsRepository.findById(stock.getStuffId()); - if (existstuffs == null) { - throw new RuntimeException("材料がありません"); - } stuffs = existstuffs.get(); } diff --git a/backend/src/main/java/com/example/todoapp/service/ToBuysService.java b/backend/src/main/java/com/example/todoapp/service/ToBuysService.java index 678aae6..4de730a 100644 --- a/backend/src/main/java/com/example/todoapp/service/ToBuysService.java +++ b/backend/src/main/java/com/example/todoapp/service/ToBuysService.java @@ -91,14 +91,22 @@ public class ToBuysService { stuff = optStuff.get(); } - ToBuys toBuys = new ToBuys(); - toBuys.setUser(user); - toBuys.setStuff(stuff); - toBuys.setAmount(toBuyDTO.getAmount()); - toBuys.setStore(toBuyDTO.getShop()); + Optional existingToBuy = toBuysRepository.findByUserAndStuff(user, stuff); - // データベースに保存 - return toBuysRepository.save(toBuys); + if (existingToBuy.isPresent()) { + // 存在する場合は数量を更新 + ToBuys existing = existingToBuy.get(); + existing.setAmount(existing.getAmount() + toBuyDTO.getAmount()); + return toBuysRepository.save(existing); + } else { + // 新しい材料を作成 + ToBuys toBuys = new ToBuys(); + toBuys.setUser(user); + toBuys.setStuff(stuff); + toBuys.setAmount(toBuyDTO.getAmount()); + toBuys.setStore(toBuyDTO.getShop()); + return toBuysRepository.save(toBuys); + } } diff --git a/frontend/src/pages/StockPage.tsx b/frontend/src/pages/StockPage.tsx index 3379f18..2aa95ba 100644 --- a/frontend/src/pages/StockPage.tsx +++ b/frontend/src/pages/StockPage.tsx @@ -30,6 +30,14 @@ import { STOCK_ERRORS } from '../constants/errorMessages'; import DatePicker, { registerLocale } from 'react-datepicker'; import { ja } from 'date-fns/locale/ja'; // date-fnsの日本語ロケールをインポート +// 日付をyyyy-MM-dd形式で返す関数 +const formatDateLocal = (date: Date) => { + const year = date.getFullYear(); + const month = (date.getMonth() + 1).toString().padStart(2, '0'); + const day = date.getDate().toString().padStart(2, '0'); + return `${year}-${month}-${day}`; +}; + // 新規在庫の初期状態 const EMPTY_STOCK: Omit & { stuffId: number | null } & { newAddition: boolean } = { stuffId: null, @@ -494,7 +502,7 @@ const StockPage: React.FC = () => { popperClassName="custom-datepicker-popper" selected={newStock.buyDate ? new Date(newStock.buyDate) : null} onChange={(date) => - setNewStock({ ...newStock, buyDate: date ? date.toISOString().split('T')[0] : '' }) + setNewStock({ ...newStock, buyDate: date ? formatDateLocal(date) : '' }) } dateFormat="yyyy/MM/dd" customInput={ @@ -521,7 +529,7 @@ const StockPage: React.FC = () => { popperClassName="custom-datepicker-popper" selected={newStock.expDate ? new Date(newStock.expDate) : null} onChange={(date) => - setNewStock({ ...newStock, expDate: date ? date.toISOString().split('T')[0] : '' }) + setNewStock({ ...newStock, expDate: date ? formatDateLocal(date) : '' }) } dateFormat="yyyy/MM/dd" customInput={ From 00b6f7f1c74adc4a28d5bc718ada8e226eb3a9dc Mon Sep 17 00:00:00 2001 From: "Masaharu.Kato" Date: Mon, 16 Jun 2025 13:04:58 +0900 Subject: [PATCH 04/16] =?UTF-8?q?=E3=83=A1=E3=83=83=E3=82=BB=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=82=B3=E3=83=B3=E3=83=9D=E3=83=BC=E3=83=8D=E3=83=B3?= =?UTF-8?q?=E3=83=88=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/MessageAlert.tsx | 29 ++++++++++++++++++++++ frontend/src/pages/AddRecipe.tsx | 31 ++++++++++++++++++++---- 2 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 frontend/src/components/MessageAlert.tsx diff --git a/frontend/src/components/MessageAlert.tsx b/frontend/src/components/MessageAlert.tsx new file mode 100644 index 0000000..7368571 --- /dev/null +++ b/frontend/src/components/MessageAlert.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { Snackbar, Alert, AlertColor } from '@mui/material'; + +interface MessageAlertProps { + open: boolean; + message: string; + severity: AlertColor; // 'error' | 'warning' | 'info' | 'success' + onClose: (event?: React.SyntheticEvent | Event, reason?: string) => void; + duration?: number; +} + +const MessageAlert: React.FC = ({ + open, + message, + severity, + onClose, + duration = 6000, +}) => { + return ( + + + {message} + + + ); +}; + +export default MessageAlert; diff --git a/frontend/src/pages/AddRecipe.tsx b/frontend/src/pages/AddRecipe.tsx index 72ca041..2486d70 100644 --- a/frontend/src/pages/AddRecipe.tsx +++ b/frontend/src/pages/AddRecipe.tsx @@ -25,7 +25,8 @@ import { Select, FormControl, InputLabel, - ListItemIcon + ListItemIcon, + AlertColor } from '@mui/material'; import { Add as AddIcon, Delete as DeleteIcon, ShoppingBasket as ShoppingBasketIcon, @@ -36,6 +37,7 @@ import { StuffAndCategoryAndAmount } from '../types/types'; import EditAmountDialog from '../components/EditAmountDialog'; import { recipeApi, toBuyApi } from '../services/api'; import { useNavigate, useParams } from 'react-router-dom'; +import MessageAlert from '../components/MessageAlert'; const AddRecipe: React.FC = () => { const { recipeId: recipeIdStr } = useParams(); @@ -43,6 +45,20 @@ const AddRecipe: React.FC = () => { const navigate = useNavigate(); + // メッセージ + const [_openMessage, _setOpenMessage] = useState(false); + const [_messageText, _setMessageText] = useState(''); + const [_messageType, _setMessageType] = useState('info'); + const _showMessage = (message: string, severity: AlertColor) => { + _setOpenMessage(true); + _setMessageText(message); + _setMessageType(severity); + } + const showErrorMessage = (message: string) => _showMessage(message, 'error'); + const showWarningMessage = (message: string) => _showMessage(message, 'warning'); + const showInfoMessage = (message: string) => _showMessage(message, 'info'); + const showSuccessMessage = (message: string) => _showMessage(message, 'success'); + // 編集時,既存情報を読み込んだかどうか const [recipeLoaded, setRecipeLoaded] = useState(false); // 料理名,説明 @@ -75,12 +91,12 @@ const AddRecipe: React.FC = () => { const handleSaveRecipe = async () => { if (!recipeName) { - alert('レシピ名が入力されていません!') + showErrorMessage('レシピ名が入力されていません!') return false; } if (!items.length) { - alert('材料が追加されていません!') + showErrorMessage('材料が追加されていません!') return false; } @@ -102,7 +118,7 @@ const AddRecipe: React.FC = () => { stuffAndAmountArray: items, }) } catch { - alert('レシピの送信に失敗しました。既に同じ名前の料理が存在する可能性があります。'); + showErrorMessage('レシピの送信に失敗しました。同じ料理名が存在する可能性があります。'); return false; } @@ -130,7 +146,10 @@ const AddRecipe: React.FC = () => { }, []); return ( - (recipeId && !recipeLoaded) + <> + _setOpenMessage(false)}> + + {(recipeId && !recipeLoaded) ?

読み込み中...

: @@ -237,6 +256,8 @@ const AddRecipe: React.FC = () => { }} /> + } + ); }; From 3a3296b0d20ad738cb5c75b4614aa260c4f7b31f Mon Sep 17 00:00:00 2001 From: "zhang.pengcheng" Date: Mon, 16 Jun 2025 13:15:22 +0900 Subject: [PATCH 05/16] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E3=81=97=E3=81=BE?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/todoapp/controller/ToBuysController.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/backend/src/main/java/com/example/todoapp/controller/ToBuysController.java b/backend/src/main/java/com/example/todoapp/controller/ToBuysController.java index 799d3f9..048c69f 100644 --- a/backend/src/main/java/com/example/todoapp/controller/ToBuysController.java +++ b/backend/src/main/java/com/example/todoapp/controller/ToBuysController.java @@ -125,9 +125,6 @@ public class ToBuysController { }) .collect(Collectors.toList()); - // Map responseBody = new HashMap<>(); - // responseBody.put("tobuy_array", responseList); - return ResponseEntity.ok(responseList); } From 777fdc72af7516414f9a5ee4a4889055d7ae93f9 Mon Sep 17 00:00:00 2001 From: "Haru.Kusano" Date: Mon, 16 Jun 2025 13:30:17 +0900 Subject: [PATCH 06/16] =?UTF-8?q?=E3=83=9C=E3=83=88=E3=83=A0=E3=83=8A?= =?UTF-8?q?=E3=83=93=E3=82=B2=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE?= =?UTF-8?q?=E4=BD=9C=E6=88=90=EF=BC=86=E3=82=B5=E3=82=A4=E3=83=89=E3=83=90?= =?UTF-8?q?=E3=83=BC=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/Layout.tsx | 102 ++++++++++------------------ frontend/src/pages/TaskListPage.tsx | 8 +-- 2 files changed, 40 insertions(+), 70 deletions(-) diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index fa90b27..10d255d 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -16,7 +16,10 @@ import { ListItemIcon, ListItemButton, Divider, - IconButton + IconButton, + BottomNavigation, + BottomNavigationAction, + Paper } from '@mui/material'; import { Menu as MenuIcon, @@ -24,6 +27,7 @@ import { Inventory as InventoryIcon, // テストページ用のアイコン Science as ScienceIcon, // 鈴木 SoupKitchen as SoupKitchenIcon, + ShoppingCart as ShoppingCartIcon } from '@mui/icons-material'; import { useNavigate, Outlet, useLocation } from 'react-router-dom'; @@ -31,6 +35,7 @@ const Layout: React.FC = () => { const navigate = useNavigate(); const location = useLocation(); const [drawerOpen, setDrawerOpen] = useState(false); + const [bottomNavi, setBottomNavi] = useState(1); /** * ログアウト処理を行うハンドラー関数 @@ -41,19 +46,9 @@ const Layout: React.FC = () => { navigate('/login'); }; - /** - * 画面遷移処理を行うハンドラー関数 - * 指定されたパスに遷移し、サイドメニューを閉じる - */ - const handleNavigate = (path: string) => { - navigate(path); - setDrawerOpen(false); - }; + - // 現在のパスに基づいてメニュー項目が選択状態かどうかを判定 - const isSelected = (path: string): boolean => { - return location.pathname === path; - }; + // メニューを開閉するハンドラー const toggleDrawer = () => { @@ -65,15 +60,6 @@ const Layout: React.FC = () => { {/* ヘッダー部分 - アプリ名とログアウトボタンを表示 */} - - - shopchop @@ -83,52 +69,36 @@ const Layout: React.FC = () => { - {/* サイドメニュー */} - setDrawerOpen(false)} - > - + { + setBottomNavi(newValue); + switch(newValue) { + case 0: + navigate('stock'); + break; + case 1: + navigate('tasks'); + break; + case 2: + navigate('recipeList'); + break; + } + // ここでルーティング処理などを行う + }} > - - handleNavigate('/tasks')} - selected={isSelected('/tasks')} - > - - - - {/* テストページへのリンクを追加 */} + } /> + } /> + } /> + + - {/* 在庫リストへのリンクを追加 */} - handleNavigate('/addRecipe')} - selected={isSelected('/addRecipe')} - > - - - - handleNavigate('/recipeList')} - selected={isSelected('/recipeList')} - > - - - - handleNavigate('/stock')} - selected={isSelected('/stock')} - > - - - - - - - {/* メインコンテンツ領域 - 子ルートのコンポーネントがここに表示される */} diff --git a/frontend/src/pages/TaskListPage.tsx b/frontend/src/pages/TaskListPage.tsx index 29082a3..7d8b59d 100644 --- a/frontend/src/pages/TaskListPage.tsx +++ b/frontend/src/pages/TaskListPage.tsx @@ -294,28 +294,28 @@ const TaskListPage: React.FC = () => { {/* 新規材料作成ボタン - 画面下部に固定表示 */} - + 材料のみ追加 setOpenAddToBuyDialog(true)} > {/*新規料理追加ボタン - 画面下部に固定表示 */} - + 料理から追加 { // setOpenAddToBuyDialog(true); navigate('/RecipeList'); From 3867bfb30628bf450a43f90fe048dc25a3c060d4 Mon Sep 17 00:00:00 2001 From: "Masaharu.Kato" Date: Mon, 16 Jun 2025 13:34:51 +0900 Subject: [PATCH 07/16] =?UTF-8?q?=E3=83=A1=E3=83=83=E3=82=BB=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=82=B3=E3=83=B3=E3=83=86=E3=82=AD=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=81=A8=E3=81=97=E3=81=A6=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/Layout.tsx | 63 +++++++++++++++++++--- frontend/src/components/MessageContext.tsx | 17 ++++++ frontend/src/pages/AddRecipe.tsx | 20 ++----- 3 files changed, 76 insertions(+), 24 deletions(-) create mode 100644 frontend/src/components/MessageContext.tsx diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index fa90b27..fc5a140 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -2,7 +2,7 @@ * アプリケーションの共通レイアウトを提供するコンポーネント * ヘッダー(AppBar)とメインコンテンツ領域を含む基本的なページ構造を定義 */ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { AppBar, Toolbar, @@ -16,7 +16,8 @@ import { ListItemIcon, ListItemButton, Divider, - IconButton + IconButton, + AlertColor } from '@mui/material'; import { Menu as MenuIcon, @@ -26,6 +27,8 @@ import { SoupKitchen as SoupKitchenIcon, } from '@mui/icons-material'; import { useNavigate, Outlet, useLocation } from 'react-router-dom'; +import { MessageContext } from './MessageContext'; +import MessageAlert from './MessageAlert'; const Layout: React.FC = () => { const navigate = useNavigate(); @@ -60,6 +63,40 @@ const Layout: React.FC = () => { setDrawerOpen(!drawerOpen); }; + // メッセージ表示 + + // ページ遷移後もメッセージを維持 + useEffect(() => { + const saved = sessionStorage.getItem('globalMessage'); + if (saved) { + const { message, severity } = JSON.parse(saved); + showMessage(message, severity); + } + }, []); + + const [msgOpen, setMsgOpen] = useState(false); + const [msgText, setMsgText] = useState(''); + const [msgType, setMsgType] = useState('info'); + + const showMessage = (msg: string, sev: AlertColor) => { + setMsgText(msg); + setMsgType(sev); + setMsgOpen(true); + sessionStorage.setItem('globalMessage', JSON.stringify({ message: msg, severity: sev })); + }; + + const showErrorMessage = (message: string) => showMessage(message, 'error'); + const showWarningMessage = (message: string) => showMessage(message, 'warning'); + const showInfoMessage = (message: string) => showMessage(message, 'info'); + const showSuccessMessage = (message: string) => showMessage(message, 'success'); + + const handleMsgClose = () => { + setMsgOpen(false); + setMsgText(''); + sessionStorage.removeItem('globalMessage'); + }; + + return ( {/* ヘッダー部分 - アプリ名とログアウトボタンを表示 */} @@ -103,16 +140,16 @@ const Layout: React.FC = () => { {/* テストページへのリンクを追加 */} - {/* 在庫リストへのリンクを追加 */} - handleNavigate('/addRecipe')} + {/* 在庫リストへのリンクを追加 */} + handleNavigate('/addRecipe')} selected={isSelected('/addRecipe')} > - handleNavigate('/recipeList')} + handleNavigate('/recipeList')} selected={isSelected('/recipeList')} > @@ -133,7 +170,17 @@ const Layout: React.FC = () => { {/* メインコンテンツ領域 - 子ルートのコンポーネントがここに表示される */} - {/* React Router の Outlet - 子ルートのコンポーネントがここにレンダリングされる */} + + + + {/* React Router の Outlet - 子ルートのコンポーネントがここにレンダリングされる */} + + diff --git a/frontend/src/components/MessageContext.tsx b/frontend/src/components/MessageContext.tsx new file mode 100644 index 0000000..599f2a5 --- /dev/null +++ b/frontend/src/components/MessageContext.tsx @@ -0,0 +1,17 @@ + +import React, { createContext, useContext } from 'react'; + +export interface MessageContextType { + showErrorMessage: (message: string) => void; + showWarningMessage: (message: string) => void; + showSuccessMessage: (message: string) => void; + showInfoMessage: (message: string) => void; +} + +export const MessageContext = createContext(undefined); + +export const useMessage = () => { + const context = useContext(MessageContext); + if (!context) throw new Error('useMessage must be used within MessageContext.Provider'); + return context; +}; diff --git a/frontend/src/pages/AddRecipe.tsx b/frontend/src/pages/AddRecipe.tsx index 2486d70..9a397dd 100644 --- a/frontend/src/pages/AddRecipe.tsx +++ b/frontend/src/pages/AddRecipe.tsx @@ -38,6 +38,7 @@ import EditAmountDialog from '../components/EditAmountDialog'; import { recipeApi, toBuyApi } from '../services/api'; import { useNavigate, useParams } from 'react-router-dom'; import MessageAlert from '../components/MessageAlert'; +import { useMessage } from '../components/MessageContext'; const AddRecipe: React.FC = () => { const { recipeId: recipeIdStr } = useParams(); @@ -45,20 +46,6 @@ const AddRecipe: React.FC = () => { const navigate = useNavigate(); - // メッセージ - const [_openMessage, _setOpenMessage] = useState(false); - const [_messageText, _setMessageText] = useState(''); - const [_messageType, _setMessageType] = useState('info'); - const _showMessage = (message: string, severity: AlertColor) => { - _setOpenMessage(true); - _setMessageText(message); - _setMessageType(severity); - } - const showErrorMessage = (message: string) => _showMessage(message, 'error'); - const showWarningMessage = (message: string) => _showMessage(message, 'warning'); - const showInfoMessage = (message: string) => _showMessage(message, 'info'); - const showSuccessMessage = (message: string) => _showMessage(message, 'success'); - // 編集時,既存情報を読み込んだかどうか const [recipeLoaded, setRecipeLoaded] = useState(false); // 料理名,説明 @@ -77,6 +64,9 @@ const AddRecipe: React.FC = () => { const [editingItem, setEditingItem] = useState(emptyItem); const [editingItemIdx, setEditingItemIdx] = useState(0); + // エラーメッセージ表示 + const { showErrorMessage } = useMessage(); + const loadRecipe = async () => { if (recipeId && !recipeLoaded) { const recipe = await recipeApi.getById(recipeId); @@ -147,8 +137,6 @@ const AddRecipe: React.FC = () => { return ( <> - _setOpenMessage(false)}> - {(recipeId && !recipeLoaded) ?

読み込み中...

: From 69a7ede672a5c1cb4786837be0a5eb06bbcbdf8b Mon Sep 17 00:00:00 2001 From: "Masaharu.Kato" Date: Mon, 16 Jun 2025 13:42:29 +0900 Subject: [PATCH 08/16] =?UTF-8?q?=E3=83=A1=E3=83=83=E3=82=BB=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=83=86=E3=82=AD=E3=82=B9=E3=83=88=E6=B6=88=E5=8E=BB?= =?UTF-8?q?=E3=81=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/Layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index fc5a140..01b985e 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -92,7 +92,7 @@ const Layout: React.FC = () => { const handleMsgClose = () => { setMsgOpen(false); - setMsgText(''); + // setMsgText(''); // ここで空にすると,メッセージが消えるアニメーションが始まる時点で文字が消えてしまう sessionStorage.removeItem('globalMessage'); }; From 81ce6c95306208b976adbd300bb34b4a90d826bc Mon Sep 17 00:00:00 2001 From: "Masaharu.Kato" Date: Mon, 16 Jun 2025 13:42:40 +0900 Subject: [PATCH 09/16] =?UTF-8?q?=E6=88=90=E5=8A=9F=E6=99=82=E3=81=AB?= =?UTF-8?q?=E3=82=82=E3=83=A1=E3=83=83=E3=82=BB=E3=83=BC=E3=82=B8=E3=82=92?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/AddRecipe.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/pages/AddRecipe.tsx b/frontend/src/pages/AddRecipe.tsx index 9a397dd..a8fccef 100644 --- a/frontend/src/pages/AddRecipe.tsx +++ b/frontend/src/pages/AddRecipe.tsx @@ -65,7 +65,7 @@ const AddRecipe: React.FC = () => { const [editingItemIdx, setEditingItemIdx] = useState(0); // エラーメッセージ表示 - const { showErrorMessage } = useMessage(); + const { showErrorMessage, showSuccessMessage } = useMessage(); const loadRecipe = async () => { if (recipeId && !recipeLoaded) { @@ -117,7 +117,7 @@ const AddRecipe: React.FC = () => { const handleSubmit = async () => { const recipeId = await handleSaveRecipe(); - // alert('レシピが保存されました!'); + showSuccessMessage('レシピが保存されました!'); if (!recipeId) return; navigate('/recipeList'); } @@ -126,7 +126,7 @@ const AddRecipe: React.FC = () => { const recipeId = await handleSaveRecipe(); if (!recipeId) return false; await toBuyApi.addByRecipe(recipeId); - // alert('レシピが保存されて買うものリストに追加されました!'); + showSuccessMessage('レシピが保存されて買うものリストに追加されました!'); navigate('/tasks'); } From 7da7bb32738ed98907d678f738af884d3340a9c0 Mon Sep 17 00:00:00 2001 From: "Masaharu.Kato" Date: Mon, 16 Jun 2025 14:02:44 +0900 Subject: [PATCH 10/16] =?UTF-8?q?=E3=83=A1=E3=83=83=E3=82=BB=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E8=A1=A8=E7=A4=BA=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/AddRecipe.tsx | 2 +- frontend/src/pages/RecipeList.tsx | 5 ++++- frontend/src/pages/StockPage.tsx | 7 +++++-- frontend/src/pages/TaskListPage.tsx | 9 +++++---- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/frontend/src/pages/AddRecipe.tsx b/frontend/src/pages/AddRecipe.tsx index a8fccef..798d39b 100644 --- a/frontend/src/pages/AddRecipe.tsx +++ b/frontend/src/pages/AddRecipe.tsx @@ -117,8 +117,8 @@ const AddRecipe: React.FC = () => { const handleSubmit = async () => { const recipeId = await handleSaveRecipe(); - showSuccessMessage('レシピが保存されました!'); if (!recipeId) return; + showSuccessMessage('レシピが保存されました!'); navigate('/recipeList'); } diff --git a/frontend/src/pages/RecipeList.tsx b/frontend/src/pages/RecipeList.tsx index 42826db..ac93704 100644 --- a/frontend/src/pages/RecipeList.tsx +++ b/frontend/src/pages/RecipeList.tsx @@ -31,11 +31,14 @@ import { SoupKitchen as SoupKitchenIcon } from '@mui/icons-material'; import { ToBuy, Stuff, RecipeWithId, RecipeDetail } from '../types/types'; +import { useMessage } from '../components/MessageContext'; const RecipeList: React.FC = () => { const navigate = useNavigate(); // 料理リストの料理名を格納する配列 + const { showErrorMessage } = useMessage(); + // すべての料理リスト const [allRecipes, setAllRecipes] = useState(); @@ -48,7 +51,7 @@ const RecipeList: React.FC = () => { const recipes = await recipeApi.getAllRecipes(); setAllRecipes(recipes); } catch (error) { - alert("レシピの取得に失敗しました."); + showErrorMessage("レシピの取得に失敗しました。"); // console.error(`${TASK_ERRORS.FETCH_FAILED}:`, error); } }; diff --git a/frontend/src/pages/StockPage.tsx b/frontend/src/pages/StockPage.tsx index 2aa95ba..7688049 100644 --- a/frontend/src/pages/StockPage.tsx +++ b/frontend/src/pages/StockPage.tsx @@ -29,6 +29,7 @@ import { import { STOCK_ERRORS } from '../constants/errorMessages'; import DatePicker, { registerLocale } from 'react-datepicker'; import { ja } from 'date-fns/locale/ja'; // date-fnsの日本語ロケールをインポート +import { useMessage } from '../components/MessageContext'; // 日付をyyyy-MM-dd形式で返す関数 const formatDateLocal = (date: Date) => { @@ -72,6 +73,8 @@ const StockPage: React.FC = () => { // 在庫の編集状態 const [editStock, setEditStock] = useState(null); + const { showWarningMessage } = useMessage(); + // コンポーネントマウント時にタスク一覧を取得 useEffect(() => { fetchStocks(); @@ -196,7 +199,7 @@ const StockPage: React.FC = () => { setEditStock({ ...selectedRow }); setIsEditOpen(true); } else { - alert("編集する食材を選択してください。"); + showWarningMessage("編集する食材を選択してください。"); } }; // 変更を適用 @@ -248,7 +251,7 @@ const StockPage: React.FC = () => { if (selectedRow) { setIsDeleteOpen(true); } else { - alert("削除する食材を選択してください。"); + showWarningMessage("削除する食材を選択してください。"); } }; /** 削除ダイアログを閉じる */ diff --git a/frontend/src/pages/TaskListPage.tsx b/frontend/src/pages/TaskListPage.tsx index 74b756d..0a20838 100644 --- a/frontend/src/pages/TaskListPage.tsx +++ b/frontend/src/pages/TaskListPage.tsx @@ -31,6 +31,7 @@ import AddStuffAmountDialog from '../components/AddStuffAmountDialog'; import BuyDialog from '../components/BuyDialog'; import { useNavigate } from 'react-router-dom'; import DatePicker from 'react-datepicker'; +import { useMessage } from '../components/MessageContext'; //import { FaCarrot } from "react-icons/fa6"; //エラー起きる いったん保留 @@ -91,7 +92,7 @@ const TaskListPage: React.FC = () => { shop: undefined, }); - + const { showErrorMessage } = useMessage(); // コンポーネントマウント時にタスク一覧を取得 useEffect(() => { @@ -149,7 +150,7 @@ const TaskListPage: React.FC = () => { const handleAddNewToBuy = async () => { try { if (isNaN(newToBuy.amount)) { - console.log('数量が正しくありません.'); + showErrorMessage('数量が正しくありません.'); return; } @@ -171,7 +172,7 @@ const TaskListPage: React.FC = () => { const handleUpdateNewToBuy = async () => { try { if (isNaN(editingItem.amount)) { - console.log('数量が正しくありません.'); + showErrorMessage('数量が正しくありません.'); return; } @@ -195,7 +196,7 @@ const TaskListPage: React.FC = () => { console.log("newPrice:", newStock.price) console.log("parsedPrice: ", parsedPrice) if (isNaN(parsedPrice)) { - alert('入力が無効です') + showErrorMessage('価格が正しく入力されていません。') return //setNewStock({ ...newStock, price: parsedPrice }); } From 12a0d38bd9d2ad8e1453ba5460d37f16cc58a14a Mon Sep 17 00:00:00 2001 From: "zhang.pengcheng" Date: Mon, 16 Jun 2025 14:08:48 +0900 Subject: [PATCH 11/16] =?UTF-8?q?/tobuy/addByRecipe=E3=81=AB=E6=96=B0?= =?UTF-8?q?=E3=81=97=E3=81=84=E5=88=86=E9=87=8F=E3=83=91=E3=83=A9=E3=83=A1?= =?UTF-8?q?=E3=83=BC=E3=82=BF=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/example/todoapp/controller/ToBuysController.java | 4 ++-- .../java/com/example/todoapp/service/ToBuysService.java | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/com/example/todoapp/controller/ToBuysController.java b/backend/src/main/java/com/example/todoapp/controller/ToBuysController.java index 048c69f..d5753e9 100644 --- a/backend/src/main/java/com/example/todoapp/controller/ToBuysController.java +++ b/backend/src/main/java/com/example/todoapp/controller/ToBuysController.java @@ -92,7 +92,6 @@ public class ToBuysController { } - /** * 指定されたユーザーIDに基づいてすべての「買うもの」リストを取得する * @@ -186,7 +185,8 @@ public class ToBuysController { Authentication authentication) { Long recipeId = payload.get("recipeId"); - List responseList = toBuysService.addByRecipeId(recipeId, authentication); + Long servings = payload.get("servings"); + List responseList = toBuysService.addByRecipeId(recipeId, servings,authentication); //shopのフィールドを削除 List> filteredList = responseList.stream() diff --git a/backend/src/main/java/com/example/todoapp/service/ToBuysService.java b/backend/src/main/java/com/example/todoapp/service/ToBuysService.java index 4de730a..fe3bfe7 100644 --- a/backend/src/main/java/com/example/todoapp/service/ToBuysService.java +++ b/backend/src/main/java/com/example/todoapp/service/ToBuysService.java @@ -220,7 +220,7 @@ public class ToBuysService { * @param authentication 認証情報 * @return 追加された「買うもの」のリスト */ - public List addByRecipeId(Long recipeId, Authentication authentication) { + public List addByRecipeId(Long recipeId, Long servings,Authentication authentication) { // ユーザー情報を取得 String username = authentication.getName(); User user = userRepository.findByUsername(username) @@ -233,7 +233,9 @@ public class ToBuysService { for (RecipeStuffs rs : recipeStuffsList) { Stuffs stuff = rs.getStuff(); - int requiredAmount = rs.getAmount(); + + // 材料の数量をサービング数に基づいて計算 + int requiredAmount = rs.getAmount() * (servings != null ? servings.intValue() : 1); Optional existingToBuyOpt = toBuysRepository.findByUserAndStuff(user, stuff); From 5fd2e33507dc37a07dc7f886ab3e3d36afbee081 Mon Sep 17 00:00:00 2001 From: Amagasu Date: Mon, 16 Jun 2025 14:44:38 +0900 Subject: [PATCH 12/16] show stuffName, when api/tobuy/buy --- frontend/src/components/BuyDialog.tsx | 15 ++++++++++++++- frontend/src/pages/TaskListPage.tsx | 3 ++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/BuyDialog.tsx b/frontend/src/components/BuyDialog.tsx index 6acfdd3..27485fe 100644 --- a/frontend/src/components/BuyDialog.tsx +++ b/frontend/src/components/BuyDialog.tsx @@ -26,12 +26,14 @@ const formatDateLocal = (date: Date) => { const BuyDialog = ({ openDialog, setOpenDialog, + stuffName, newStock, setNewStock, onSubmit, }: { openDialog: boolean, setOpenDialog: (open: boolean) => void, + stuffName: string, newStock: NewStock, setNewStock: (tobuy: NewStock) => void, onSubmit: () => void, @@ -40,11 +42,21 @@ const BuyDialog = ({ return ( - setOpenDialog(false)} disableScrollLock={true} PaperProps={{ sx: { minHeight: '500px', maxHeight: '80vh' } }} + setOpenDialog(false)} disableScrollLock={true} PaperProps={{ sx: { minHeight: '600px', maxHeight: '80vh' } }} > 在庫登録 + {/* 材料名表示 */} + + {/* 価格入力フィールド */} {/* 購入日・消費期限を横並びに */} diff --git a/frontend/src/pages/TaskListPage.tsx b/frontend/src/pages/TaskListPage.tsx index 74b756d..9eccffb 100644 --- a/frontend/src/pages/TaskListPage.tsx +++ b/frontend/src/pages/TaskListPage.tsx @@ -251,6 +251,7 @@ const TaskListPage: React.FC = () => { { setOpenInfoDialog(true) + setEditingItem(tobuy) setSelectedToBuyId(tobuy.tobuyId) // handleDeleteTask(tobuy.tobuyId) }}> @@ -330,7 +331,7 @@ const TaskListPage: React.FC = () => { {/* 購入処理(在庫登録)のための数値入力ダイアログ */} - + {/* 数量変更ダイアログ */} Date: Mon, 16 Jun 2025 15:37:14 +0900 Subject: [PATCH 13/16] =?UTF-8?q?=E6=96=99=E7=90=86=E3=83=AA=E3=82=B9?= =?UTF-8?q?=E3=83=88=EF=BC=9A=E5=89=8A=E9=99=A4=E3=83=80=E3=82=A4=E3=82=A2?= =?UTF-8?q?=E3=83=AD=E3=82=B0=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/AddRecipe.tsx | 33 ++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/AddRecipe.tsx b/frontend/src/pages/AddRecipe.tsx index 2618fda..ee570bd 100644 --- a/frontend/src/pages/AddRecipe.tsx +++ b/frontend/src/pages/AddRecipe.tsx @@ -58,6 +58,8 @@ const AddRecipe: React.FC = () => { // 編集しているアイテム const [editingItem, setEditingItem] = useState(emptyItem); const [editingItemIdx, setEditingItemIdx] = useState(0); + //削除確認ダイアログの表示状態 + const [openDeleteDialog, setOpenDeleteDialog] = useState(false); const loadRecipe = async () => { if (recipeId && !recipeName) { @@ -138,7 +140,7 @@ const AddRecipe: React.FC = () => { value={recipeSummary} onChange={(e) => setRecipeSummary(e.target.value)} /> -

+

材料リスト

{/* すべての材料情報を表示 */} @@ -179,7 +181,11 @@ const AddRecipe: React.FC = () => { tooltip: { sx: { backgroundColor: "white", color: "red", fontSize: "0.8rem", padding: "6px", borderRadius: "6px" } }, }}> setItems([...items.slice(0, index), ...items.slice(index + 1)])}> + onClick={() => { + setOpenDeleteDialog(true) + setEditingItem(item) + setEditingItemIdx(index) + }}> @@ -219,6 +225,29 @@ const AddRecipe: React.FC = () => { setOpenAddDialog(false); }} /> + {/* 削除ダイアログ */} + setOpenDeleteDialog(false)} disableScrollLock={true} + fullWidth + maxWidth="sm" + sx={{ overflow: "hidden" }} + > + 食材の削除 + + {editingItem && ( + <> + {editingItem.stuffName}を削除します。 + ⚠️ 注意: 削除すると復元できません。 + + + + )} + + + {/* 数量変更ダイアログ */} Date: Mon, 16 Jun 2025 16:41:11 +0900 Subject: [PATCH 14/16] =?UTF-8?q?=E3=82=B9=E3=83=9E=E3=83=9B=E5=8B=95?= =?UTF-8?q?=E4=BD=9C=E7=A2=BA=E8=AA=8D=E7=94=A8=E3=81=AE=E3=83=90=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=82=A8=E3=83=B3=E3=83=89=E8=A8=AD=E5=AE=9A=E3=83=95?= =?UTF-8?q?=E3=82=A1=E3=82=A4=E3=83=AB=E3=81=AE=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/resources/application-localfwd.yml | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 backend/src/main/resources/application-localfwd.yml diff --git a/backend/src/main/resources/application-localfwd.yml b/backend/src/main/resources/application-localfwd.yml new file mode 100644 index 0000000..536f15b --- /dev/null +++ b/backend/src/main/resources/application-localfwd.yml @@ -0,0 +1,20 @@ +spring: + datasource: + url: jdbc:postgresql://localhost:5432/${LOCAL_DB_NAME} + driver-class-name: org.postgresql.Driver + username: ${LOCAL_DB_USER} + password: ${LOCAL_DB_PASSWORD} + jpa: + database-platform: org.hibernate.dialect.PostgreSQLDialect + properties: + hibernate: + jdbc: + lob: + non_contextual_creation: true + +server: + address: 0.0.0.0 + port: 8080 + +cors: + allowed-origins: http://${WIN_IP}:3000 \ No newline at end of file From 6ea97add080950ef7b5dc0b3c27f76fc779f254a Mon Sep 17 00:00:00 2001 From: "Masaharu.Kato" Date: Mon, 16 Jun 2025 17:32:01 +0900 Subject: [PATCH 15/16] =?UTF-8?q?=E3=83=9C=E3=83=88=E3=83=A0=E3=83=8A?= =?UTF-8?q?=E3=83=93=E3=82=B2=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=83=90?= =?UTF-8?q?=E3=83=BC=E3=81=AB=E8=A2=AB=E3=82=89=E3=81=AA=E3=81=84=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=83=9C=E3=82=BF=E3=83=B3=E3=81=AE=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/AddRecipe.tsx | 2 +- frontend/src/pages/DishList.tsx | 7 ++++--- frontend/src/pages/RecipeList.tsx | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/frontend/src/pages/AddRecipe.tsx b/frontend/src/pages/AddRecipe.tsx index 798d39b..1000a0e 100644 --- a/frontend/src/pages/AddRecipe.tsx +++ b/frontend/src/pages/AddRecipe.tsx @@ -215,7 +215,7 @@ const AddRecipe: React.FC = () => {
-
+
-
-
-
+