/** * テストページコンポーネント * 白紙の状態で表示されるテスト用のページ */ import React, { useState, useEffect } from 'react'; import { stockApi, stuffApi } from '../services/api'; import { Stock, Stuff } from '../types/types'; import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper } from "@mui/material"; import { Container, Typography, Tooltip, Fab, Dialog, DialogTitle, DialogContent, DialogActions, TextField, Button, Box, Checkbox, FormControlLabel, FormGroup, FormControl, InputLabel, Select, MenuItem, } from '@mui/material'; import { STOCK_ERRORS } from '../constants/errorMessages'; import DatePicker, { registerLocale } from 'react-datepicker'; import { ja } from 'date-fns/locale/ja'; // date-fnsの日本語ロケールをインポート // 新規在庫の初期状態 const EMPTY_STOCK: Omit & { stuffId: number | null } & { newAddition: boolean } = { stuffId: null, stuffName: '', amount: 1, price: 0, lastUpdate: '', buyDate: new Date().toISOString(), expDate: '', category: '', newAddition: false, // 材料を新規作成するか否か // shop '', } // 日本語ロケールを登録 registerLocale('ja', ja); const StockPage: React.FC = () => { const [stocks, setStocks] = useState([]); // セル選択の表示状態 const [selectedRow, setSelectedRow] = useState(null); // 追加ダイアログボックスの表示状態 const [isAddOpen, setIsAddOpen] = useState(false); // 在庫追加に使う状態 const [newStock, setNewStock] = useState(EMPTY_STOCK); const [stuffs, setStuffs] = useState([]); // 編集ダイアロボックスの表示状態 const [isEditOpen, setIsEditOpen] = useState(false); // 削除メッセージダイアログの表示状態 const [isDeleteOpen, setIsDeleteOpen] = useState(false); // 在庫の編集状態 const [editStock, setEditStock] = useState(null); // コンポーネントマウント時にタスク一覧を取得 useEffect(() => { fetchStocks(); }, []); /** * APIから在庫一覧を取得する関数 * 取得したタスクをstate(tasks)に設定 */ const fetchStocks = async () => { try { const stocks = await stockApi.getStocks(); console.log('Stocks=', stocks) setStocks(stocks); } catch (error) { console.error(`${STOCK_ERRORS.FETCH_FAILED}:`, error); } }; /** * 在庫リストに新規食材を作成するハンドラー */ const handleCreateStock = async () => { try { if (newStock.newAddition) { newStock.stuffId = null; } if (isNaN(newStock.amount)) return; if (isNaN(newStock.price)) return; console.log(newStock) const today = new Date().toISOString().substring(0, 10); const updatedStock = { ...newStock, lastUpdate: today }; // lastUpdate に today を設定 console.log("送信するデータ:", updatedStock); // 送信前のデータを確認 await stockApi.addStock(updatedStock); // 修正したオブジェクトを API に送信 // await stockApi.addStock(newStock); setIsAddOpen(false); // ダイアログを閉じる setNewStock(EMPTY_STOCK); // 入力内容をリセット fetchStocks(); // 作成後のタスク一覧を再取得 } catch (error) { console.error(`${STOCK_ERRORS.CREATE_FAILED}:`, error); } }; /** * 在庫リストを編集するハンドラー */ const handleUpdateStock = async (stockId: number, amount: number, price: number, buyDate: string, expDate: string) => { try { const today = new Date().toISOString().substring(0, 10); await stockApi.updateStock({ stockId, amount, price, lastUpdate: today, buyDate, expDate }); fetchStocks(); // 削除後の買うもの一覧を再取得 } catch (error) { console.error(`${STOCK_ERRORS.UPDATE_FAILED}:`, error); } }; /** * 在庫を削除するハンドラー * 指定されたIDのタスクをAPIを通じて削除 */ const handleDeleteStock = async (stockId: number) => { try { await stockApi.deleteStock(stockId); fetchStocks(); // 削除後の買うもの一覧を再取得 } catch (error) { console.error(`${STOCK_ERRORS.DELETE_FAILED}:`, error); } }; /** * カテゴリー選択?? */ const onChangeCategory = async (category: string) => { setNewStock({ ...newStock, category }) const result = await stuffApi.getStuffs(category) setStuffs(result) } /** * 文字列(ISO 8601形式)をyyyy/MM/ddに変換する関数 */ const formatDate = (isoString: string): string => { const date = new Date(isoString); const year = date.getFullYear(); const month = (date.getMonth() + 1).toString().padStart(2, "0"); // 月は0始まりなので+1 const day = date.getDate().toString().padStart(2, "0"); return `${year}/${month}/${day}`; }; /* Date型をyyyy/MM/ddに変換する関数 const formatDate = (date: Date): string => { const year = date.getFullYear(); const month = (date.getMonth() + 1).toString().padStart(2, "0"); // 月は0始まりなので+1 const day = date.getDate().toString().padStart(2, "0"); return `${year}/${month}/${day}`; }; */ /** 追加ボタンを押したときにダイアログを開く */ const handleOpenAdd = () => { setIsAddOpen(true); }; /** 削除ダイアログを閉じる */ const handleCloseAdd = () => { setIsAddOpen(false); }; /** * セルを選択する関数. 再度クリックで選択解除 */ const handleRowClick = (stock: Stock) => { setSelectedRow(prev => (prev?.stockId === stock.stockId ? null : stock)); }; /** 編集ボタンを押したときにダイアログを開く */ // ダイアログを開く際に `selectedRow` の値を `editStock` にセット const handleOpenEdit = () => { if (selectedRow) { setEditStock({ ...selectedRow }); setIsEditOpen(true); } else { alert("編集する食材を選択してください。"); } }; // 変更を適用 const handleApplyChanges = async () => { if (editStock) { try { await handleUpdateStock( editStock.stockId, Number(editStock.amount), Number(editStock.price), editStock.buyDate, editStock.expDate ); setSelectedRow(editStock); // `selectedRow` を更新して変更を即時反映 fetchStocks(); // 最新データを取得してテーブルに反映 setSelectedRow(null); // セルの選択を解除 } catch (error) { console.error(`${STOCK_ERRORS.UPDATE_FAILED}:`, error); } } setIsEditOpen(false); // ダイアログを閉じる }; // ダイアログを開く際に `selectedRow` の値を `editStock` にコピー useEffect(() => { if (selectedRow) { setEditStock({ ...selectedRow }); } }, [selectedRow]); // `selectedRow` が変更されたら `editStock` に反映 // テキストフィールドの変更を検知 const handleChange = (event: React.ChangeEvent) => { if (editStock) { setEditStock({ ...editStock, [event.target.name]: event.target.value, }); } }; /** 編集ダイアログを閉じる */ const handleCloseEdit = () => { setIsEditOpen(false); }; /** 削除ボタンを押したときにダイアログを開く */ const handleOpenDelete = () => { if (selectedRow) { setIsDeleteOpen(true); } else { alert("削除する食材を選択してください。"); } }; /** 削除ダイアログを閉じる */ const handleCloseDelete = () => { setIsDeleteOpen(false); }; /** テーブルを表示する関数 */ const StockTable = (stocks: Stock[], categories: string[]) => { const filteredStocks = stocks.filter(stock => categories.includes(stock.category)); if (filteredStocks.length === 0) return null; return ( <> 食材名 数量 購入価格 購入日 消費・賞味期限 {filteredStocks.map(stock => { const today = new Date(); const expDate = new Date(stock.expDate); const timeDiff = expDate.getTime() - today.getTime(); const daysLeft = Math.ceil(timeDiff / (1000 * 60 * 60 * 24)); return ( handleRowClick(stock)} style={{ backgroundColor: selectedRow?.stockId === stock.stockId ? "yellow" : "white", cursor: "pointer" }} > {stock.stuffName} {stock.amount} {stock.price} {formatDate(stock.buyDate)} {formatDate(stock.expDate)} ); })}
{/* 編集ダイアログ */} 在庫の編集 {editStock && ( <> {editStock.stuffName} )} {/* 削除ダイアログ */} 食材の削除 {selectedRow && ( <> 【{selectedRow.stuffName}】を削除します。 ⚠️ 注意: 削除すると復元できません。 )} ); }; return ( 在庫一覧 {/* 在庫の食材追加ボタン */} {/* 新規タスク作成ダイアログ */} setIsAddOpen(false)} disableScrollLock={true}> 在庫に食材を追加 } label="食材を新規追加" checked={newStock.newAddition} onChange={(e) => setNewStock({ ...newStock, newAddition: (e.target as HTMLInputElement).checked })} /> {/*材料カテゴリ選択 */} カテゴリ {!newStock.newAddition && 材料名(選択) } {/* タスクタイトル入力フィールド */} {newStock.newAddition && setNewStock({ ...newStock, stuffName: e.target.value })} sx={{ marginBottom: 2 }} />} {/* 数量入力フィールド */} { const value = e.target.value; const parsedValue = parseInt(value, 10); // 数値に変換 if (isNaN(parsedValue) || parsedValue >= 1) { // 入力欄をいったん空欄にできるようにする,ただし空欄でない場合は1以上のみOK setNewStock({ ...newStock, amount: parsedValue }); // number型で保存 } }} // sx={{ width: "50%" }} type="number" inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} // ここで整数のみ許可 /> {/* 購入価格入力フィールド */} { const value = e.target.value; const parsedValue = parseInt(value, 10); // 数値に変換 if (isNaN(parsedValue) || parsedValue >= 0) { // 入力欄をいったん空欄にできるようにする,ただし空欄でない場合は0以上のみOK setNewStock({ ...newStock, price: parsedValue }); // number型で保存 } }} // sx={{ width: "50%" }} type="number" inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} // ここで整数のみ許可 /> {/* 購入日・消費期限を横並びに */} {/* 購入日入力フィールド */} setNewStock({ ...newStock, buyDate: date ? date.toISOString().split('T')[0] : '' }) } dateFormat="yyyy/MM/dd" customInput={ } isClearable //withPortal // ← 他の文字との重なり対策 /> {/* setNewStock({ ...newStock, buyDate: e.target.value })} /> */} {/* 消費・賞味期限入力フィールド */} setNewStock({ ...newStock, expDate: date ? date.toISOString().split('T')[0] : '' }) } dateFormat="yyyy/MM/dd" customInput={ } isClearable //withPortal /> {/* 在庫の食材編集ボタン(全テーブル共通) */} {/* 在庫の食材削除ボタン (全テーブル共通) */} {/* 在庫一覧リスト */} {/* 乳製品 */} 乳製品
{StockTable(stocks, ["乳製品"])}
{/* 肉・魚 */} 魚・肉
{StockTable(stocks, ["魚・肉"])}
{/* 野菜 */} 野菜
{StockTable(stocks, ["野菜"])}
{/* 調味料 */} 調味料
{StockTable(stocks, ["調味料"])}
{/* その他 */} その他
{StockTable(stocks, ["その他"])}
); }; export default StockPage;