Merge remote-tracking branch 'origin/develop-frontend-recipe-api' into feature_frontend_dishlist

feature_frontend_dishlist
masato.fujita 4 months ago
commit 341af26a01
  1. 16
      .vscode/launch.json
  2. 148
      backend/src/main/java/com/example/todoapp/controller/StocksController.java
  3. 64
      backend/src/main/java/com/example/todoapp/controller/StuffsController.java
  4. 80
      backend/src/main/java/com/example/todoapp/controller/ToBuysController.java
  5. 14
      backend/src/main/java/com/example/todoapp/dto/BuyRequestDTO.java
  6. 8
      backend/src/main/java/com/example/todoapp/dto/DeleteStockRequest.java
  7. 4
      backend/src/main/java/com/example/todoapp/dto/DeleteToBuyRequest.java
  8. 54
      backend/src/main/java/com/example/todoapp/dto/ResponseStockDTO.java
  9. 46
      backend/src/main/java/com/example/todoapp/dto/StockDTO.java
  10. 42
      backend/src/main/java/com/example/todoapp/dto/StuffsDTO.java
  11. 6
      backend/src/main/java/com/example/todoapp/dto/ToBuyResponse.java
  12. 28
      backend/src/main/java/com/example/todoapp/dto/ToBuysDTO.java
  13. 19
      backend/src/main/java/com/example/todoapp/model/RecipeStuffs.java
  14. 7
      backend/src/main/java/com/example/todoapp/model/Recipes.java
  15. 30
      backend/src/main/java/com/example/todoapp/model/Stocks.java
  16. 10
      backend/src/main/java/com/example/todoapp/model/Stuffs.java
  17. 2
      backend/src/main/java/com/example/todoapp/model/Task.java
  18. 25
      backend/src/main/java/com/example/todoapp/model/ToBuys.java
  19. 67
      backend/src/main/java/com/example/todoapp/repository/StocksRepository.java
  20. 21
      backend/src/main/java/com/example/todoapp/repository/StuffsRepository.java
  21. 24
      backend/src/main/java/com/example/todoapp/repository/ToBuysRepository.java
  22. 112
      backend/src/main/java/com/example/todoapp/service/StocksService.java
  23. 39
      backend/src/main/java/com/example/todoapp/service/StuffsService.java
  24. 134
      backend/src/main/java/com/example/todoapp/service/ToBuysService.java
  25. 35
      frontend/src/constants/errorMessages.ts
  26. 2
      frontend/src/pages/AddDishes1.tsx
  27. 49
      frontend/src/pages/AddDishes2.tsx
  28. 75
      frontend/src/pages/StockPage.tsx
  29. 83
      frontend/src/pages/TaskListPage.tsx
  30. 392
      frontend/src/services/api.ts
  31. 75
      frontend/src/types/types.ts

@ -0,0 +1,16 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Launch Spring Boot App (local profile)",
"request": "launch",
"cwd": "${workspaceFolder}/backend",
"mainClass": "com.example.todoapp.TodoApplication",
"projectName": "todo-app",
"args": "",
"envFile": "${workspaceFolder}/backend/.env",
"vmArgs": "-Dspring.profiles.active=local"
}
]
}

@ -0,0 +1,148 @@
package com.example.todoapp.controller;
import com.example.todoapp.dto.ResponseStockDTO;
import com.example.todoapp.dto.DeleteStockRequest;
import com.example.todoapp.dto.StockDTO;
import com.example.todoapp.model.Stocks;
import com.example.todoapp.model.User;
import com.example.todoapp.repository.UserRepository;
import com.example.todoapp.service.StocksService;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 在庫管理のコントローラー
* <p>
* このコントローラーは在庫の取得作成更新削除などの
* エンドポイントを提供しますすべてのエンドポイントは認証が必要です
* </p>
*/
@RestController
@RequestMapping("/stocks")
public class StocksController {
@Autowired
private StocksService stockService;
@Autowired
private UserRepository userRepository;
/**
* ログインユーザーのすべての在庫を取得する
*
* @param authentication 認証情報
* @return ユーザーの在庫リスト
*/
@GetMapping("/get")
public ResponseEntity<List<ResponseStockDTO>> getAllStocks(Authentication authentication) {
List<Stocks> stocks = stockService.getALLStocksByUser(authentication.getName());
// エンティティからDTOへの変換
List<ResponseStockDTO> stockDTOs = stocks.stream()
.map(ResponseStockDTO::fromEntity)
.collect(Collectors.toList());
return ResponseEntity.ok(stockDTOs);
}
/**
* 指定されたIDの在庫を取得する
*
* @param authentication 認証情報
* @param stockId 在庫ID
* @return 在庫情報
*/
@GetMapping("/{id}")
public ResponseEntity<StockDTO> getStockById(
Authentication authentication,
@PathVariable("id") Long stockId) {
Stocks stock = stockService.getStockById(authentication.getName(), stockId);
return ResponseEntity.ok(StockDTO.fromEntity(stock));
}
/**
* 新しい在庫を作成する
*
* @param authentication 認証情報
* @param stock 作成する在庫の情報
* @return 作成された在庫
*/
@PostMapping("/add")
public ResponseEntity<StockDTO> createStock(
Authentication authentication,
@Valid @RequestBody Stocks stock) {
Stocks createdStock = stockService.createStock(authentication.getName(), stock);
return ResponseEntity.ok(StockDTO.fromEntity(createdStock));
}
/**
* 指定されたIDの在庫を更新する
*
* @param authentication 認証情報
* @param stockId 更新する在庫のID
* @param stockDetails 更新内容
* @return 更新された在庫
*/
@PutMapping("/update")
public ResponseEntity<Map<String, Object>> updateStock(
Authentication authentication,
@Valid @RequestBody StockDTO stockDTO) {
Stocks updatedStock = stockService.updateStocks(authentication.getName(), stockDTO);
Map<String, Object> response = new HashMap<>();
if (updatedStock == null) {
response.put("result", false);
response.put("message", "指定されたIDの在庫が見つかりませんでした");
return ResponseEntity.ok(response);
}else {
response.put("result", true);
response.put("message", "变更成功しました");
}
return ResponseEntity.ok(response);
}
/**
* 指定されたIDの在庫を削除する
*
* @param authentication 認証情報
* @param taskId 削除する在庫のID
* @return 空のレスポンス
*/
@DeleteMapping("/delete")
public ResponseEntity<?> deleteStock(
Authentication authentication,
@RequestBody DeleteStockRequest request
) {
// 認証されたユーザー名を取得
String username = authentication.getName();
// ユーザー情報を取得
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
int deletedCount = stockService.deleteStockById(user.getId(), request.getStockId());
Map<String, Object> response = new HashMap<>();
if (deletedCount > 0) {
response.put("result", true);
response.put("message", "削除成功しました");
} else {
response.put("result", false);
response.put("message", "指定された在庫が見つかりませんでした");
}
return ResponseEntity.ok(response);
}
}

@ -0,0 +1,64 @@
//--------------------------------
// StuffsController.java
//
// 更新履歴:2025/06/09 新規作成
// Copyright(c) 2025 IVIS All rights reserved.
//--------------------------------------------
package com.example.todoapp.controller;
import com.example.todoapp.dto.StuffsDTO;
import com.example.todoapp.model.Stuffs;
import com.example.todoapp.service.StuffsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.stream.Collectors;
import java.util.Collections;
/**
* 材料リストに関するRESTコントローラー
*
* このコントローラーは材料 (stuffs) の管理に関するエンドポイントを提供します
*
*/
@RestController
@RequestMapping("/stuff")
public class StuffsController {
@Autowired
private StuffsService stuffsService;
/**
* カテゴリ指定で材料を取得するメソッド
*
* @param category カテゴリ名
* @return 指定されたカテゴリに属する材料のリスト
*/
@GetMapping("/get")
public ResponseEntity<?> getAllStuffsByCategory(String category) {
List<Stuffs> stuffsList = stuffsService.getAllStuffsByCategory(category);
//DTOに変換
List<StuffsDTO> responceList = stuffsList.stream()
.map(stuff -> {
StuffsDTO resp = new StuffsDTO();
resp.setStuffId(stuff.getStuffId());
resp.setStuffName(stuff.getStuffName());
resp.setSummary(stuff.getSummary());
resp.setCategory(stuff.getCategory());
return resp;
})
.collect(Collectors.toList());
// 空のリストの場合は空のリストを返す
if (responceList.isEmpty()) {
return ResponseEntity.ok(Collections.emptyList());
}
return ResponseEntity.ok(responceList);
}
}

