Merge remote-tracking branch 'origin/develop-frontend' into feature-frontend-dishedit-kato

feature-frontend-dishedit-kato
Masaharu.Kato 4 months ago
commit ff7723d97a
  1. 3
      backend/src/main/java/com/example/todoapp/controller/StocksController.java
  2. 51
      backend/src/main/java/com/example/todoapp/dto/AddStocksDTO.java
  3. 37
      backend/src/main/java/com/example/todoapp/service/StocksService.java
  4. 1605
      frontend/package-lock.json
  5. 2
      frontend/package.json
  6. 6
      frontend/src/App.tsx
  7. 5
      frontend/src/index.css
  8. 239
      frontend/src/pages/StockPage.tsx
  9. 128
      frontend/src/pages/TaskListPage.tsx
  10. 34
      frontend/src/services/api.ts

@ -1,5 +1,6 @@
package com.example.todoapp.controller; package com.example.todoapp.controller;
import com.example.todoapp.dto.AddStocksDTO;
import com.example.todoapp.dto.DeleteStockRequestDTO; import com.example.todoapp.dto.DeleteStockRequestDTO;
import com.example.todoapp.dto.StockResponseDTO; import com.example.todoapp.dto.StockResponseDTO;
import com.example.todoapp.dto.StockDTO; import com.example.todoapp.dto.StockDTO;
@ -79,7 +80,7 @@ public class StocksController {
@PostMapping("/add") @PostMapping("/add")
public ResponseEntity<StockDTO> createStock( public ResponseEntity<StockDTO> createStock(
Authentication authentication, Authentication authentication,
@Valid @RequestBody Stocks stock) { @Valid @RequestBody AddStocksDTO stock) {
Stocks createdStock = stockService.createStock(authentication.getName(), stock); Stocks createdStock = stockService.createStock(authentication.getName(), stock);
return ResponseEntity.ok(StockDTO.fromEntity(createdStock)); return ResponseEntity.ok(StockDTO.fromEntity(createdStock));
} }

@ -0,0 +1,51 @@
//--------------------------------
// AddStocksDTO.java
//
//
// 更新履歴:2025/06/12 新規作成
// Copyright(c) 2025 IVIS All rights reserved.
//--------------------------------------------
package com.example.todoapp.dto;
import com.example.todoapp.model.Stocks;
import lombok.Data;
import java.time.LocalDate;
/**
* 在庫のデータ転送オブジェクトDTO
* <p>
* このクラスはクライアントとサーバー間で在庫情報をやり取りするために使用されます
* エンティティとは異なり必要な情報のみを含み関連エンティティへの参照ではなくIDのみを保持します
* </p>
*/
@Data
public class AddStocksDTO {
private Long stuffId;
private String stuffName;
private int amount;
private int price;
private String category;
private LocalDate buyDate;
private LocalDate lastUpdate;
private LocalDate expDate;
/**
* 在庫エンティティからDTOを作成する
*
* @param stock 変換元の在庫エンティティ
* @return 変換されたAddStocksDTOオブジェクト
*/
public static AddStocksDTO fromEntity(Stocks stock) {
AddStocksDTO dto = new AddStocksDTO();
dto.setStuffId(stock.getStuff().getStuffId());
dto.setStuffName(stock.getStuff().getStuffName());
dto.setCategory(stock.getStuff().getCategory());
dto.setAmount(stock.getAmount());
dto.setPrice(stock.getPrice());
dto.setBuyDate(stock.getBuyDate());
dto.setLastUpdate(stock.getLastUpdate());
dto.setExpDate(stock.getExpDate());
return dto;
}
}

@ -1,16 +1,21 @@
package com.example.todoapp.service; 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.dto.UpdateStockRequest;
import com.example.todoapp.model.Stocks; import com.example.todoapp.model.Stocks;
import com.example.todoapp.model.Stuffs;
import com.example.todoapp.util.MessageUtils; import com.example.todoapp.util.MessageUtils;
import com.example.todoapp.model.User; import com.example.todoapp.model.User;
import com.example.todoapp.repository.StocksRepository; import com.example.todoapp.repository.StocksRepository;
import com.example.todoapp.repository.StuffsRepository;
import com.example.todoapp.repository.UserRepository; import com.example.todoapp.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;
import java.util.Optional;
/** /**
* Stocksテーブルのサービスクラス * Stocksテーブルのサービスクラス
@ -23,6 +28,9 @@ public class StocksService {
@Autowired @Autowired
private StocksRepository stocksRepository; private StocksRepository stocksRepository;
@Autowired
private StuffsRepository stuffsRepository;
@Autowired @Autowired
private UserRepository userRepository; private UserRepository userRepository;
@ -36,10 +44,33 @@ public class StocksService {
* @param stock 追加する在庫情報 * @param stock 追加する在庫情報
* @return 保存された在庫エンティティ * @return 保存された在庫エンティティ
*/ */
public Stocks createStock(String username, Stocks stock) { public Stocks createStock(String username, AddStocksDTO stock) {
User user = getUserByUsername(username); User user = getUserByUsername(username);
stock.setUser(user); Stocks stockEntity = new Stocks();
return stocksRepository.save(stock); Stuffs stuffs;
if (stock.getStuffId() == null) {
// 新しい材料を作成
Stuffs newStuff = new Stuffs();
newStuff.setStuffName(stock.getStuffName());
newStuff.setCategory(stock.getCategory());
stuffs = stuffsRepository.save(newStuff);
} else {
// 材料情報を取得
Optional<Stuffs> existstuffs = stuffsRepository.findById(stock.getStuffId());
if (existstuffs == null) {
throw new RuntimeException("材料がありません");
}
stuffs = existstuffs.get();
}
stockEntity.setStuff(stuffs);
stockEntity.setUser(user);
stockEntity.setAmount(stock.getAmount());
stockEntity.setPrice(stock.getPrice());
stockEntity.setBuyDate(stock.getBuyDate());
stockEntity.setLastUpdate(stock.getLastUpdate());
stockEntity.setExpDate(stock.getExpDate());
return stocksRepository.save(stockEntity);
} }

File diff suppressed because it is too large Load Diff

@ -19,7 +19,9 @@
"@types/node": "^16.18.68", "@types/node": "^16.18.68",
"@types/react": "^18.2.45", "@types/react": "^18.2.45",
"@types/react-dom": "^18.2.18", "@types/react-dom": "^18.2.18",
"date-fns": "^4.1.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-datepicker": "^8.4.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-router-dom": "^6.21.1", "react-router-dom": "^6.21.1",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",

@ -18,6 +18,9 @@ import RecipeList from './pages/RecipeList';
import FullCalendar from '@fullcalendar/react'; import FullCalendar from '@fullcalendar/react';
import dayGridPlugin from "@fullcalendar/daygrid"; import dayGridPlugin from "@fullcalendar/daygrid";
// 使うカレンダー
import 'react-datepicker/dist/react-datepicker.css';
/** /**
* Material UIテーマを定義 * Material UIテーマを定義
* *
@ -58,6 +61,7 @@ const theme = createTheme({
}, },
}); });
/*//
const Calendar = () => { const Calendar = () => {
return ( return (
<FullCalendar <FullCalendar
@ -65,7 +69,7 @@ const Calendar = () => {
initialView="dayGridMonth" initialView="dayGridMonth"
/> />
); );
} }*/
/** /**
* *

@ -33,3 +33,8 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace; monospace;
} }
/* DatePickerのカレンダーがダイアログより上に表示されるように */
.custom-datepicker-popper {
z-index: 1500 !important;
}

@ -3,8 +3,8 @@
* *
*/ */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { stockApi } from '../services/api'; import { stockApi, stuffApi } from '../services/api';
import { Stock } from '../types/types'; import { Stock, Stuff } from '../types/types';
import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper } from "@mui/material"; import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper } from "@mui/material";
import { import {
Container, Container,
@ -18,15 +18,40 @@ import {
TextField, TextField,
Button, Button,
Box, Box,
Checkbox,
FormControlLabel,
FormGroup,
FormControl,
InputLabel,
Select,
MenuItem,
} from '@mui/material'; } from '@mui/material';
import { STOCK_ERRORS } from '../constants/errorMessages'; import { STOCK_ERRORS } from '../constants/errorMessages';
// 新規在庫の初期状態
const EMPTY_STOCK: Omit<Stock, 'stockId' | 'stuffId'> & { stuffId: number | null } & { newAddition: boolean } = {
stuffId: null,
stuffName: '',
amount: 0,
price: 0,
lastUpdate: '',
buyDate: '',
expDate: '',
category: '',
newAddition: false, // 材料を新規作成するか否か
// shop '',
}
const StockPage: React.FC = () => { const StockPage: React.FC = () => {
const [stocks, setStocks] = useState<Stock[]>([]); const [stocks, setStocks] = useState<Stock[]>([]);
// セル選択の表示状態 // セル選択の表示状態
const [selectedRow, setSelectedRow] = useState<Stock | null>(null); const [selectedRow, setSelectedRow] = useState<Stock | null>(null);
// 追加ダイアログボックスの表示状態
const [isAddOpen, setIsAddOpen] = useState(false);
// 在庫追加に使う状態
const [newStock, setNewStock] = useState(EMPTY_STOCK);
const [stuffs, setStuffs] = useState<Stuff[]>([]);
// 編集ダイアロボックスの表示状態 // 編集ダイアロボックスの表示状態
const [isEditOpen, setIsEditOpen] = useState(false); const [isEditOpen, setIsEditOpen] = useState(false);
// 削除メッセージダイアログの表示状態 // 削除メッセージダイアログの表示状態
@ -53,6 +78,31 @@ const StockPage: React.FC = () => {
} }
}; };
/**
*
*/
const handleCreateStock = async () => {
try {
if (newStock.newAddition) {
newStock.stuffId = null;
}
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);
}
};
/** /**
* *
*/ */
@ -79,6 +129,15 @@ const StockPage: React.FC = () => {
} }
}; };
/**
*
*/
const onChangeCategory = async (category: string) => {
setNewStock({ ...newStock, category })
const result = await stuffApi.getStuffs(category)
setStuffs(result)
}
/** /**
* (ISO 8601)yyyy/MM/ddに変換する関数 * (ISO 8601)yyyy/MM/ddに変換する関数
*/ */
@ -98,6 +157,15 @@ const StockPage: React.FC = () => {
}; };
*/ */
/** 追加ボタンを押したときにダイアログを開く */
const handleOpenAdd = () => {
setIsAddOpen(true);
};
/** 削除ダイアログを閉じる */
const handleCloseAdd = () => {
setIsAddOpen(false);
};
/** /**
* . * .
*/ */
@ -187,12 +255,18 @@ const StockPage: React.FC = () => {
<TableCell></TableCell> <TableCell></TableCell>
<TableCell></TableCell> <TableCell></TableCell>
<TableCell></TableCell> <TableCell></TableCell>
<TableCell></TableCell>
<TableCell></TableCell> <TableCell></TableCell>
<TableCell></TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
{filteredStocks.map(stock => ( {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 (
<TableRow <TableRow
key={stock.stockId} key={stock.stockId}
onClick={() => handleRowClick(stock)} onClick={() => handleRowClick(stock)}
@ -201,14 +275,20 @@ const StockPage: React.FC = () => {
<TableCell>{stock.stuffName}</TableCell> <TableCell>{stock.stuffName}</TableCell>
<TableCell>{stock.amount}</TableCell> <TableCell>{stock.amount}</TableCell>
<TableCell>{stock.price}</TableCell> <TableCell>{stock.price}</TableCell>
<TableCell>{formatDate(stock.expDate)}</TableCell>
<TableCell>{formatDate(stock.buyDate)}</TableCell> <TableCell>{formatDate(stock.buyDate)}</TableCell>
<TableCell
style={daysLeft <= 3 ? { color: "red", fontWeight: "bold" } : {}}
>
{formatDate(stock.expDate)}
</TableCell>
</TableRow> </TableRow>
))} );
})}
</TableBody> </TableBody>
</Table> </Table>
</TableContainer> </TableContainer>
{/* 編集ダイアログ */} {/* 編集ダイアログ */}
<Dialog open={isEditOpen} onClose={handleCloseEdit} fullWidth maxWidth="sm"> <Dialog open={isEditOpen} onClose={handleCloseEdit} fullWidth maxWidth="sm">
<DialogTitle></DialogTitle> <DialogTitle></DialogTitle>
@ -235,19 +315,19 @@ const StockPage: React.FC = () => {
onChange={handleChange} onChange={handleChange}
/> />
<TextField <TextField
label="賞味・消費期限 (yyyy-MM-dd)" label="購入日 (yyyy-MM-dd)"
fullWidth fullWidth
margin="normal" margin="normal"
name="expDate" name="buyDate"
value={editStock.expDate} value={editStock.buyDate}
onChange={handleChange} onChange={handleChange}
/> />
<TextField <TextField
label="購入日 (yyyy-MM-dd)" label="消費・賞味期限 (yyyy-MM-dd)"
fullWidth fullWidth
margin="normal" margin="normal"
name="buyDate" name="expDate"
value={editStock.buyDate} value={editStock.expDate}
onChange={handleChange} onChange={handleChange}
/> />
@ -277,7 +357,7 @@ const StockPage: React.FC = () => {
<DialogContent> <DialogContent>
{selectedRow && ( {selectedRow && (
<> <>
<Typography variant="h4">{selectedRow.stuffName}</Typography> <Typography variant="h4">{selectedRow.stuffName}</Typography>
<Typography variant="body1" color="error"> 注意: 削除すると復元できません</Typography> <Typography variant="body1" color="error"> 注意: 削除すると復元できません</Typography>
<Button onClick={() => { setIsDeleteOpen(false); setSelectedRow(null); }} sx={{ mt: 3, mb: 2, left: '70%' }}></Button> <Button onClick={() => { setIsDeleteOpen(false); setSelectedRow(null); }} sx={{ mt: 3, mb: 2, left: '70%' }}></Button>
<Button variant="contained" color="error" onClick={() => { <Button variant="contained" color="error" onClick={() => {
@ -290,8 +370,6 @@ const StockPage: React.FC = () => {
)} )}
</DialogContent> </DialogContent>
</Dialog> </Dialog>
</> </>
); );
}; };
@ -303,15 +381,138 @@ const StockPage: React.FC = () => {
</Typography> </Typography>
{/* タスク編集ボタン(全テーブル共通) */} {/* 在庫の食材追加ボタン */}
<Button variant="contained" color="primary" onClick={handleOpenAdd} sx={{ mt: 3, mb: 2, left: '78%' }}>
</Button>
{/* 新規タスク作成ダイアログ */}
<Dialog open={isAddOpen} onClose={() => setIsAddOpen(false)} disableScrollLock={true}>
<Box display="flex" alignItems="center" >
<DialogTitle sx={{ flexGrow: 1 }}></DialogTitle>
<FormGroup row>
<FormControlLabel
control={<Checkbox />}
label="食材を新規追加"
checked={newStock.newAddition}
onChange={(e) => setNewStock({ ...newStock, newAddition: (e.target as HTMLInputElement).checked })}
/>
</FormGroup>
</Box>
<DialogContent>
<Box sx={{ pt: 1 }}>
{/*材料カテゴリ選択 */}
<FormControl sx={{ width: "50%", marginBottom: 2 }}>
<InputLabel id="demo-simple-select-label"></InputLabel>
<Select
labelId="demo-simple-select-label"
value={newStock.category}
onChange={(e) => onChangeCategory(e.target.value)}
>
<MenuItem value="乳製品"></MenuItem>
<MenuItem value="魚・肉"></MenuItem>
<MenuItem value="野菜"></MenuItem>
<MenuItem value="調味料">調</MenuItem>
<MenuItem value="その他"></MenuItem>
</Select>
</FormControl>
{!newStock.newAddition && <FormControl sx={{ width: "100%", marginBottom: 2 }}>
<InputLabel id="demo-simple-select-label"></InputLabel>
<Select
labelId="demo-simple-select-label"
value={newStock.stuffId}
onChange={(e) => setNewStock({ ...newStock, stuffId: Number(e.target.value) })}
>
{stuffs.map((stuff) => (
<MenuItem key={stuff.stuffId} value={stuff.stuffId}>
{stuff.stuffName}
</MenuItem>
))}
</Select>
</FormControl>}
{/* タスクタイトル入力フィールド */}
{newStock.newAddition && <TextField
autoFocus
margin="dense"
label="材料名"
fullWidth
value={newStock.stuffName}
onChange={(e) => setNewStock({ ...newStock, stuffName: e.target.value })}
sx={{ marginBottom: 2 }}
/>}
{/* 数量入力フィールド */}
<TextField
margin="dense"
label="数量"
fullWidth
value={newStock.amount}
onChange={(e) => {
const value = e.target.value;
const parsedValue = parseInt(value, 10); // 数値に変換
if (!isNaN(parsedValue)) {
setNewStock({ ...newStock, amount: parsedValue }); // number型で保存
}
}}
// sx={{ width: "50%" }}
type="number"
inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} // ここで整数のみ許可
/>
{/* 購入価格入力フィールド */}
<TextField
margin="dense"
label="購入価格"
fullWidth
value={newStock.price}
onChange={(e) => {
const value = e.target.value;
const parsedValue = parseInt(value, 10); // 数値に変換
if (!isNaN(parsedValue)) {
setNewStock({ ...newStock, price: parsedValue }); // number型で保存
}
}}
// sx={{ width: "50%" }}
type="number"
inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} // ここで整数のみ許可
/>
{/* 購入日入力フィールド */}
<TextField
margin="dense"
label="購入日(yyyy/MM/dd)"
fullWidth
value={newStock.buyDate}
onChange={(e) => setNewStock({ ...newStock, buyDate: e.target.value })}
/>
{/* 賞味・消費期限入力フィールド */}
<TextField
margin="dense"
label="消費・賞味期限(yyyy/MM/dd)"
fullWidth
value={newStock.expDate}
onChange={(e) => setNewStock({ ...newStock, expDate: e.target.value })}
/>
</Box>
</DialogContent>
<DialogActions>
<Button onClick={() => setIsAddOpen(false)}></Button>
<Button onClick={handleCreateStock} variant="contained">
</Button>
</DialogActions>
</Dialog>
{/* 在庫の食材編集ボタン(全テーブル共通) */}
<Button variant="contained" color="success" onClick={handleOpenEdit} sx={{ mt: 3, mb: 2, left: '80%' }}> <Button variant="contained" color="success" onClick={handleOpenEdit} sx={{ mt: 3, mb: 2, left: '80%' }}>
</Button> </Button>
{/* タスク削除ボタン */} {/* 在庫の食材削除ボタン (全テーブル共通) */}
<Button variant="contained" color="error" onClick={handleOpenDelete} sx={{ mt: 3, mb: 2, left: '82%' }}></Button> <Button variant="contained" color="error" onClick={handleOpenDelete} sx={{ mt: 3, mb: 2, left: '82%' }}></Button>
{/* 在庫一覧リスト */} {/* 在庫一覧リスト */}
{/* 乳製品 */} {/* 乳製品 */}
<Typography variant="h4" component="h1" gutterBottom></Typography> <Typography variant="h4" component="h1" gutterBottom></Typography>

@ -15,6 +15,10 @@ import {
IconButton, IconButton,
Fab, Fab,
Box, Box,
Dialog,
DialogTitle,
DialogContent,
Button,
} from '@mui/material'; } from '@mui/material';
import { import {
Add as AddIcon, Delete as DeleteIcon, ShoppingBasket as ShoppingBasketIcon, Add as AddIcon, Delete as DeleteIcon, ShoppingBasket as ShoppingBasketIcon,
@ -26,6 +30,8 @@ import EditAmountDialog from '../components/EditAmountDialog';
import AddStuffAmountDialog from '../components/AddStuffAmountDialog'; import AddStuffAmountDialog from '../components/AddStuffAmountDialog';
import BuyDialog from '../components/BuyDialog'; import BuyDialog from '../components/BuyDialog';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import DatePicker from 'react-datepicker';
//import { FaCarrot } from "react-icons/fa6"; //エラー起きる いったん保留 //import { FaCarrot } from "react-icons/fa6"; //エラー起きる いったん保留
@ -63,6 +69,11 @@ const TaskListPage: React.FC = () => {
expDate: '', expDate: '',
}); });
//削除確認ダイアログの表示状態
const [openDeleteDialog, setOpenDeleteDialog] = useState(false);
const [selectedTask, setSelectedTask] = useState<ToBuy["tobuyId"]>(0);
const [editingItem, setEditingItem] = useState<ToBuy>({ const [editingItem, setEditingItem] = useState<ToBuy>({
tobuyId: 0, tobuyId: 0,
stuffId: 0, stuffId: 0,
@ -72,6 +83,13 @@ const TaskListPage: React.FC = () => {
}); });
const [newToBuy, setNewToBuy] = useState<StuffAndCategoryAndAmount>(EMPTY_TOBUY); const [newToBuy, setNewToBuy] = useState<StuffAndCategoryAndAmount>(EMPTY_TOBUY);
const [selectedDeleteTask, setSelectedDeleteTask] = useState<ToBuy>({
tobuyId: 0,
stuffId: 0,
stuffName: "",
amount: 0,
shop: undefined,
});
@ -123,28 +141,6 @@ const TaskListPage: React.FC = () => {
} }
}; };
/**
*
*/
const handleBuy = async () => {
try {
const today = new Date().toISOString().substring(0, 10);
const parsedPrice = parseInt(newStock.price, 10);
console.log("newPrice:", newStock.price)
console.log("parsedPrice: ", parsedPrice)
if (isNaN(parsedPrice)) {
alert('入力が無効です')
return
//setNewStock({ ...newStock, price: parsedPrice });
}
await toBuyApi.buy({ tobuyId: selectedToBuyId, ...newStock, price: parsedPrice, lastUpdate: today }); //データベースに送信
setOpenInfoDialog(false);
fetchTasks(); // 変更後後の買うもの一覧を再取得
} catch (error) {
console.error(`${TOBUY_ERRORS.BUY_FAILED}:`, error);
}
};
/** /**
* *
* APIに送信して新規作成 * APIに送信して新規作成
@ -189,20 +185,27 @@ const TaskListPage: React.FC = () => {
} }
}; };
// /** /**
// * クリックされたタスクの情報を取得するための関数 *
// * @param tobuyId */
// * @returns tobuyIdが一致するtoBuy const handleBuy = async () => {
// */ try {
// const getToBuyDetails = (tobuyId: number): ToBuy => { const today = new Date().toISOString().substring(0, 10);
// console.log(tobuyId) const parsedPrice = parseInt(newStock.price, 10);
// const result = tobuys.find((toBuy) => toBuy.tobuyId === tobuyId); console.log("newPrice:", newStock.price)
// if(result === undefined){ console.log("parsedPrice: ", parsedPrice)
// throw new Error(`tobuyId: ${tobuyId} に対応するデータが見つかりません`); if (isNaN(parsedPrice)) {
// } alert('入力が無効です')
// return result; return
// }; //setNewStock({ ...newStock, price: parsedPrice });
}
await toBuyApi.buy({ tobuyId: selectedToBuyId, ...newStock, price: parsedPrice, lastUpdate: today }); //データベースに送信
setOpenInfoDialog(false);
fetchTasks(); // 変更後後の買うもの一覧を再取得
} catch (error) {
console.error(`${TOBUY_ERRORS.BUY_FAILED}:`, error);
}
};
return ( return (
<Container> <Container>
@ -223,7 +226,10 @@ const TaskListPage: React.FC = () => {
boxShadow: 1, boxShadow: 1,
}} }}
> >
<ListItemText primary={tobuy.stuffName} /> {/* 食材名 */}
<ListItemText
primary={tobuy.stuffName}
/>
{/* 買い物リスト:食材情報記入ボタン */} {/* 買い物リスト:食材情報記入ボタン */}
<ListItemSecondaryAction> <ListItemSecondaryAction>
<Typography variant="body1" component="span" sx={{ marginRight: '1em' }}> <Typography variant="body1" component="span" sx={{ marginRight: '1em' }}>
@ -255,10 +261,28 @@ const TaskListPage: React.FC = () => {
{/* 買い物リスト:食材削除ボタン */} {/* 買い物リスト:食材削除ボタン */}
<Tooltip title="項目を削除" <Tooltip title="項目を削除"
componentsProps={{ componentsProps={{
tooltip: {sx: {backgroundColor: "white", color: "red", fontSize: "0.8rem", padding: "6px", borderRadius: "6px"}}, tooltip: {
}}> sx: {
<IconButton edge="end" sx={{ marginRight: 0, marginLeft: 0 }} aria-label="delete" backgroundColor: "white",
onClick={() => handleDeleteToBuy(tobuy.tobuyId)}> color: "red",
fontSize: "0.8rem",
padding: "6px",
borderRadius: "6px",
},
},
}}
>
<IconButton
edge="end"
sx={{ marginRight: 0, marginLeft: 0 }}
aria-label="delete"
onClick={() => {//handleDeleteTask(tobuy.tobuyId)
setSelectedDeleteTask(tobuy)
setOpenDeleteDialog(true)
}
}
>
<DeleteIcon /> <DeleteIcon />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
@ -314,6 +338,28 @@ const TaskListPage: React.FC = () => {
setEditingItem={(v) => setEditingItem({...editingItem, ...v})} setEditingItem={(v) => setEditingItem({...editingItem, ...v})}
onSubmit={handleUpdateNewToBuy} /> onSubmit={handleUpdateNewToBuy} />
{/* 削除ダイアログ */}
<Dialog open={openDeleteDialog} onClose={() => setOpenDeleteDialog(false)} disableScrollLock={true}
fullWidth
maxWidth="sm"
sx={{ overflow: "hidden" }}
>
<DialogTitle></DialogTitle>
<DialogContent>
{selectedDeleteTask && (
<>
<Typography variant="h4">{selectedDeleteTask.stuffName}</Typography>
<Typography variant="body1" color="error"> 注意: 削除すると復元できません</Typography>
<Button onClick={() => setOpenDeleteDialog(false)} sx={{ mt: 3, mb: 2, left: '70%' }}></Button>
<Button variant="contained" color="error" onClick={() => {
handleDeleteToBuy(selectedDeleteTask.tobuyId);
setOpenDeleteDialog(false); // 削除処理後にダイアログを閉じる
}}
style={{ marginTop: "10px" }} sx={{ mt: 3, mb: 2, left: '72%' }}></Button>
</>
)}
</DialogContent>
</Dialog>
</Container> </Container>
); );

@ -277,6 +277,40 @@ export const stockApi = {
}, },
/**
*
* @param stock
* @returns
*/
addStock: async (stock: Omit<Stock, 'stockId' | 'stuffId'> & { stuffId: number | null }): Promise<{ result: boolean; message: string }> => {
console.log("送信するデータ:", stock); // 送信前のデータ確認
stock.buyDate = makeDateObject(stock.buyDate)?.toISOString()?.substring(0, 10) || ''
stock.expDate = makeDateObject(stock.expDate)?.toISOString()?.substring(0, 10) || ''
console.log("変換後のデータ:", stock); // 日付変換後のデータ確認
const response = await fetch(`${API_BASE_URL}/api/stocks/add`, {
method: 'POST',
headers: getHeaders(),
body: JSON.stringify(stock),
});
console.log("レスポンスステータス:", response.status);
console.log("レスポンスヘッダー:", response.headers);
// console.log("レスポンス内容:", await response.text());
if (!response.ok) {
throw new Error(STOCK_ERRORS.CREATE_FAILED);
}
return response.json();
// return {result: true}
},
/** /**
* *
*/ */

Loading…
Cancel
Save