@ -7,9 +7,11 @@
package com.example.todoapp.controller;
import com.example.todoapp.dto.BuyRequestDTO;
import com.example.todoapp.dto.DeleteToBuyRequest;
import com.example.todoapp.dto.ToBuyResponse;
import com.example.todoapp.dto.ToBuysDTO;
import com.example.todoapp.model.Stuffs;
import com.example.todoapp.model.ToBuys;
import com.example.todoapp.model.User;
import com.example.todoapp.repository.UserRepository;
@ -20,6 +22,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.Collections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
@ -30,7 +33,7 @@ import org.springframework.web.bind.annotation.*;
/**
* 購入リストに関するRESTコントローラー
* <p>
* このコントローラーは購入リスト (to_buys) へのアイテム追加機能を提供します
* このコントローラーは購入リスト (toBuys) へのアイテム追加機能を提供します
* リクエストボディには ToBuyDTO 形式のデータが期待されます
* </p>
*/
@ -52,19 +55,41 @@ public class ToBuysController {
*/
@PostMapping("/add")
public ResponseEntity<String> addToBuys(
public ResponseEntity<Map<String, Object>> addToBuys(
@Valid @RequestBody ToBuysDTO dto,
Authentication authentication) {
toBuysService.addToBuys(dto, authentication);
return ResponseEntity.ok("Item added to 'To Buys' successfully");
ToBuys createdToBuy = toBuysService.addToBuys(dto, authentication);
Map<String, Object> response = new HashMap<>();
response.put("result", true);
response.put("tobuyId", createdToBuy.getTobuyId());
response.put("stuffId", createdToBuy.getStuff().getStuffId());
response.put("message", "追加に成功しました");
return ResponseEntity.ok(response);
}
/**
* 指定されたIDの在庫を更新する
*
* @param authentication 認証情報
* @param dto 更新内容
* @return 更新された
*/
@PutMapping("/update")
public ResponseEntity<String> updateToBuys(
public ResponseEntity<Map<String, Object>> updateToBuys(
@Valid @RequestBody ToBuysDTO dto,
Authentication authentication) {
toBuysService.updateToBuys(dto, authentication);
return ResponseEntity.ok("Item updated to 'To Buys' successfully");
ToBuys updatedToBuy = toBuysService.updateToBuys(dto, authentication);
Map<String, Object> response = new HashMap<>();
response.put("tobuyId", updatedToBuy.getTobuyId());
response.put("stuffId", updatedToBuy.getStuff().getStuffId());
return ResponseEntity.ok(response);
}
@ -85,23 +110,24 @@ public class ToBuysController {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
List<ToBuys> toBuysList = toBuysService.getToBuysByUserId(user.getId());
List<ToBuys> toBuysList = toBuysService.getToBuysByUser(user);
// DTO形式に変換して返す
List<ToBuyResponse> responseList = toBuysList.stream()
.map(toBuy -> {
ToBuyResponse resp = new ToBuyResponse();
resp.setTobuy_id(toBuy.getTobuy_id());
resp.setStuff_id(toBuy.getStuffs().getStuff_id());
resp.setStuff_name(toBuy.getStuffs().getStuff_name());
Stuffs stuff = toBuy.getStuff();
resp.setTobuyId(toBuy.getTobuyId());
resp.setStuffId(stuff.getStuffId());
resp.setStuffName(stuff.getStuffName());
resp.setAmount(toBuy.getAmount());
resp.setShop(toBuy.getStore());
return resp;
})
.collect(Collectors.toList());
Map<String, Object> responseBody = new HashMap<>();
responseBody.put("tobuy_array", responseList);
// Map<String, Object> responseBody = new HashMap<>();
// responseBody.put("tobuy_array", responseList);
return ResponseEntity.ok(responseList);
}
@ -116,19 +142,11 @@ public class ToBuysController {
public ResponseEntity<Map<String, Boolean>> deleteToBuy(
@RequestBody DeleteToBuyRequest request,
Authentication authentication) {
// 認証されたユーザー名を取得
String username = authentication.getName();
// ユーザー情報を取得
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
int deletedCount = toBuysService.deleteToBuyByIds(user.getId(), request.getTobuy_id());
int deletedCount = toBuysService.deleteToBuyById(request.getTobuyId());
Map<String, Boolean> response = new HashMap<>();
if (deletedCount > 0) {
response.put("result", true);
} else {
@ -137,4 +155,24 @@ public class ToBuysController {
return ResponseEntity.ok(response);
}
/**
* 買うものリストの在庫登録購入処理
* 買うものリストから削除し追加情報を付加して在庫リストに追加
*
* @param request 買うものの情報を含むリクエストボディ
* @return
*/
@PostMapping("/buy")
public ResponseEntity<Map<String, Boolean>> buyToBuys(
Authentication authentication,
@RequestBody BuyRequestDTO toBuysDTO) {
toBuysService.buyToBuys(authentication.getName(), toBuysDTO);
Map<String, Boolean> response = new HashMap<>();
response.put("result", true);
return ResponseEntity.ok(response);
}
}

@ -0,0 +1,14 @@
package com.example.todoapp.dto;
import java.time.LocalDate;
import lombok.Data;
@Data
public class BuyRequestDTO {
private Long tobuyId;
private int price;
private LocalDate expDate;
private LocalDate lastUpdate;
private LocalDate buyDate;
}

@ -0,0 +1,8 @@
package com.example.todoapp.dto;
import lombok.Data;
@Data
public class DeleteStockRequest {
private Long stockId;
}

@ -4,6 +4,6 @@ import lombok.Data;
@Data
public class DeleteToBuyRequest {
private Long user_id;
private int tobuy_id;
private Long userId;
private Long tobuyId;
}

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

@ -0,0 +1,46 @@
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 StockDTO {
private Long stockId;
private Long stuffId;
private Long userId;
private int amount;
private int price;
private LocalDate buyDate;
private LocalDate lastUpdate;
private LocalDate expDate;
/**
* 在庫エンティティからDTOを作成する
*
* @param stock 変換元の在庫エンティティ
* @return 変換されたStockDTOオブジェクト
*/
public static StockDTO fromEntity(Stocks stock) {
StockDTO dto = new StockDTO();
dto.setStockId(stock.getStockId());
dto.setStuffId(stock.getStuff().getStuffId());
dto.setUserId(stock.getUser().getId());
dto.setAmount(stock.getAmount());
dto.setPrice(stock.getPrice());
dto.setBuyDate(stock.getBuyDate());
dto.setLastUpdate(stock.getLastUpdate());
dto.setExpDate(stock.getExpDate());
return dto;
}
}

@ -0,0 +1,42 @@
//--------------------------------
// StuffsDTO.java
//
//
// 更新履歴:2025/06/09 新規作成
// Copyright(c) 2025 IVIS All rights reserved.
//--------------------------------------------
package com.example.todoapp.dto;
import com.example.todoapp.model.Stuffs;
import lombok.Data;
/**
* 材料のデータ転送オブジェクトDTOクラス
*
* このクラスは材料情報をクライアントとサーバー間でやり取りするために使用されます
* エンティティとは異なり必要な情報のみを含み関連エンティティへの参照ではなくIDのみを保持します
*/
@Data
public class StuffsDTO {
private Long stuffId; // 材料ID
private String stuffName; // 材料名
private String summary; // 概要
private String category; // カテゴリ
/**
* StuffsエンティティからDTOを作成する
*
* @param stuff 変換元のStuffsエンティティ
* @return 変換されたStuffsDTOオブジェクト
*/
public static StuffsDTO fromEntity(Stuffs stuff) {
StuffsDTO dto = new StuffsDTO();
dto.setStuffId(stuff.getStuffId());
dto.setStuffName(stuff.getStuffName());
dto.setSummary(stuff.getSummary());
dto.setCategory(stuff.getCategory());
return dto;
}
}

@ -4,9 +4,9 @@ import lombok.Data;
@Data
public class ToBuyResponse {
private int tobuy_id;
private Long stuff_id;
private String stuff_name;
private Long tobuyId;
private Long stuffId;
private String stuffName;
private int amount;
private String shop;
}

@ -1,6 +1,9 @@
package com.example.todoapp.dto;
import java.time.LocalDate;
import lombok.Data;
/**
* カテゴリDTOクラス
* このクラスはタスクのカテゴリ情報を表します
@ -8,14 +11,17 @@ import lombok.Data;
*
*/
@Data
public class ToBuysDTO {
private Long stuff_id;
private Long user_id;
private int amount;
private String shop;
private String stuff_name;
private String category;
}
@Data
public class ToBuysDTO {
private Long tobuyId;
private Long stuffId;
private Long userId;
private int price;
private int amount;
private String shop;
private String stuffName;
private String category;
private LocalDate expDate;
private LocalDate lastUpdate;
private LocalDate buyDate;
}

@ -28,7 +28,7 @@ import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@Entity
@Table(name = "recipe_stuffs")
@Table(name = "recipeStuffs")
public class RecipeStuffs {
@ -37,7 +37,8 @@ public class RecipeStuffs {
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int recipe_stuffs_id ;
@Column(name="recipeStuffsId")
private Long recipeStuffsId ;
/**
* 料理の一意識別子 FK
@ -45,8 +46,8 @@ public class RecipeStuffs {
@NotBlank
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(
name = "recipe_id",
referencedColumnName = "recipe_id",
name = "recipeId",
referencedColumnName = "recipeId",
nullable = false
)
private Recipes recipes;
@ -58,17 +59,17 @@ public class RecipeStuffs {
@NotBlank
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(
name = "stuff_id",
referencedColumnName = "stuff_id",
name = "stuffId",
referencedColumnName = "stuffId",
nullable = false
)
private Stuffs stuffs;
private Stuffs stuff;
/**
* 材料の数量
* 材料の数量(デフォルト1)
*/
@NotBlank
@Column(nullable = false)
private int amount;
private int amount = 1;
}

@ -35,14 +35,15 @@ public class Recipes {
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int recipe_id ;
@Column(name="recipeId")
private Long recipeId ;
/**
* カテゴリ名
*/
@NotNull
@Column(unique = true, length = 255, nullable = false)
private String recipie_name;
@Column(name="recipeName", unique = true, length = 255, nullable = false)
private String recipieName;
/**
* カテゴリ

@ -39,7 +39,7 @@ public class Stocks {
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int stock_id ;
private Long stockId;
/**
@ -48,24 +48,20 @@ public class Stocks {
@NotNull
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(
name = "stuff_id",
referencedColumnName = "stuff_id",
name = "stuffId",
referencedColumnName = "stuffId",
nullable = false
)
private Stuffs stuffs;
private Stuffs stuff;
/**
* ユーザーテーブル参照用の外部キー
* タスクの所有者ユーザー
* 多対一の関係で遅延ロードを使用
*/
@NotNull
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(
name = "user_id",
referencedColumnName = "id",
nullable = false
)
private User user_id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "userId", nullable = false)
private User user;
/**
* 在庫数量デフォルト値: 1
@ -83,18 +79,18 @@ public class Stocks {
* 購入日
*/
@Column(nullable = false)
private LocalDate buy_date;
private LocalDate buyDate;
/**
* 最後の操作時間
*/
@Column(nullable = false)
private LocalDate last_update;
private LocalDate lastUpdate;
/**
* 賞味期限
*/
@Column(nullable = false)
private LocalDate exp_date;
private LocalDate expDate;
}

@ -1,9 +1,8 @@
//--------------------------------
// Stuffs.java
//
// 分類:社員管理システムV2・ビジネスロジック層
//
// 更新履歴:2025/06/02 新規作成
// 更新履歴:2025/06/09 新規作成
// Copyright(c) 2025 IVIS All rights reserved.
//--------------------------------------------
@ -35,14 +34,15 @@ import lombok.NoArgsConstructor;
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long stuff_id ;
@Column(name = "stuffId")
private Long stuffId;
/**
* カテゴリ名
*/
@NotNull
@Column(unique = true, length = 255, nullable = false)
private String stuff_name;
@Column(name = "stuffName", unique = true, length = 255, nullable = false)
private String stuffName;
/**
* カテゴリ

@ -51,7 +51,7 @@ public class Task {
* 多対一の関係で遅延ロードを使用
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
@JoinColumn(name = "userId", nullable = false)
private User user;
/**

@ -28,7 +28,7 @@ import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@Entity
@Table(name = "to_buys")
@Table(name = "toBuys")
public class ToBuys {
/**
@ -36,7 +36,8 @@ public class ToBuys {
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int tobuy_id ;
@Column(name = "tobuyId")
private Long tobuyId ;
/**
* 材料の一意識別子 FK
@ -44,22 +45,24 @@ public class ToBuys {
@NotNull
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(
name = "stuff_id",
referencedColumnName = "stuff_id",
name = "stuffId",
referencedColumnName = "stuffId",
nullable = false
)
private Stuffs stuffs;
private Stuffs stuff;
/**
* ユーザーテーブル参照用の外部キー
*/
// @NotNull
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(
name = "user_id",
referencedColumnName = "id"
)
private User user_id;
/**
* タスクの所有者ユーザー
* 多対一の関係で遅延ロードを使用
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "userId", nullable = false)
private User user;
/**
* 購入する数量

@ -0,0 +1,67 @@
//--------------------------------
// ToBuysRepository.java
//
//
// 更新履歴:2025/06/05 新規作成
// Copyright(c) 2025 IVIS All rights reserved.
//--------------------------------------------
package com.example.todoapp.repository;
import com.example.todoapp.model.Stocks;
import jakarta.transaction.Transactional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 在庫エンティティのリポジトリインターフェース
* <p>
* このインターフェースは在庫データへのアクセスと操作を提供します
* Spring Data JPAによって自動的に実装されます
* </p>
*/
@Repository
public interface StocksRepository extends JpaRepository<Stocks, Long> {
/**
* userIdから在庫一覧をstockId順で取得する
*
* @param userId 検索するユーザーID
* @return 在庫リスト
*/
List<Stocks> findStocksByUserId(Long userId);
/**
* 在庫情報を更新する
*
* @param stock 編集する新たな情報が入ったstockオブジェクト
* @return 編集に成功したらtrue
*/
// boolean updateStocksByStockId(Stocks stock);
//updateをクエリ作成にて実装
@Modifying
@Transactional
@Query("UPDATE Stocks s SET s.amount = :#{#stock.amount}, s.price = :#{#stock.price}, s.buyDate = :#{#stock.buyDate}, s.lastUpdate = :#{#stock.lastUpdate}, s.expDate = :#{#stock.expDate} WHERE s.stockId = :#{#stock.stockId}")
int updateStocksById(@Param("stock") Stocks stock);
/**
* 在庫リストから指定した食材を削除する
*
* @param stockId 削除する在庫
* @param userId 削除するユーザー
*/
// void deleteStocksByStockIdAndUserId(Long stockId, Long userId);
@Modifying
@Transactional
@Query("DELETE FROM Stocks t WHERE t.user.id = :userId AND t.stockId = :stockId")
int deleteByUserIdAndStockId(@Param("userId") Long userId, @Param("stockId") Long stockId);
}

@ -2,13 +2,19 @@
// StuffsRepository.java
//
//
// 更新履歴:2025/06/05 新規作成
// 更新履歴:2025/06/09 新規作成
// Copyright(c) 2025 IVIS All rights reserved.
//--------------------------------------------
package com.example.todoapp.repository;
import com.example.todoapp.model.Stuffs;
import java.util.Optional;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@ -22,4 +28,17 @@ import org.springframework.stereotype.Repository;
@Repository
public interface StuffsRepository extends JpaRepository<Stuffs, Long> {
// 材料情報を主キーで取得するメソッド(必要に応じて追加)
Stuffs findByStuffId(Long stuffId);
Optional<Stuffs> findByStuffName(String stuffName);
/**
* 指定されたカテゴリに属する材料のリストを取得するメソッド.
*
* @param category カテゴリ名
* @return 指定されたカテゴリに属する材料のリスト
*/
List<Stuffs> findByCategory(String category);
}

@ -18,32 +18,40 @@ import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
/**
* to_buys テーブルへのアクセスを行うリポジトリ
* toBuys テーブルへのアクセスを行うリポジトリ
* <p>
* このクラスは to_buys テーブルに対する基本的なCRUD操作を提供します
* このクラスは toBuys テーブルに対する基本的なCRUD操作を提供します
* Spring Data JPAによって自動的に実装されます
* </p>
*/
@Repository
public interface ToBuysRepository extends JpaRepository<ToBuys, Integer> {
/**
* 指定されたIDに基づいて買うものレコードを取得する
*
* @param tobuyId 買うものID
* @return 買うものレコード
*/
@Query("SELECT t FROM ToBuys t WHERE t.tobuyId = ?1")
ToBuys findById(Long tobuyId);
/**
* 指定されたユーザーIDに基づいて買うものリストを取得する
*
* @param userId ユーザーID
* @return 買うものリスト
*/
@Query("SELECT t FROM ToBuys t WHERE t.user_id.id = ?1")
List<ToBuys> findByUserId(Long user_id);
@Query("SELECT t FROM ToBuys t WHERE t.user.id = ?1")
List<ToBuys> findByUser(Long userId);
/**
* 指定されたユーザーIDに基づいて買うものリストを取得する
* 指定された買うものIDに基づいて買うものリストを削除
*
* @param userId ユーザーID
* @param tobuyId 買うものID
* @return
*/
@Modifying
@Query("DELETE FROM ToBuys t WHERE t.user_id.id = :userId AND t.tobuy_id = :tobuyId")
int deleteByUserIdAndTobuyId(@Param("userId") Long userId, @Param("tobuyId") int tobuyId);
@Query("DELETE FROM ToBuys t WHERE t.tobuyId = :tobuyId")
int deleteByTobuyId(@Param("tobuyId") Long tobuyId);
}

@ -0,0 +1,112 @@
package com.example.todoapp.service;
import com.example.todoapp.dto.StockDTO;
import com.example.todoapp.model.Stocks;
import com.example.todoapp.util.MessageUtils;
import com.example.todoapp.model.User;
import com.example.todoapp.repository.StocksRepository;
import com.example.todoapp.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* Stocksテーブルのサービスクラス
* <p>
* このクラスは在庫の追加取得更新削除などのビジネスロジックを提供します
* </p>
*/
@Service
public class StocksService {
@Autowired
private StocksRepository stocksRepository;
@Autowired
private UserRepository userRepository;
@Autowired
private MessageUtils messageUtils;
/**
* 新たな在庫を追加する
*
* @param username ユーザー名
* @param stock 追加する在庫情報
* @return 保存された在庫エンティティ
*/
public Stocks createStock(String username, Stocks stock) {
User user = getUserByUsername(username);
stock.setUser(user);
return stocksRepository.save(stock);
}
/**
* 指定されたユーザのすべての在庫を取得する
* @param username ユーザー名
* @return ユーザーの在庫リスト(stockId昇順)
*/
public List<Stocks> getALLStocksByUser(String username) {
User user = getUserByUsername(username);
return stocksRepository.findStocksByUserId(user.getId());
}
/**
* 指定されたユーザーの特定の在庫を取得する
*
* @param username ユーザー名
* @param stockId 在庫ID
* @return 在庫エンティティ
* @throws RuntimeException 在庫が見つからない場合または他のユーザーの在庫にアクセスしようとした場合
*/
public Stocks getStockById(String username, Long stockId) {
User user = getUserByUsername(username);
return stocksRepository.findById(stockId)
.filter(stock -> stock.getUser().equals(user)) // ユーザーの在庫かどうかを確認
.orElseThrow(() -> new RuntimeException(messageUtils.getMessage("error.stock.not.found")));
}
/**
* 指定された在庫情報を編集する
*
* @param username ユーザー名
* @param stockId 変数する在庫ID
* @param stockDetails 編集内容(新しい情報)
* @return 編集された在庫エンティティ
*/
public Stocks updateStocks(String username, StockDTO stockDto) {
Stocks stock = getStockById(username, stockDto.getStockId());
stock.setAmount(stockDto.getAmount());
stock.setPrice(stockDto.getPrice());
stock.setLastUpdate(stockDto.getLastUpdate());
stock.setBuyDate(stockDto.getBuyDate());
stock.setExpDate(stockDto.getExpDate());
return stocksRepository.save(stock);
}
/**
* 指定された在庫を削除する
*
* @param userId ユーザー名
* @param stockId 削除する在庫のID
*/
public int deleteStockById(Long userId, Long stockId) {
return stocksRepository.deleteByUserIdAndStockId(userId, stockId);
}
/**
* ユーザー名からユーザーエンティティを取得する
*
* @param username ユーザー名
* @return ユーザーエンティティ
* @throws UsernameNotFoundException ユーザーが見つからない場合
*/
private User getUserByUsername(String username) {
return userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(messageUtils.getMessage("error.auth.user.not.found.with.name", new Object[]{username})));
}
}

@ -0,0 +1,39 @@
//--------------------------------
// StuffsService.java
//
//
// 更新履歴:2025/06/09 新規作成
// Copyright(c) 2025 IVIS All rights reserved.
//--------------------------------------------
package com.example.todoapp.service;
import com.example.todoapp.model.Stuffs;
import com.example.todoapp.repository.StuffsRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 材料 (stuffs) のサービスクラス
*
* このクラスは材料 (stuffs) に関するビジネスロジックを提供します
*/
@Service
public class StuffsService {
@Autowired
private StuffsRepository stuffsRepository;
/**
* カテゴリ指定で材料を取得するメソッド
*
* @param category カテゴリ名
* @return 指定されたカテゴリに属する材料のリスト
*/
public List<Stuffs> getAllStuffsByCategory(String category) {
return stuffsRepository.findByCategory(category);
}
}

@ -8,10 +8,14 @@
package com.example.todoapp.service;
import com.example.todoapp.util.MessageUtils;
import com.example.todoapp.dto.BuyRequestDTO;
import com.example.todoapp.dto.ToBuysDTO;
import com.example.todoapp.model.Stocks;
import com.example.todoapp.model.Stuffs;
import com.example.todoapp.model.ToBuys;
import com.example.todoapp.model.User;
import com.example.todoapp.repository.StocksRepository;
import com.example.todoapp.repository.StuffsRepository;
import com.example.todoapp.repository.ToBuysRepository;
import com.example.todoapp.repository.UserRepository;
@ -19,6 +23,7 @@ import com.example.todoapp.repository.UserRepository;
import jakarta.transaction.Transactional;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -28,7 +33,7 @@ import java.util.Optional;
/**
* 購入リストのサービスクラス
* <p>
* このクラスは購入リスト (to_buys) の登録処理を提供します
* このクラスは購入リスト (toBuys) の登録処理を提供します
* 材料 (stuffs) の存在確認と新規作成ユーザー情報の取得などを行います
* </p>
*/
@ -44,6 +49,12 @@ public class ToBuysService {
@Autowired
private StuffsRepository stuffsRepository;
@Autowired
private StocksRepository stocksRepository;
@Autowired
private MessageUtils messageUtils;
/**
@ -51,38 +62,38 @@ public class ToBuysService {
*
* @param toBuyDTO 追加する購入アイテムのデータDTO
*/
public void addToBuys(ToBuysDTO toBuyDTO, Authentication authentication) {
public ToBuys addToBuys(ToBuysDTO toBuyDTO, Authentication authentication) {
// ユーザー情報を取得
String username = authentication.getName();
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new RuntimeException("ユーザーが見つかりません: " + username));
Stuffs stuffs;
if (toBuyDTO.getStuff_id() == null) {
Stuffs stuff;
if (toBuyDTO.getStuffId() == null) {
// 新しい材料を作成
stuffs = new Stuffs();
stuffs.setStuff_name(toBuyDTO.getStuff_name());
stuffs.setCategory(toBuyDTO.getCategory());
stuffs = stuffsRepository.save(stuffs);
stuff = new Stuffs();
stuff.setStuffName(toBuyDTO.getStuffName());
stuff.setCategory(toBuyDTO.getCategory());
stuff = stuffsRepository.save(stuff);
} else {
// 材料情報を取得
Optional<Stuffs> optionalStuffs = stuffsRepository.findById(toBuyDTO.getStuff_id());
Optional<Stuffs> optionalStuffs = stuffsRepository.findById(toBuyDTO.getStuffId());
if (!optionalStuffs.isPresent()) {
throw new RuntimeException("材料がありません");
}
stuffs = optionalStuffs.get();
stuff = optionalStuffs.get();
}
ToBuys toBuys = new ToBuys();
toBuys.setUser_id(user);
toBuys.setStuffs(stuffs);
toBuys.setUser(user);
toBuys.setStuff(stuff);
toBuys.setAmount(toBuyDTO.getAmount());
toBuys.setStore(toBuyDTO.getShop());
// データベースに保存
toBuysRepository.save(toBuys);
return toBuysRepository.save(toBuys);
}
/**
@ -90,7 +101,7 @@ public class ToBuysService {
*
* @param toBuyDTO 変更する購入アイテムのデータDTO
*/
public void updateToBuys(ToBuysDTO toBuyDTO, Authentication authentication) {
public ToBuys updateToBuys(ToBuysDTO toBuyDTO, Authentication authentication) {
// ユーザー情報を取得
String username = authentication.getName();
@ -98,57 +109,106 @@ public class ToBuysService {
.orElseThrow(() -> new RuntimeException("ユーザーが見つかりません: " + username));
Stuffs stuffs;
if (toBuyDTO.getStuff_id() == null) {
if (toBuyDTO.getStuffId() == null) {
Optional<Stuffs> existingStuffs = stuffsRepository.findByStuffName(toBuyDTO.getStuffName());
// 新しい材料を作成
stuffs = new Stuffs();
stuffs.setStuff_name(toBuyDTO.getStuff_name());
stuffs.setCategory(toBuyDTO.getCategory());
stuffs = stuffsRepository.save(stuffs);
if (existingStuffs.isPresent()) {
// 如果存在,更新已有材料的属性
stuffs = existingStuffs.get();
stuffs.setCategory(toBuyDTO.getCategory()); // 可选:更新分类
} else {
// 否则新建材料
stuffs = new Stuffs();
stuffs.setStuffName(toBuyDTO.getStuffName());
stuffs.setCategory(toBuyDTO.getCategory());
}
stuffsRepository.save(stuffs);
} else {
// 材料情報を取得
Optional<Stuffs> optionalStuffs = stuffsRepository.findById(toBuyDTO.getStuff_id());
Optional<Stuffs> optionalStuffs = stuffsRepository.findById(toBuyDTO.getStuffId());
if (!optionalStuffs.isPresent()) {
throw new RuntimeException("材料がありません");
}
stuffs = optionalStuffs.get();
//update
// stuffs.setStuff_name(toBuyDTO.getStuff_name());
// stuffs.setCategory(toBuyDTO.getCategory());
stuffs = stuffsRepository.save(stuffs);
}
ToBuys toBuys = new ToBuys();
toBuys.setUser_id(user);
toBuys.setStuffs(stuffs);
toBuys.setTobuyId(toBuyDTO.getTobuyId());
toBuys.setUser(user);
toBuys.setStuff(stuffs);
toBuys.setAmount(toBuyDTO.getAmount());
toBuys.setStore(toBuyDTO.getShop());
// データベースに保存
toBuysRepository.save(toBuys);
return toBuysRepository.save(toBuys);
}
/**
* 指定されたユーザーIDに基づいてすべての買うものリストを取得する
* 指定されたユーザーに基づいてすべての買うものリストを取得する
*
* @param userId ユーザーID
* @param user ユーザー
* @return ユーザーに紐づく買うものリスト
*/
public List<ToBuys> getToBuysByUserId(Long userId) {
return toBuysRepository.findByUserId(userId);
public List<ToBuys> getToBuysByUser(User user) {
return toBuysRepository.findByUser(user.getId());
}
/**
* 指定されたユーザーIDと購入リストIDに基づいて買うものを削除する
* 指定された購入リストIDに基づいて買うものを削除する
*
* @param userId ユーザーID
* @param tobuyId 購入リストID
*/
@Transactional
public int deleteToBuyByIds(Long userId, int tobuyId) {
return toBuysRepository.deleteByUserIdAndTobuyId(userId, tobuyId);
public int deleteToBuyById(Long tobuyId) {
return toBuysRepository.deleteByTobuyId(tobuyId);
}
/**
* 指定されたユーザーIDと購入データに基づいて買うものを購入する
*
* @param username ユーザーID
* @param dto 購入データ
*/
@Transactional
public Stocks buyToBuys(String username, BuyRequestDTO dto) {
// ユーザー情報を取得
User user = getUserByUsername(username);
// Tobuy情報の取得
ToBuys tobuy = toBuysRepository.findById(dto.getTobuyId());
// 新しい在庫を作成
Stocks stock = new Stocks();
stock.setStuff(tobuy.getStuff());
stock.setUser(user);
stock.setAmount(tobuy.getAmount());
stock.setPrice(dto.getPrice());
stock.setLastUpdate(dto.getLastUpdate());
stock.setBuyDate(dto.getBuyDate());
stock.setExpDate(dto.getExpDate());
// 買うものリストから削除
System.out.println("tobuy.getTobuyId()=" + tobuy.getTobuyId());
deleteToBuyById(tobuy.getTobuyId());
// データベースに保存
return stocksRepository.save(stock);
}
/**
* ユーザー名からユーザーエンティティを取得する
*
* @param username ユーザー名
* @return ユーザーエンティティ
* @throws UsernameNotFoundException ユーザーが見つからない場合
*/
private User getUserByUsername(String username) {
return userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(messageUtils.getMessage("error.auth.user.not.found.with.name", new Object[]{username})));
}
}

@ -14,18 +14,37 @@ export const AUTH_ERRORS = {
REGISTER_FAILED: 'ユーザー登録に失敗しました',
};
// タスク関連のエラーメッセージ
export const TASK_ERRORS = {
FETCH_FAILED: 'タスクの取得に失敗しました',
CREATE_FAILED: 'タスクの作成に失敗しました',
UPDATE_FAILED: 'タスクの更新に失敗しました',
DELETE_FAILED: 'タスクの削除に失敗しました',
};
// 買うものリスト関連のエラーメッセージ
export const TOBUY_ERRORS = {
FETCH_FAILED: '買うものリストの取得に失敗しました',
CREATE_FAILED: '買うものリストの作成に失敗しました',
UPDATE_FAILED: '買うものリストの更新に失敗しました',
DELETE_FAILED: '買うものリストの削除に失敗しました',
BUY_FAILED: '買うものリストの購入処理に失敗しました',
};
// 材料リスト関連のエラーメッセージ(料理の追加編集で用いる)
export const STUFF_ERRORS = {
FETCH_FAILED: '材料リストの取得に失敗しました',
CREATE_FAILED: '材料リストの作成に失敗しました',
UPDATE_FAILED: '材料リストの更新に失敗しました',
DELETE_FAILED: '材料リストの削除に失敗しました',
BUY_FAILED: '材料リストの購入処理に失敗しました',
};
// 在庫リスト関連のエラーメッセージ
export const STOCK_ERRORS = {
FETCH_FAILED: '在庫リストの取得に失敗しました',
CREATE_FAILED: '在庫リストの作成に失敗しました',
UPDATE_FAILED: '在庫リストの更新に失敗しました',
DELETE_FAILED: '在庫リストの削除に失敗しました',
BUY_FAILED: '在庫リストの購入処理に失敗しました',
};
// // タスク関連のエラーメッセージ
// export const TASK_ERRORS = {
// FETCH_FAILED: 'タスクの取得に失敗しました',
// CREATE_FAILED: 'タスクの作成に失敗しました',
// UPDATE_FAILED: 'タスクの更新に失敗しました',
// DELETE_FAILED: 'タスクの削除に失敗しました',
// };

@ -60,7 +60,7 @@ const AddDishes1: React.FC = () => {
InputLabelProps={{ style: { fontSize: "40px" }}}
style={{width: "80%" }}
InputProps={{ style: { fontSize: "40px"} }}
name="dish_name"
name="dishName"
// autoComplete="username"
autoFocus
value={dish}

@ -3,7 +3,7 @@
*
*/
import React, { useState, useEffect } from 'react';
import { taskApi, toBuyApi } from '../services/api';
import { toBuyApi } from '../services/api';
import {
Container,
Typography,
@ -31,8 +31,7 @@ import {
Add as AddIcon, Delete as DeleteIcon, ShoppingBasket as ShoppingBasketIcon,
SoupKitchen as SoupKitchenIcon
} from '@mui/icons-material';
import { Task } from '../types/types';
import { TASK_ERRORS } from '../constants/errorMessages';
import { STUFF_ERRORS } from '../constants/errorMessages';
import { GENERAL_ERRORS } from '../constants/errorMessages';
import CategoryDropDown from "../components/CategoryDropDown";
@ -42,7 +41,7 @@ const EMPTY_TASK = { id: 0, title: '', amount: 0, completed: false };
// 新規タスクの初期状態(データベース登録用)
const EMPTY_TASK_DATA_BASE = { id: 0, title: '', amount: 0, completed: false };
interface Empty_Task {
interface Stuff {
title: string; // 食材名
amount: number; // 食材の個数
completed: boolean; //
@ -51,9 +50,9 @@ interface Empty_Task {
const AddDishes2: React.FC = () => {
const receivedData = localStorage.getItem("dishName");
// タスク一覧の状態管理
const [tasks, setTasks] = useState<Task[]>([]);
const [tasks, setTasks] = useState<Stuff[]>([]);
const [addtasks, setAddTasks] = useState<Empty_Task[]>([]);
const [addtasks, setAddTasks] = useState<Stuff[]>([]);
// エラーメッセージの状態管理
const [error, setError] = useState(false);
// 新規タスク作成ダイアログの表示状態
@ -61,23 +60,23 @@ const AddDishes2: React.FC = () => {
// 新規タスクの入力内容
const [newTask, setNewTask] = useState(EMPTY_TASK);
// コンポーネントマウント時にタスク一覧を取得
useEffect(() => {
fetchTasks();
}, []);
// // コンポーネントマウント時にタスク一覧を取得
// useEffect(() => {
// fetchTasks();
// }, []);
/**
* APIからタスク一覧を取得する関数
* state(tasks)
*/
const fetchTasks = async () => {
try {
const tasks = await taskApi.getTasks();
setTasks(tasks);
} catch (error) {
console.error(`${TASK_ERRORS.FETCH_FAILED}:`, error);
}
};
// /**
// * APIからタスク一覧を取得する関数
// * 取得したタスクをstate(tasks)に設定
// */
// const fetchTasks = async () => {
// try {
// const tasks = await taskApi.getTasks();
// setTasks(tasks);
// } catch (error) {
// console.error(`${TASK_ERRORS.FETCH_FAILED}:`, error);
// }
// };
/**
*
* IDのタスクをAPIを通じて削除
@ -89,7 +88,7 @@ const AddDishes2: React.FC = () => {
setAddTasks(newAddTasks);
// fetchTasks(); // 削除後のタスク一覧を再取得
} catch (error) {
console.error(`${TASK_ERRORS.DELETE_FAILED}:`, error);
console.error(`${STUFF_ERRORS.DELETE_FAILED}:`, error);
}
};
@ -120,7 +119,7 @@ const AddDishes2: React.FC = () => {
setNewTask(EMPTY_TASK); // 入力内容をリセット
// fetchTasks(); // 作成後のタスク一覧を再取得
} catch (error) {
console.error(`${TASK_ERRORS.CREATE_FAILED}:`, error);
console.error(`${STUFF_ERRORS.CREATE_FAILED}:`, error);
}
};
@ -134,7 +133,7 @@ const AddDishes2: React.FC = () => {
setNewTask(EMPTY_TASK); // 入力内容をリセット
// fetchTasks(); // 作成後のタスク一覧を再取得
} catch (error) {
console.error(`${TASK_ERRORS.CREATE_FAILED}:`, error);
console.error(`${STUFF_ERRORS.CREATE_FAILED}:`, error);
}
};

@ -20,7 +20,7 @@ import {
Box,
} from '@mui/material';
import { TASK_ERRORS } from '../constants/errorMessages';
import { STOCK_ERRORS } from '../constants/errorMessages';
const StockPage: React.FC = () => {
@ -47,9 +47,10 @@ const StockPage: React.FC = () => {
const fetchStocks = async () => {
try {
const stocks = await stockApi.getStocks();
setStocks(stocks.stock_array);
console.log('Stocks=', stocks)
setStocks(stocks);
} catch (error) {
console.error(`${TASK_ERRORS.FETCH_FAILED}:`, error);
console.error(`${STOCK_ERRORS.FETCH_FAILED}:`, error);
}
};
@ -139,14 +140,14 @@ const StockPage: React.FC = () => {
.filter(stock => stock.category == "乳製品") // 乳製品だけ抽出
.map(stock => (
<TableRow
key={stock.stock_id}
onClick={() => handleRowClick(stock.stock_id)}
style={{ backgroundColor: selectedRow === stock.stock_id ? "yellow" : "white", cursor: "pointer" }}>
<TableCell>{stock.stuff_name}</TableCell>
key={stock.stockId}
onClick={() => handleRowClick(stock.stockId)}
style={{ backgroundColor: selectedRow === stock.stockId ? "yellow" : "white", cursor: "pointer" }}>
<TableCell>{stock.stuffName}</TableCell>
<TableCell>{stock.amount}</TableCell>
<TableCell>{stock.price}</TableCell>
<TableCell>{formatDate(stock.exp_date)}</TableCell>
<TableCell>{formatDate(stock.buy_date)}</TableCell>
<TableCell>{formatDate(stock.expDate)}</TableCell>
<TableCell>{formatDate(stock.buyDate)}</TableCell>
</TableRow>
))}
</TableBody>
@ -156,12 +157,12 @@ const StockPage: React.FC = () => {
</div>
<Typography variant="h4" component="h1" gutterBottom>
</Typography>
{/* 肉・魚一覧表示エリア - 青い背景のコンテナ */}
{/* 魚・肉一覧表示エリア - 青い背景のコンテナ */}
<div style={{ border: '3px solid black', borderRadius: '8px', backgroundColor: '#add8e6', height: 'auto', padding: '20px', marginBottom: "20px" }}>
{stocks.filter(stock => stock.category == "肉" || stock.category == "魚").length === 0 ? null : (
{stocks.filter(stock => stock.category == "魚・肉").length === 0 ? null : (
<TableContainer component={Paper}>
<Table>
<TableHead sx={{ backgroundColor: "#dcdcdc", color: "#333" }}>
@ -175,17 +176,17 @@ const StockPage: React.FC = () => {
</TableHead>
<TableBody>
{stocks
.filter(stock => stock.category == "肉" || stock.category == "魚") // 肉と魚だけ抽出
.filter(stock => stock.category == "魚・肉") // 「魚・肉」だけ抽出
.map(stock => (
<TableRow
key={stock.stock_id}
onClick={() => handleRowClick(stock.stock_id)}
style={{ backgroundColor: selectedRow === stock.stock_id ? "yellow" : "white", cursor: "pointer" }}>
<TableCell>{stock.stuff_name}</TableCell>
key={stock.stockId}
onClick={() => handleRowClick(stock.stockId)}
style={{ backgroundColor: selectedRow === stock.stockId ? "yellow" : "white", cursor: "pointer" }}>
<TableCell>{stock.stuffName}</TableCell>
<TableCell>{stock.amount}</TableCell>
<TableCell>{stock.price}</TableCell>
<TableCell>{formatDate(stock.exp_date)}</TableCell>
<TableCell>{formatDate(stock.buy_date)}</TableCell>
<TableCell>{formatDate(stock.expDate)}</TableCell>
<TableCell>{formatDate(stock.buyDate)}</TableCell>
</TableRow>
))}
</TableBody>
@ -217,14 +218,14 @@ const StockPage: React.FC = () => {
.filter(stock => stock.category == "野菜") // 野菜だけ抽出
.map(stock => (
<TableRow
key={stock.stock_id}
onClick={() => handleRowClick(stock.stock_id)}
style={{ backgroundColor: selectedRow === stock.stock_id ? "yellow" : "white", cursor: "pointer" }}>
<TableCell>{stock.stuff_name}</TableCell>
key={stock.stockId}
onClick={() => handleRowClick(stock.stockId)}
style={{ backgroundColor: selectedRow === stock.stockId ? "yellow" : "white", cursor: "pointer" }}>
<TableCell>{stock.stuffName}</TableCell>
<TableCell>{stock.amount}</TableCell>
<TableCell>{stock.price}</TableCell>
<TableCell>{formatDate(stock.exp_date)}</TableCell>
<TableCell>{formatDate(stock.buy_date)}</TableCell>
<TableCell>{formatDate(stock.expDate)}</TableCell>
<TableCell>{formatDate(stock.buyDate)}</TableCell>
</TableRow>
))}
</TableBody>
@ -255,14 +256,14 @@ const StockPage: React.FC = () => {
.filter(stock => stock.category == "調味料") // 調味料だけ抽出
.map(stock => (
<TableRow
key={stock.stock_id}
onClick={() => handleRowClick(stock.stock_id)}
style={{ backgroundColor: selectedRow === stock.stock_id ? "yellow" : "white", cursor: "pointer" }}>
<TableCell>{stock.stuff_name}</TableCell>
key={stock.stockId}
onClick={() => handleRowClick(stock.stockId)}
style={{ backgroundColor: selectedRow === stock.stockId ? "yellow" : "white", cursor: "pointer" }}>
<TableCell>{stock.stuffName}</TableCell>
<TableCell>{stock.amount}</TableCell>
<TableCell>{stock.price}</TableCell>
<TableCell>{formatDate(stock.exp_date)}</TableCell>
<TableCell>{formatDate(stock.buy_date)}</TableCell>
<TableCell>{formatDate(stock.expDate)}</TableCell>
<TableCell>{formatDate(stock.buyDate)}</TableCell>
</TableRow>
))}
</TableBody>
@ -293,14 +294,14 @@ const StockPage: React.FC = () => {
.filter(stock => stock.category == "その他") // その他だけ抽出
.map(stock => (
<TableRow
key={stock.stock_id}
onClick={() => handleRowClick(stock.stock_id)}
style={{ backgroundColor: selectedRow === stock.stock_id ? "yellow" : "white", cursor: "pointer" }}>
<TableCell>{stock.stuff_name}</TableCell>
key={stock.stockId}
onClick={() => handleRowClick(stock.stockId)}
style={{ backgroundColor: selectedRow === stock.stockId ? "yellow" : "white", cursor: "pointer" }}>
<TableCell>{stock.stuffName}</TableCell>
<TableCell>{stock.amount}</TableCell>
<TableCell>{stock.price}</TableCell>
<TableCell>{formatDate(stock.exp_date)}</TableCell>
<TableCell>{formatDate(stock.buy_date)}</TableCell>
<TableCell>{formatDate(stock.expDate)}</TableCell>
<TableCell>{formatDate(stock.buyDate)}</TableCell>
</TableRow>
))}
</TableBody>

@ -33,22 +33,28 @@ import {
Add as AddIcon, Delete as DeleteIcon, ShoppingBasket as ShoppingBasketIcon,
SoupKitchen as SoupKitchenIcon
} from '@mui/icons-material';
import { Task, ToBuy, Stuff } from '../types/types';
import { TASK_ERRORS } from '../constants/errorMessages';
import { ToBuy, Stuff, Stock } from '../types/types';
import { TOBUY_ERRORS } from '../constants/errorMessages';
//import { FaCarrot } from "react-icons/fa6"; //エラー起きる いったん保留
// 新規タスクの初期状態
const EMPTY_TASK: Omit<ToBuy, 'tobuy_id' | 'stuff_id'> & { stuff_id: number | null, category: string } & { newAddition: boolean } = {
stuff_id: null,
stuff_name: '',
const EMPTY_TOBUY: Omit<ToBuy, 'tobuyId' | 'stuffId'> & { stuffId: number | null, category: string } & { newAddition: boolean } = {
stuffId: null,
stuffName: '',
amount: 0,
shop: '',
category: '',
newAddition: false,
}
const EMPTY_STOCK = {
price: 0,
buyDate: '',
expDate: '',
}
const TaskListPage: React.FC = () => {
// タスク一覧の状態管理
const [tobuys, setToBuys] = useState<ToBuy[]>([]);
@ -58,12 +64,14 @@ const TaskListPage: React.FC = () => {
//在庫登録ダイアログの表示状態
const [openInfoDialog, setOpenInfoDialog] = useState(false);
const [selectedTask, setSelectedTask] = useState<ToBuy["tobuy_id"]>(0);
const [selectedTask, setSelectedTask] = useState<ToBuy["tobuyId"]>(0);
const [newToBuy, setNewToBuy] = useState(EMPTY_TASK);
const [newToBuy, setNewToBuy] = useState(EMPTY_TOBUY);
const [stuffs, setStuffs] = useState<Stuff[]>([]);
const [newStock, setNewStock] = useState(EMPTY_STOCK);
// コンポーネントマウント時にタスク一覧を取得
useEffect(() => {
@ -77,16 +85,16 @@ const TaskListPage: React.FC = () => {
const fetchTasks = async () => {
try {
const tobuys = await toBuyApi.getToBuys();
setToBuys(tobuys.tobuy_array);
setToBuys(tobuys);
} catch (error) {
console.error(`${TASK_ERRORS.FETCH_FAILED}:`, error);
console.error(`${TOBUY_ERRORS.FETCH_FAILED}:`, error);
}
};
const onChangeCategory = async (category: string) => {
setNewToBuy({ ...newToBuy, category })
const result = await stuffApi.getStuffs(category)
setStuffs(result.stuff_array)
setStuffs(result)
}
// /**
@ -112,9 +120,22 @@ const TaskListPage: React.FC = () => {
const handleDeleteTask = async (toBuyId: number) => {
try {
await toBuyApi.deleteToBuy(toBuyId);
fetchTasks(); // 削除後のタスク一覧を再取得
fetchTasks(); // 削除後の買うもの一覧を再取得
} catch (error) {
console.error(`${TOBUY_ERRORS.DELETE_FAILED}:`, error);
}
};
/**
*
*/
const handleBuy = async (tobuyId: number) => {
try {
const today = new Date().toISOString().substring(0, 10);
await toBuyApi.buy({tobuyId, ...newStock, lastUpdate: today});
fetchTasks(); // 削除後の買うもの一覧を再取得
} catch (error) {
console.error(`${TASK_ERRORS.DELETE_FAILED}:`, error);
console.error(`${TOBUY_ERRORS.BUY_FAILED}:`, error);
}
};
@ -126,15 +147,15 @@ const TaskListPage: React.FC = () => {
const handleCreateTask = async () => {
try {
if (newToBuy.newAddition) {
newToBuy.stuff_id = null;
newToBuy.stuffId = null;
}
console.log(newToBuy)
await toBuyApi.addToBuy(newToBuy);
setOpenDialog(false); // ダイアログを閉じる
setNewToBuy(EMPTY_TASK); // 入力内容をリセット
setNewToBuy(EMPTY_TOBUY); // 入力内容をリセット
fetchTasks(); // 作成後のタスク一覧を再取得
} catch (error) {
console.error(`${TASK_ERRORS.CREATE_FAILED}:`, error);
console.error(`${TOBUY_ERRORS.CREATE_FAILED}:`, error);
}
};
@ -149,7 +170,7 @@ const TaskListPage: React.FC = () => {
{/* タスク一覧をマップして各タスクをリストアイテムとして表示 */}
{tobuys && tobuys.map((tobuy) => (
<ListItem
key={tobuy.tobuy_id}
key={tobuy.tobuyId}
sx={{
bgcolor: 'background.paper',
mb: 1,
@ -166,7 +187,7 @@ const TaskListPage: React.FC = () => {
*/}
{/* タスクのタイトルと説明 - 完了状態に応じて取り消し線を表示 */}
<ListItemText
primary={`${tobuy.stuff_name} × ${tobuy.amount}`}
primary={`${tobuy.stuffName} × ${tobuy.amount}`}
//secondary={tobuy.amount}
sx={{
textDecoration: false ? 'line-through' : 'none',
@ -180,8 +201,8 @@ const TaskListPage: React.FC = () => {
aria-label="食材情報追加"
onClick={() => {
setOpenInfoDialog(true)
setSelectedTask(tobuy.tobuy_id)
// handleDeleteTask(tobuy.tobuy_id)
setSelectedTask(tobuy.tobuyId)
// handleDeleteTask(tobuy.tobuyId)
}}
>
<ShoppingBasketIcon />
@ -205,7 +226,7 @@ const TaskListPage: React.FC = () => {
<IconButton
edge="end"
aria-label="delete"
onClick={() => handleDeleteTask(tobuy.tobuy_id)}
onClick={() => handleDeleteTask(tobuy.tobuyId)}
>
<DeleteIcon />
</IconButton>
@ -273,12 +294,12 @@ const TaskListPage: React.FC = () => {
<InputLabel id="demo-simple-select-label"></InputLabel>
<Select
labelId="demo-simple-select-label"
value={newToBuy.stuff_id}
onChange={(e) => setNewToBuy({ ...newToBuy, stuff_id: Number(e.target.value) })}
value={newToBuy.stuffId}
onChange={(e) => setNewToBuy({ ...newToBuy, stuffId: Number(e.target.value) })}
>
{stuffs.map((stuff) => (
<MenuItem key={stuff.stuff_id} value={stuff.stuff_id}>
{stuff.stuff_name}
<MenuItem key={stuff.stuffId} value={stuff.stuffId}>
{stuff.stuffName}
</MenuItem>
))}
</Select>
@ -290,8 +311,8 @@ const TaskListPage: React.FC = () => {
margin="dense"
label="材料名"
fullWidth
value={newToBuy.stuff_name}
onChange={(e) => setNewToBuy({ ...newToBuy, stuff_name: e.target.value })}
value={newToBuy.stuffName}
onChange={(e) => setNewToBuy({ ...newToBuy, stuffName: e.target.value })}
sx={{ marginBottom: 2 }}
/>}
{/* 数量入力フィールド */}
@ -333,6 +354,8 @@ const TaskListPage: React.FC = () => {
margin="dense"
label="価格"
fullWidth
value={newStock.price}
onChange={(e) => setNewStock({...newStock, price: parseInt(e.target.value)})}
/>
{/* 消費・賞味期限入力フィールド */}
<TextField
@ -340,6 +363,8 @@ const TaskListPage: React.FC = () => {
label="消費・賞味期限(yyyy/MM/dd)"
fullWidth
multiline
value={newStock.expDate}
onChange={(e) => setNewStock({...newStock, expDate: e.target.value})}
/>
{/* 購入日入力フィールド */}
<TextField
@ -347,6 +372,8 @@ const TaskListPage: React.FC = () => {
label="購入日(yyyy/MM/dd)"
fullWidth
multiline
value={newStock.buyDate}
onChange={(e) => setNewStock({...newStock, buyDate: e.target.value})}
/>
</Box>
</DialogContent>
@ -354,9 +381,9 @@ const TaskListPage: React.FC = () => {
<Button onClick={() => setOpenInfoDialog(false)}></Button>
<Button onClick={() => {
if (selectedTask) {
handleDeleteTask(selectedTask)
handleBuy(selectedTask)
setOpenInfoDialog(false)
setNewToBuy(EMPTY_TASK); // 入力内容をリセット
setNewToBuy(EMPTY_TOBUY); // 入力内容をリセット
}
}}
variant="contained">

@ -3,8 +3,8 @@
* APIとの通信を担当するモジュール
*
*/
import { LoginCredentials, RegisterCredentials, AuthResponse, Task, ToBuy, Stuff, Stock } from '../types/types';
import { AUTH_ERRORS, TASK_ERRORS, TOBUY_ERRORS } from '../constants/errorMessages';
import { LoginCredentials, RegisterCredentials, AuthResponse, /* Task, */ ToBuy, Stuff, Stock, Recipes } from '../types/types';
import { AUTH_ERRORS, TOBUY_ERRORS, STOCK_ERRORS } from '../constants/errorMessages';
// APIのベースURL - 環境変数から取得するか、デフォルト値を使用
const API_BASE_URL = process.env.REACT_APP_API_BASE_URL || 'http://localhost:8080';
@ -92,7 +92,7 @@ export const toBuyApi = {
*
* @returns
*/
getToBuys: async (): Promise<{ "tobuy_array": ToBuy[] }> => {
getToBuys: async (): Promise<ToBuy[]> => {
const response = await fetch(`${API_BASE_URL}/api/tobuy/get`, {
headers: getHeaders(), // 認証トークンを含むヘッダー
});
@ -101,27 +101,7 @@ export const toBuyApi = {
throw new Error(TOBUY_ERRORS.FETCH_FAILED);
}
const tobuy_array = await response.json();
return {tobuy_array};
// return {
// "tobuy_array": [
// {
// "tobuy_id": 1,
// "stuff_id": 2,
// "stuff_name": "じゃがいも",
// "amount": 3,
// "shop": "shopXXX"
// },
// {
// "tobuy_id": 2,
// "stuff_id": 5,
// "stuff_name": "にんじん",
// "amount": 1
// }
// ]
// }
return await response.json();
},
/**
@ -129,7 +109,7 @@ export const toBuyApi = {
* @param tobuy
* @returns
*/
addToBuy: async (tobuy: Omit<ToBuy, 'stuff_id' | 'tobuy_id'> & { stuff_id: number | null, category: string }): Promise<any> => {
addToBuy: async (tobuy: Omit<ToBuy, 'stuffId' | 'tobuyId'> & { stuffId: number | null, category: string }): Promise<any> => {
const response = await fetch(`${API_BASE_URL}/api/tobuy/add`, {
method: 'POST',
headers: getHeaders(),
@ -140,13 +120,13 @@ export const toBuyApi = {
throw new Error(TOBUY_ERRORS.CREATE_FAILED);
}
// return response.json();
return {result: true}
return response.json();
// return {result: true}
// return {
// "result": true,
// "tobuy_id": 1,
// "stuff_id": 6,
// "tobuyId": 1,
// "stuffId": 6,
// "message": "追加に成功しました",
// }
@ -156,11 +136,11 @@ export const toBuyApi = {
*
* @param id ID
*/
deleteToBuy: async (tobuy_id: number): Promise<{ result: boolean }> => {
deleteToBuy: async (tobuyId: number): Promise<{ result: boolean }> => {
const response = await fetch(`${API_BASE_URL}/api/tobuy/delete`, {
method: 'DELETE',
headers: getHeaders(),
body: JSON.stringify({tobuy_id}),
body: JSON.stringify({tobuyId}),
});
if (!response.ok) {
@ -173,47 +153,77 @@ export const toBuyApi = {
// "result": true
// }
},
/**
*
*/
buy: async (req: {tobuyId: number, price: number, expDate: string, buyDate: string, lastUpdate: string}): Promise<{ result: boolean }> => {
console.log('req: ', req)
req.buyDate = makeDateObject(req.buyDate)?.toISOString()?.substring(0, 10) || ''
req.expDate = makeDateObject(req.expDate)?.toISOString()?.substring(0, 10) || ''
const response = await fetch(`${API_BASE_URL}/api/tobuy/buy`, {
method: 'POST',
headers: getHeaders(),
body: JSON.stringify(req),
});
if (!response.ok) {
throw new Error(TOBUY_ERRORS.BUY_FAILED);
}
return response.json()
},
}
export const stuffApi = {
getStuffs: async (category: string): Promise<{ stuff_array: Stuff[] }> => {
const data = [
{ "stuff_id": 1, "stuff_name": "牛乳", "category": "乳製品" },
{ "stuff_id": 2, "stuff_name": "ヨーグルト", "category": "乳製品" },
{ "stuff_id": 3, "stuff_name": "チーズ", "category": "乳製品" },
{ "stuff_id": 4, "stuff_name": "バター", "category": "乳製品" },
{ "stuff_id": 5, "stuff_name": "生クリーム", "category": "乳製品" },
{ "stuff_id": 6, "stuff_name": "鮭", "category": "魚・肉" },
{ "stuff_id": 7, "stuff_name": "鶏むね肉", "category": "魚・肉" },
{ "stuff_id": 8, "stuff_name": "豚バラ肉", "category": "魚・肉" },
{ "stuff_id": 9, "stuff_name": "牛ひき肉", "category": "魚・肉" },
{ "stuff_id": 10, "stuff_name": "まぐろ", "category": "魚・肉" },
{ "stuff_id": 11, "stuff_name": "にんじん", "category": "野菜" },
{ "stuff_id": 12, "stuff_name": "キャベツ", "category": "野菜" },
{ "stuff_id": 13, "stuff_name": "ほうれん草", "category": "野菜" },
{ "stuff_id": 14, "stuff_name": "玉ねぎ", "category": "野菜" },
{ "stuff_id": 15, "stuff_name": "ピーマン", "category": "野菜" },
{ "stuff_id": 16, "stuff_name": "醤油", "category": "調味料" },
{ "stuff_id": 17, "stuff_name": "味噌", "category": "調味料" },
{ "stuff_id": 18, "stuff_name": "塩", "category": "調味料" },
{ "stuff_id": 19, "stuff_name": "砂糖", "category": "調味料" },
{ "stuff_id": 20, "stuff_name": "酢", "category": "調味料" },
{ "stuff_id": 21, "stuff_name": "米", "category": "その他" },
{ "stuff_id": 22, "stuff_name": "パスタ", "category": "その他" },
{ "stuff_id": 23, "stuff_name": "小麦粉", "category": "その他" },
{ "stuff_id": 24, "stuff_name": "卵", "category": "その他" },
{ "stuff_id": 25, "stuff_name": "豆腐", "category": "その他" }
]
const filtered = data.filter(stuff => stuff.category == category)
return {
"stuff_array": filtered
getStuffs: async (category: string): Promise<Stuff[]> => {
const data = await fetch(`${API_BASE_URL}/api/stuff/get?category=${encodeURIComponent(category)}`, {
headers: getHeaders(), // 認証トークンを含むヘッダー
});
if (!data.ok) {
throw new Error(`Failed to fetch stuffs for category ${category}`);
}
return data.json();
// const data = [
// { stuffId: 1, stuffName: "牛乳", category: "乳製品" },
// { stuffId: 2, stuffName: "ヨーグルト", category: "乳製品" },
// { stuffId: 3, stuffName: "チーズ", category: "乳製品" },
// { stuffId: 4, stuffName: "バター", category: "乳製品" },
// { stuffId: 5, stuffName: "生クリーム", category: "乳製品" },
// { stuffId: 6, stuffName: "鮭", category: "魚・肉" },
// { stuffId: 7, stuffName: "鶏むね肉", category: "魚・肉" },
// { stuffId: 8, stuffName: "豚バラ肉", category: "魚・肉" },
// { stuffId: 9, stuffName: "牛ひき肉", category: "魚・肉" },
// { stuffId: 10, stuffName: "まぐろ", category: "魚・肉" },
// { stuffId: 11, stuffName: "にんじん", category: "野菜" },
// { stuffId: 12, stuffName: "キャベツ", category: "野菜" },
// { stuffId: 13, stuffName: "ほうれん草", category: "野菜" },
// { stuffId: 14, stuffName: "玉ねぎ", category: "野菜" },
// { stuffId: 15, stuffName: "ピーマン", category: "野菜" },
// { stuffId: 16, stuffName: "醤油", category: "調味料" },
// { stuffId: 17, stuffName: "味噌", category: "調味料" },
// { stuffId: 18, stuffName: "塩", category: "調味料" },
// { stuffId: 19, stuffName: "砂糖", category: "調味料" },
// { stuffId: 20, stuffName: "酢", category: "調味料" },
// { stuffId: 21, stuffName: "米", category: "その他" },
// { stuffId: 22, stuffName: "パスタ", category: "その他" },
// { stuffId: 23, stuffName: "小麦粉", category: "その他" },
// { stuffId: 24, stuffName: "卵", category: "その他" },
// { stuffId: 25, stuffName: "豆腐", category: "その他" }
// ]
// const filtered = data.filter(stuff => stuff.category == category)
// return filtered
}
}
@ -223,139 +233,217 @@ export const stockApi = {
*
* @returns
*/
getStocks: async (): Promise<{ "stock_array": Stock[] }> => {
// const response = await fetch(`${API_BASE_URL}/api/tobuy/get`, {
// headers: getHeaders(), // 認証トークンを含むヘッダー
// });
// if (!response.ok) {
// throw new Error(TASK_ERRORS.FETCH_FAILED);
// }
getStocks: async (): Promise<Stock[]> => {
const response = await fetch(`${API_BASE_URL}/api/stocks/get`, {
headers: getHeaders(), // 認証トークンを含むヘッダー
});
// return response.json();
return {
"stock_array": [
{
"stock_id": 1,
"stuff_id": 10,
"stuff_name": "豚肉",
"amount": 100,
"price": 200,
"buy_date": "2025-05-18T09:00:00.000Z",
"last_update": "2025-05-18T09:00:00.000Z",
"exp_date": "2025-05-19T10:15:00.000Z",
"category": "肉"
},
{
"stock_id": 2,
"stuff_id": 1,
"stuff_name": "トマト",
"amount": 10,
"price": 200,
"buy_date": "2025-05-18T09:00:00.000Z",
"last_update": "2025-05-18T09:00:00.000Z",
"exp_date": "2025-05-19T10:15:00.000Z",
"category": "野菜"
}
]
if (!response.ok) {
throw new Error(STOCK_ERRORS.FETCH_FAILED);
}
return response.json();
},
}
/**
* ()
* API機能を提供するオブジェクト
*
* API機能を提供するオブジェクト
*/
export const taskApi = {
export const recipeApi = {
/**
*
* @returns
*
* @param recipeData
* @returns
*/
getTasks: async (): Promise<Task[]> => {
const response = await fetch(`${API_BASE_URL}/api/tasks`, {
addRecipe: async (recipeData: Recipes): Promise<{
result: string;
recipe_id: number;
message: string;
}> => {
const response = await fetch(`${API_BASE_URL}/api/recipes/add`, {
method: 'POST',
headers: getHeaders(), // 認証トークンを含むヘッダー
body: JSON.stringify(recipeData),
});
if (!response.ok) {
throw new Error(TASK_ERRORS.FETCH_FAILED);
const errorData = await response.json().catch(() => null);
throw new Error(errorData?.message);
}
return response.json();
},
/**
* IDのタスクを取得
* @param id ID
* @returns
*
* @returns
*/
getTask: async (id: number): Promise<Task> => {
const response = await fetch(`${API_BASE_URL}/api/tasks/${id}`, {
headers: getHeaders(),
getAllRecipes: async (): Promise<Array<{
recipeId: number;
recipeName: string;
summary: number;
}>> => {
const response = await fetch(`${API_BASE_URL}/api/recipes/getAll`, {
method: 'GET',
headers: getHeaders(), // 認証トークンを含むヘッダー
});
if (!response.ok) {
throw new Error(TASK_ERRORS.FETCH_FAILED);
const errorData = await response.json().catch(() => null);
throw new Error(
errorData?.message
);
}
return response.json();
},
/**
*
* @param task ,
* @returns
*/
addStuff: async (task: Omit<Task, 'userId' | 'createdAt' | 'price' | 'buyDate' | 'expirationDate' | 'newAddition'>): Promise<Task> => {
const response = await fetch(`${API_BASE_URL}/api/tubuy/add`, {
method: 'POST',
*
* @param recipeId ID
* @returns
*/
getById: async (recipeId: number): Promise<{
recipeId: number;
recipeName: string;
summary: string;
stuffs: Array<{ stuffId: number; stuffName: string; amount: number }>;
}> => {
const response = await fetch(`${API_BASE_URL}/api/recipes/getById?recipeId=${recipeId}`, {
method: 'GET',
headers: getHeaders(),
body: JSON.stringify(task),
});
if (!response.ok) {
throw new Error(TASK_ERRORS.CREATE_FAILED);
const errorData = await response.json().catch(() => null);
throw new Error(errorData?.message);
}
return response.json();
},
/**
*
* @param id ID
* @param task
* @returns
*
* @param recipeData
* @returns /
*/
updateTask: async (id: number, task: Partial<Task>): Promise<Task> => {
const response = await fetch(`${API_BASE_URL}/api/tasks/${id}`, {
method: 'PUT',
update: async (recipeData: { recipeId: number } & Recipes): Promise<{ result: boolean; message: string }> => {
const response = await fetch(`${API_BASE_URL}/api/recipes/update`, {
method: 'POST',
headers: getHeaders(),
body: JSON.stringify(task),
body: JSON.stringify(recipeData),
});
if (!response.ok) {
throw new Error(TASK_ERRORS.UPDATE_FAILED);
const errorData = await response.json().catch(() => null);
throw new Error(errorData?.message);
}
return response.json();
},
};
/**
*
* @param id ID
*/
deleteTask: async (id: number): Promise<void> => {
const response = await fetch(`${API_BASE_URL}/api/tasks/${id}`, {
method: 'DELETE',
headers: getHeaders(),
});
if (!response.ok) {
throw new Error(TASK_ERRORS.DELETE_FAILED);
function makeDateObject(dateStr: String) {
// 例: '2025/06/15' または '2025-06-15' を '2025-06-15' に変換
const parts = dateStr.split(/[-\/]/); // ハイフンかスラッシュで分割
if (parts.length === 3) {
return new Date(parts[0] + '-' + parts[1] + '-' + parts[2]);
}
},
};
return null; // 無効な日付の場合
}
// /**
// * (サンプル,実際には不要)
// * タスク管理関連のAPI機能を提供するオブジェクト
// * タスクの取得、作成、更新、削除などの機能を含む
// */
// export const taskApi = {
// /**
// * 全タスクを取得
// * @returns タスク一覧
// */
// getTasks: async (): Promise<Task[]> => {
// const response = await fetch(`${API_BASE_URL}/api/tasks`, {
// headers: getHeaders(), // 認証トークンを含むヘッダー
// });
// if (!response.ok) {
// throw new Error(TASK_ERRORS.FETCH_FAILED);
// }
// return response.json();
// },
// /**
// * 指定IDのタスクを取得
// * @param id タスクID
// * @returns 単一のタスク情報
// */
// getTask: async (id: number): Promise<Task> => {
// const response = await fetch(`${API_BASE_URL}/api/tasks/${id}`, {
// headers: getHeaders(),
// });
// if (!response.ok) {
// throw new Error(TASK_ERRORS.FETCH_FAILED);
// }
// return response.json();
// },
// /**
// * 新規材料を作成
// * @param task 作成するタスク情報(価格,作成日時、更新日時は除外)
// * @returns 作成されたタスク情報
// */
// addStuff: async (task: Omit<Task, 'userId' | 'createdAt' | 'price' | 'buyDate' | 'expirationDate' | 'newAddition'>): Promise<Task> => {
// const response = await fetch(`${API_BASE_URL}/api/tubuy/add`, {
// method: 'POST',
// headers: getHeaders(),
// body: JSON.stringify(task),
// });
// if (!response.ok) {
// throw new Error(TASK_ERRORS.CREATE_FAILED);
// }
// return response.json();
// },
// /**
// * タスクを更新
// * @param id 更新対象のタスクID
// * @param task 更新するタスク情報(部分的な更新も可能)
// * @returns 更新後のタスク情報
// */
// updateTask: async (id: number, task: Partial<Task>): Promise<Task> => {
// const response = await fetch(`${API_BASE_URL}/api/tasks/${id}`, {
// method: 'PUT',
// headers: getHeaders(),
// body: JSON.stringify(task),
// });
// if (!response.ok) {
// throw new Error(TASK_ERRORS.UPDATE_FAILED);
// }
// return response.json();
// },
// /**
// * タスクを削除
// * @param id 削除対象のタスクID
// */
// deleteTask: async (id: number): Promise<void> => {
// const response = await fetch(`${API_BASE_URL}/api/tasks/${id}`, {
// method: 'DELETE',
// headers: getHeaders(),
// });
// if (!response.ok) {
// throw new Error(TASK_ERRORS.DELETE_FAILED);
// }
// },
// };

@ -1,37 +1,37 @@
/**
*
*
*/
export interface Task {
id: number; // タスクの一意識別子
stuff_name: string; // タスクのタイトル
amount: number; //材料の数量
price: number; //材料の値段
buyDate:Date; //購入日時
expirationDate: Date; //賞味・消費期限
//description?: string; // タスクの詳細説明(任意)
completed: boolean; // タスクの完了状態
userId: number; // タスクの所有者ID
createdAt: string; // タスク作成日時
newAddition: boolean //材料を新規追加するかどうか
//updatedAt: string; // タスク更新日時
}
// /**
// * タスク情報を表す型定義
// * タスクの基本情報と状態を管理
// */
// export interface Task {
// id: number; // タスクの一意識別子
// stuffName: string; // タスクのタイトル
// amount: number; //材料の数量
// price: number; //材料の値段
// buyDate:Date; //購入日時
// expirationDate: Date; //賞味・消費期限
// //description?: string; // タスクの詳細説明(任意)
// completed: boolean; // タスクの完了状態
// userId: number; // タスクの所有者ID
// createdAt: string; // タスク作成日時
// newAddition: boolean //材料を新規追加するかどうか
// //updatedAt: string; // タスク更新日時
// }
/**
*
*
*/
export interface ToBuy {
tobuy_id: number,
stuff_id: number,
stuff_name: string,
tobuyId: number,
stuffId: number,
stuffName: string,
amount: number,
shop?: string,
}
export interface Stuff {
stuff_id: number,
stuff_name: string,
stuffId: number,
stuffName: string,
category: string,
}
@ -40,14 +40,14 @@ export interface Stuff {
*
*/
export interface Stock {
stock_id: number,
stuff_id: number,
stuff_name: string,
stockId: number,
stuffId: number,
stuffName: string,
amount: number,
price: number,
buy_date: string,
last_update: string,
exp_date: string,
buyDate: string,
lastUpdate: string,
expDate: string,
category: string,
}
@ -87,3 +87,20 @@ export interface RegisterCredentials {
password: string; // パスワード
}
/**
*
*
*/
export interface Recipes {
recipeName: string;// レシピ名
summary: string;// レシピ概要
// 材料リスト(直接配列として内包)
stuffAndAmountArray : Array<{
// 既存材料IDまたは新規作成情報のどちらか一方のみ必要
stuffId?: number; // 既存材料ID(オプション)
stuffName?: string;// 新規材料名(オプション)
category?: string; // 新規材料カテゴリ(オプション)
amount?: number; // 使用量(必須)
}>;
}
Loading…
Cancel
Save