Merge branch 'develop-backend' into develop-frontend

feature-frontend-adddishes-logic^2
Masaharu.Kato 9 months ago
commit ca3b06b24a
  1. 16
      .vscode/launch.json
  2. 107
      backend/src/main/java/com/example/todoapp/controller/StocksController.java
  3. 64
      backend/src/main/java/com/example/todoapp/controller/StuffsController.java
  4. 76
      backend/src/main/java/com/example/todoapp/controller/ToBuysController.java
  5. 14
      backend/src/main/java/com/example/todoapp/dto/BuyRequestDTO.java
  6. 4
      backend/src/main/java/com/example/todoapp/dto/DeleteToBuyRequest.java
  7. 54
      backend/src/main/java/com/example/todoapp/dto/ResponseStockDTO.java
  8. 46
      backend/src/main/java/com/example/todoapp/dto/StockDTO.java
  9. 42
      backend/src/main/java/com/example/todoapp/dto/StuffsDTO.java
  10. 6
      backend/src/main/java/com/example/todoapp/dto/ToBuyResponse.java
  11. 28
      backend/src/main/java/com/example/todoapp/dto/ToBuysDTO.java
  12. 19
      backend/src/main/java/com/example/todoapp/model/RecipeStuffs.java
  13. 7
      backend/src/main/java/com/example/todoapp/model/Recipes.java
  14. 47
      backend/src/main/java/com/example/todoapp/model/Stocks.java
  15. 10
      backend/src/main/java/com/example/todoapp/model/Stuffs.java
  16. 2
      backend/src/main/java/com/example/todoapp/model/Task.java
  17. 25
      backend/src/main/java/com/example/todoapp/model/ToBuys.java
  18. 62
      backend/src/main/java/com/example/todoapp/repository/StocksRepository.java
  19. 21
      backend/src/main/java/com/example/todoapp/repository/StuffsRepository.java
  20. 24
      backend/src/main/java/com/example/todoapp/repository/ToBuysRepository.java
  21. 112
      backend/src/main/java/com/example/todoapp/service/StocksService.java
  22. 39
      backend/src/main/java/com/example/todoapp/service/StuffsService.java
  23. 134
      backend/src/main/java/com/example/todoapp/service/ToBuysService.java
  24. 35
      frontend/src/constants/errorMessages.ts
  25. 2
      frontend/src/pages/AddDishes1.tsx
  26. 49
      frontend/src/pages/AddDishes2.tsx
  27. 75
      frontend/src/pages/StockPage.tsx
  28. 83
      frontend/src/pages/TaskListPage.tsx
  29. 366
      frontend/src/services/api.ts
  30. 58
      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,107 @@
package com.example.todoapp.controller;
import com.example.todoapp.dto.ResponseStockDTO;
import com.example.todoapp.dto.StockDTO;
import com.example.todoapp.model.Stocks;
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.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
/**
* 在庫管理のコントローラー
* <p>
* このコントローラーは在庫の取得作成更新削除などの
* エンドポイントを提供しますすべてのエンドポイントは認証が必要です
* </p>
*/
@RestController
@RequestMapping("/stocks")
public class StocksController {
@Autowired
private StocksService stockService;
/**
* ログインユーザーのすべての在庫を取得する
*
* @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("/{id}")
public ResponseEntity<StockDTO> updateStock(
Authentication authentication,
@PathVariable("id") Long stockId,
@Valid @RequestBody Stocks stockDetails) {
Stocks updatedStock = stockService.updateStocks(authentication.getName(), stockId, stockDetails);
return ResponseEntity.ok(StockDTO.fromEntity(updatedStock));
}
/**
* 指定されたIDの在庫を削除する
*
* @param authentication 認証情報
* @param taskId 削除する在庫のID
* @return 空のレスポンス
*/
@DeleteMapping("/{id}")
public ResponseEntity<?> deleteStock(
Authentication authentication,
@PathVariable("id") Long stockId) {
stockService.deleteStock(authentication.getName(), stockId);
return ResponseEntity.ok().build();
}
}

@ -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,12 +7,15 @@
package com.example.todoapp.controller; package com.example.todoapp.controller;
import com.example.todoapp.dto.BuyRequestDTO;
import com.example.todoapp.dto.DeleteToBuyRequest; import com.example.todoapp.dto.DeleteToBuyRequest;
import com.example.todoapp.dto.ToBuyResponse; import com.example.todoapp.dto.ToBuyResponse;
import com.example.todoapp.dto.ToBuysDTO; import com.example.todoapp.dto.ToBuysDTO;
import com.example.todoapp.model.Stuffs;
import com.example.todoapp.model.ToBuys; import com.example.todoapp.model.ToBuys;
import com.example.todoapp.model.User; import com.example.todoapp.model.User;
import com.example.todoapp.repository.UserRepository; import com.example.todoapp.repository.UserRepository;
import com.example.todoapp.repository.StuffsRepository;
import com.example.todoapp.service.ToBuysService; import com.example.todoapp.service.ToBuysService;
import jakarta.validation.Valid; import jakarta.validation.Valid;
@ -20,6 +23,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.Collections;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@ -30,7 +34,7 @@ import org.springframework.web.bind.annotation.*;
/** /**
* 購入リストに関するRESTコントローラー * 購入リストに関するRESTコントローラー
* <p> * <p>
* このコントローラーは購入リスト (to_buys) へのアイテム追加機能を提供します * このコントローラーは購入リスト (toBuys) へのアイテム追加機能を提供します
* リクエストボディには ToBuyDTO 形式のデータが期待されます * リクエストボディには ToBuyDTO 形式のデータが期待されます
* </p> * </p>
*/ */
@ -52,19 +56,36 @@ public class ToBuysController {
*/ */
@PostMapping("/add") @PostMapping("/add")
public ResponseEntity<String> addToBuys( public ResponseEntity<Map<String, Object>> addToBuys(
@Valid @RequestBody ToBuysDTO dto, @Valid @RequestBody ToBuysDTO dto,
Authentication authentication) { 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);
} }
@PutMapping("/update") @PutMapping("/update")
public ResponseEntity<String> updateToBuys( public ResponseEntity<Map<String, Object>> updateToBuys(
@Valid @RequestBody ToBuysDTO dto, @Valid @RequestBody ToBuysDTO dto,
Authentication authentication) { Authentication authentication) {
toBuysService.updateToBuys(dto, 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 +106,24 @@ public class ToBuysController {
User user = userRepository.findByUsername(username) User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found")); .orElseThrow(() -> new UsernameNotFoundException("User not found"));
List<ToBuys> toBuysList = toBuysService.getToBuysByUserId(user.getId()); List<ToBuys> toBuysList = toBuysService.getToBuysByUser(user);
// DTO形式に変換して返す // DTO形式に変換して返す
List<ToBuyResponse> responseList = toBuysList.stream() List<ToBuyResponse> responseList = toBuysList.stream()
.map(toBuy -> { .map(toBuy -> {
ToBuyResponse resp = new ToBuyResponse(); ToBuyResponse resp = new ToBuyResponse();
resp.setTobuy_id(toBuy.getTobuy_id()); Stuffs stuff = toBuy.getStuff();
resp.setStuff_id(toBuy.getStuffs().getStuff_id()); resp.setTobuyId(toBuy.getTobuyId());
resp.setStuff_name(toBuy.getStuffs().getStuff_name()); resp.setStuffId(stuff.getStuffId());
resp.setStuffName(stuff.getStuffName());
resp.setAmount(toBuy.getAmount()); resp.setAmount(toBuy.getAmount());
resp.setShop(toBuy.getStore()); resp.setShop(toBuy.getStore());
return resp; return resp;
}) })
.collect(Collectors.toList()); .collect(Collectors.toList());
Map<String, Object> responseBody = new HashMap<>(); // Map<String, Object> responseBody = new HashMap<>();
responseBody.put("tobuy_array", responseList); // responseBody.put("tobuy_array", responseList);
return ResponseEntity.ok(responseList); return ResponseEntity.ok(responseList);
} }
@ -116,19 +138,11 @@ public class ToBuysController {
public ResponseEntity<Map<String, Boolean>> deleteToBuy( public ResponseEntity<Map<String, Boolean>> deleteToBuy(
@RequestBody DeleteToBuyRequest request, @RequestBody DeleteToBuyRequest request,
Authentication authentication) { 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<>(); Map<String, Boolean> response = new HashMap<>();
if (deletedCount > 0) { if (deletedCount > 0) {
response.put("result", true); response.put("result", true);
} else { } else {
@ -137,4 +151,24 @@ public class ToBuysController {
return ResponseEntity.ok(response); 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;
}

@ -4,6 +4,6 @@ import lombok.Data;
@Data @Data
public class DeleteToBuyRequest { public class DeleteToBuyRequest {
private Long user_id; private Long userId;
private int tobuy_id; 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 @Data
public class ToBuyResponse { public class ToBuyResponse {
private int tobuy_id; private Long tobuyId;
private Long stuff_id; private Long stuffId;
private String stuff_name; private String stuffName;
private int amount; private int amount;
private String shop; private String shop;
} }

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

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

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

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

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

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

@ -28,7 +28,7 @@ import lombok.NoArgsConstructor;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@Entity @Entity
@Table(name = "to_buys") @Table(name = "toBuys")
public class ToBuys { public class ToBuys {
/** /**
@ -36,7 +36,8 @@ public class ToBuys {
*/ */
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private int tobuy_id ; @Column(name = "tobuyId")
private Long tobuyId ;
/** /**
* 材料の一意識別子 FK * 材料の一意識別子 FK
@ -44,22 +45,24 @@ public class ToBuys {
@NotNull @NotNull
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn( @JoinColumn(
name = "stuff_id", name = "stuffId",
referencedColumnName = "stuff_id", referencedColumnName = "stuffId",
nullable = false nullable = false
) )
private Stuffs stuffs; private Stuffs stuff;
/** /**
* ユーザーテーブル参照用の外部キー * ユーザーテーブル参照用の外部キー
*/ */
// @NotNull // @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,62 @@
//--------------------------------
// 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);
}

@ -2,13 +2,19 @@
// StuffsRepository.java // StuffsRepository.java
// //
// //
// 更新履歴:2025/06/05 新規作成 // 更新履歴:2025/06/09 新規作成
// Copyright(c) 2025 IVIS All rights reserved. // Copyright(c) 2025 IVIS All rights reserved.
//-------------------------------------------- //--------------------------------------------
package com.example.todoapp.repository; package com.example.todoapp.repository;
import com.example.todoapp.model.Stuffs; import com.example.todoapp.model.Stuffs;
import java.util.Optional;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@ -22,4 +28,17 @@ import org.springframework.stereotype.Repository;
@Repository @Repository
public interface StuffsRepository extends JpaRepository<Stuffs, Long> { 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; import org.springframework.stereotype.Repository;
/** /**
* to_buys テーブルへのアクセスを行うリポジトリ * toBuys テーブルへのアクセスを行うリポジトリ
* <p> * <p>
* このクラスは to_buys テーブルに対する基本的なCRUD操作を提供します * このクラスは toBuys テーブルに対する基本的なCRUD操作を提供します
* Spring Data JPAによって自動的に実装されます * Spring Data JPAによって自動的に実装されます
* </p> * </p>
*/ */
@Repository @Repository
public interface ToBuysRepository extends JpaRepository<ToBuys, Integer> { 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に基づいて買うものリストを取得する * 指定されたユーザーIDに基づいて買うものリストを取得する
* *
* @param userId ユーザーID * @param userId ユーザーID
* @return 買うものリスト * @return 買うものリスト
*/ */
@Query("SELECT t FROM ToBuys t WHERE t.user_id.id = ?1") @Query("SELECT t FROM ToBuys t WHERE t.user.id = ?1")
List<ToBuys> findByUserId(Long user_id); List<ToBuys> findByUser(Long userId);
/** /**
* 指定されたユーザーIDに基づいて買うものリストを取得する * 指定された買うものIDに基づいて買うものリストを削除
* *
* @param userId ユーザーID
* @param tobuyId 買うものID * @param tobuyId 買うものID
* @return * @return
*/ */
@Modifying @Modifying
@Query("DELETE FROM ToBuys t WHERE t.user_id.id = :userId AND t.tobuy_id = :tobuyId") @Query("DELETE FROM ToBuys t WHERE t.tobuyId = :tobuyId")
int deleteByUserIdAndTobuyId(@Param("userId") Long userId, @Param("tobuyId") int tobuyId); int deleteByTobuyId(@Param("tobuyId") Long tobuyId);
} }

@ -0,0 +1,112 @@
package com.example.todoapp.service;
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, Long stockId, Stocks stockDetails) {
Stocks stock = getStockById(username, stockId);
stock.setAmount(stockDetails.getAmount());
stock.setPrice(stockDetails.getPrice());
stock.setLastUpdate(stockDetails.getLastUpdate());
stock.setBuyDate(stockDetails.getBuyDate());
stock.setExpDate(stockDetails.getExpDate());
return stocksRepository.save(stock);
}
/**
* 指定された在庫を削除する
*
* @param username ユーザー名
* @param taskId 削除する在庫のID
*/
public void deleteStock(String username, Long stockId) {
Stocks stock = getStockById(username, stockId);
stocksRepository.delete(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})));
}
}

@ -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; 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.dto.ToBuysDTO;
import com.example.todoapp.model.Stocks;
import com.example.todoapp.model.Stuffs; import com.example.todoapp.model.Stuffs;
import com.example.todoapp.model.ToBuys; import com.example.todoapp.model.ToBuys;
import com.example.todoapp.model.User; import com.example.todoapp.model.User;
import com.example.todoapp.repository.StocksRepository;
import com.example.todoapp.repository.StuffsRepository; import com.example.todoapp.repository.StuffsRepository;
import com.example.todoapp.repository.ToBuysRepository; import com.example.todoapp.repository.ToBuysRepository;
import com.example.todoapp.repository.UserRepository; import com.example.todoapp.repository.UserRepository;
@ -19,6 +23,7 @@ import com.example.todoapp.repository.UserRepository;
import jakarta.transaction.Transactional; import jakarta.transaction.Transactional;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -28,7 +33,7 @@ import java.util.Optional;
/** /**
* 購入リストのサービスクラス * 購入リストのサービスクラス
* <p> * <p>
* このクラスは購入リスト (to_buys) の登録処理を提供します * このクラスは購入リスト (toBuys) の登録処理を提供します
* 材料 (stuffs) の存在確認と新規作成ユーザー情報の取得などを行います * 材料 (stuffs) の存在確認と新規作成ユーザー情報の取得などを行います
* </p> * </p>
*/ */
@ -44,6 +49,12 @@ public class ToBuysService {
@Autowired @Autowired
private StuffsRepository stuffsRepository; private StuffsRepository stuffsRepository;
@Autowired
private StocksRepository stocksRepository;
@Autowired
private MessageUtils messageUtils;
/** /**
@ -51,38 +62,38 @@ public class ToBuysService {
* *
* @param toBuyDTO 追加する購入アイテムのデータDTO * @param toBuyDTO 追加する購入アイテムのデータDTO
*/ */
public void addToBuys(ToBuysDTO toBuyDTO, Authentication authentication) { public ToBuys addToBuys(ToBuysDTO toBuyDTO, Authentication authentication) {
// ユーザー情報を取得 // ユーザー情報を取得
String username = authentication.getName(); String username = authentication.getName();
User user = userRepository.findByUsername(username) User user = userRepository.findByUsername(username)
.orElseThrow(() -> new RuntimeException("ユーザーが見つかりません: " + username)); .orElseThrow(() -> new RuntimeException("ユーザーが見つかりません: " + username));
Stuffs stuffs; Stuffs stuff;
if (toBuyDTO.getStuff_id() == null) { if (toBuyDTO.getStuffId() == null) {
// 新しい材料を作成 // 新しい材料を作成
stuffs = new Stuffs(); stuff = new Stuffs();
stuffs.setStuff_name(toBuyDTO.getStuff_name()); stuff.setStuffName(toBuyDTO.getStuffName());
stuffs.setCategory(toBuyDTO.getCategory()); stuff.setCategory(toBuyDTO.getCategory());
stuffs = stuffsRepository.save(stuffs); stuff = stuffsRepository.save(stuff);
} else { } else {
// 材料情報を取得 // 材料情報を取得
Optional<Stuffs> optionalStuffs = stuffsRepository.findById(toBuyDTO.getStuff_id()); Optional<Stuffs> optionalStuffs = stuffsRepository.findById(toBuyDTO.getStuffId());
if (!optionalStuffs.isPresent()) { if (!optionalStuffs.isPresent()) {
throw new RuntimeException("材料がありません"); throw new RuntimeException("材料がありません");
} }
stuffs = optionalStuffs.get(); stuff = optionalStuffs.get();
} }
ToBuys toBuys = new ToBuys(); ToBuys toBuys = new ToBuys();
toBuys.setUser_id(user); toBuys.setUser(user);
toBuys.setStuffs(stuffs); toBuys.setStuff(stuff);
toBuys.setAmount(toBuyDTO.getAmount()); toBuys.setAmount(toBuyDTO.getAmount());
toBuys.setStore(toBuyDTO.getShop()); toBuys.setStore(toBuyDTO.getShop());
// データベースに保存 // データベースに保存
toBuysRepository.save(toBuys); return toBuysRepository.save(toBuys);
} }
/** /**
@ -90,7 +101,7 @@ public class ToBuysService {
* *
* @param toBuyDTO 変更する購入アイテムのデータDTO * @param toBuyDTO 変更する購入アイテムのデータDTO
*/ */
public void updateToBuys(ToBuysDTO toBuyDTO, Authentication authentication) { public ToBuys updateToBuys(ToBuysDTO toBuyDTO, Authentication authentication) {
// ユーザー情報を取得 // ユーザー情報を取得
String username = authentication.getName(); String username = authentication.getName();
@ -98,57 +109,106 @@ public class ToBuysService {
.orElseThrow(() -> new RuntimeException("ユーザーが見つかりません: " + username)); .orElseThrow(() -> new RuntimeException("ユーザーが見つかりません: " + username));
Stuffs stuffs; Stuffs stuffs;
if (toBuyDTO.getStuff_id() == null) { if (toBuyDTO.getStuffId() == null) {
Optional<Stuffs> existingStuffs = stuffsRepository.findByStuffName(toBuyDTO.getStuffName());
// 新しい材料を作成 // 新しい材料を作成
stuffs = new Stuffs(); if (existingStuffs.isPresent()) {
stuffs.setStuff_name(toBuyDTO.getStuff_name()); // 如果存在,更新已有材料的属性
stuffs.setCategory(toBuyDTO.getCategory()); stuffs = existingStuffs.get();
stuffs = stuffsRepository.save(stuffs); stuffs.setCategory(toBuyDTO.getCategory()); // 可选:更新分类
} else {
// 否则新建材料
stuffs = new Stuffs();
stuffs.setStuffName(toBuyDTO.getStuffName());
stuffs.setCategory(toBuyDTO.getCategory());
}
stuffsRepository.save(stuffs);
} else { } else {
// 材料情報を取得 // 材料情報を取得
Optional<Stuffs> optionalStuffs = stuffsRepository.findById(toBuyDTO.getStuff_id()); Optional<Stuffs> optionalStuffs = stuffsRepository.findById(toBuyDTO.getStuffId());
if (!optionalStuffs.isPresent()) { if (!optionalStuffs.isPresent()) {
throw new RuntimeException("材料がありません"); throw new RuntimeException("材料がありません");
} }
stuffs = optionalStuffs.get(); stuffs = optionalStuffs.get();
//update //update
// stuffs.setStuff_name(toBuyDTO.getStuff_name());
// stuffs.setCategory(toBuyDTO.getCategory());
stuffs = stuffsRepository.save(stuffs); stuffs = stuffsRepository.save(stuffs);
} }
ToBuys toBuys = new ToBuys(); ToBuys toBuys = new ToBuys();
toBuys.setUser_id(user); toBuys.setTobuyId(toBuyDTO.getTobuyId());
toBuys.setStuffs(stuffs); toBuys.setUser(user);
toBuys.setStuff(stuffs);
toBuys.setAmount(toBuyDTO.getAmount()); toBuys.setAmount(toBuyDTO.getAmount());
toBuys.setStore(toBuyDTO.getShop()); toBuys.setStore(toBuyDTO.getShop());
// データベースに保存 // データベースに保存
toBuysRepository.save(toBuys); return toBuysRepository.save(toBuys);
} }
/** /**
* 指定されたユーザーIDに基づいてすべての買うものリストを取得する * 指定されたユーザーに基づいてすべての買うものリストを取得する
* *
* @param userId ユーザーID * @param user ユーザー
* @return ユーザーに紐づく買うものリスト * @return ユーザーに紐づく買うものリスト
*/ */
public List<ToBuys> getToBuysByUserId(Long userId) { public List<ToBuys> getToBuysByUser(User user) {
return toBuysRepository.findByUserId(userId); return toBuysRepository.findByUser(user.getId());
} }
/** /**
* 指定されたユーザーIDと購入リストIDに基づいて買うものを削除する * 指定された購入リストIDに基づいて買うものを削除する
* *
* @param userId ユーザーID
* @param tobuyId 購入リストID * @param tobuyId 購入リストID
*/ */
@Transactional @Transactional
public int deleteToBuyByIds(Long userId, int tobuyId) { public int deleteToBuyById(Long tobuyId) {
return toBuysRepository.deleteByUserIdAndTobuyId(userId, 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: 'ユーザー登録に失敗しました', REGISTER_FAILED: 'ユーザー登録に失敗しました',
}; };
// タスク関連のエラーメッセージ
export const TASK_ERRORS = {
FETCH_FAILED: 'タスクの取得に失敗しました',
CREATE_FAILED: 'タスクの作成に失敗しました',
UPDATE_FAILED: 'タスクの更新に失敗しました',
DELETE_FAILED: 'タスクの削除に失敗しました',
};
// 買うものリスト関連のエラーメッセージ // 買うものリスト関連のエラーメッセージ
export const TOBUY_ERRORS = { export const TOBUY_ERRORS = {
FETCH_FAILED: '買うものリストの取得に失敗しました', FETCH_FAILED: '買うものリストの取得に失敗しました',
CREATE_FAILED: '買うものリストの作成に失敗しました', CREATE_FAILED: '買うものリストの作成に失敗しました',
UPDATE_FAILED: '買うものリストの更新に失敗しました', UPDATE_FAILED: '買うものリストの更新に失敗しました',
DELETE_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" }}} InputLabelProps={{ style: { fontSize: "40px" }}}
style={{width: "80%" }} style={{width: "80%" }}
InputProps={{ style: { fontSize: "40px"} }} InputProps={{ style: { fontSize: "40px"} }}
name="dish_name" name="dishName"
// autoComplete="username" // autoComplete="username"
autoFocus autoFocus
value={dish} value={dish}

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

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

@ -34,22 +34,28 @@ import {
Add as AddIcon, Delete as DeleteIcon, ShoppingBasket as ShoppingBasketIcon, Add as AddIcon, Delete as DeleteIcon, ShoppingBasket as ShoppingBasketIcon,
SoupKitchen as SoupKitchenIcon SoupKitchen as SoupKitchenIcon
} from '@mui/icons-material'; } from '@mui/icons-material';
import { Task, ToBuy, Stuff } from '../types/types'; import { ToBuy, Stuff, Stock } from '../types/types';
import { TASK_ERRORS } from '../constants/errorMessages'; import { TOBUY_ERRORS } from '../constants/errorMessages';
//import { FaCarrot } from "react-icons/fa6"; //エラー起きる いったん保留 //import { FaCarrot } from "react-icons/fa6"; //エラー起きる いったん保留
// 新規タスクの初期状態 // 新規タスクの初期状態
const EMPTY_TASK: Omit<ToBuy, 'tobuy_id' | 'stuff_id'> & { stuff_id: number | null, category: string } & { newAddition: boolean } = { const EMPTY_TOBUY: Omit<ToBuy, 'tobuyId' | 'stuffId'> & { stuffId: number | null, category: string } & { newAddition: boolean } = {
stuff_id: null, stuffId: null,
stuff_name: '', stuffName: '',
amount: 0, amount: 0,
shop: '', shop: '',
category: '', category: '',
newAddition: false, newAddition: false,
} }
const EMPTY_STOCK = {
price: 0,
buyDate: '',
expDate: '',
}
const TaskListPage: React.FC = () => { const TaskListPage: React.FC = () => {
// タスク一覧の状態管理 // タスク一覧の状態管理
const [tobuys, setToBuys] = useState<ToBuy[]>([]); const [tobuys, setToBuys] = useState<ToBuy[]>([]);
@ -59,12 +65,14 @@ const TaskListPage: React.FC = () => {
//在庫登録ダイアログの表示状態 //在庫登録ダイアログの表示状態
const [openInfoDialog, setOpenInfoDialog] = useState(false); 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 [stuffs, setStuffs] = useState<Stuff[]>([]);
const [newStock, setNewStock] = useState(EMPTY_STOCK);
// コンポーネントマウント時にタスク一覧を取得 // コンポーネントマウント時にタスク一覧を取得
useEffect(() => { useEffect(() => {
@ -78,16 +86,16 @@ const TaskListPage: React.FC = () => {
const fetchTasks = async () => { const fetchTasks = async () => {
try { try {
const tobuys = await toBuyApi.getToBuys(); const tobuys = await toBuyApi.getToBuys();
setToBuys(tobuys.tobuy_array); setToBuys(tobuys);
} catch (error) { } catch (error) {
console.error(`${TASK_ERRORS.FETCH_FAILED}:`, error); console.error(`${TOBUY_ERRORS.FETCH_FAILED}:`, error);
} }
}; };
const onChangeCategory = async (category: string) => { const onChangeCategory = async (category: string) => {
setNewToBuy({ ...newToBuy, category }) setNewToBuy({ ...newToBuy, category })
const result = await stuffApi.getStuffs(category) const result = await stuffApi.getStuffs(category)
setStuffs(result.stuff_array) setStuffs(result)
} }
// /** // /**
@ -113,9 +121,22 @@ const TaskListPage: React.FC = () => {
const handleDeleteTask = async (toBuyId: number) => { const handleDeleteTask = async (toBuyId: number) => {
try { try {
await toBuyApi.deleteToBuy(toBuyId); 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) { } catch (error) {
console.error(`${TASK_ERRORS.DELETE_FAILED}:`, error); console.error(`${TOBUY_ERRORS.BUY_FAILED}:`, error);
} }
}; };
@ -127,15 +148,15 @@ const TaskListPage: React.FC = () => {
const handleCreateTask = async () => { const handleCreateTask = async () => {
try { try {
if (newToBuy.newAddition) { if (newToBuy.newAddition) {
newToBuy.stuff_id = null; newToBuy.stuffId = null;
} }
console.log(newToBuy) console.log(newToBuy)
await toBuyApi.addToBuy(newToBuy); await toBuyApi.addToBuy(newToBuy);
setOpenDialog(false); // ダイアログを閉じる setOpenDialog(false); // ダイアログを閉じる
setNewToBuy(EMPTY_TASK); // 入力内容をリセット setNewToBuy(EMPTY_TOBUY); // 入力内容をリセット
fetchTasks(); // 作成後のタスク一覧を再取得 fetchTasks(); // 作成後のタスク一覧を再取得
} catch (error) { } catch (error) {
console.error(`${TASK_ERRORS.CREATE_FAILED}:`, error); console.error(`${TOBUY_ERRORS.CREATE_FAILED}:`, error);
} }
}; };
@ -150,7 +171,7 @@ const TaskListPage: React.FC = () => {
{/* タスク一覧をマップして各タスクをリストアイテムとして表示 */} {/* タスク一覧をマップして各タスクをリストアイテムとして表示 */}
{tobuys && tobuys.map((tobuy) => ( {tobuys && tobuys.map((tobuy) => (
<ListItem <ListItem
key={tobuy.tobuy_id} key={tobuy.tobuyId}
sx={{ sx={{
bgcolor: 'background.paper', bgcolor: 'background.paper',
mb: 1, mb: 1,
@ -167,7 +188,7 @@ const TaskListPage: React.FC = () => {
*/} */}
{/* タスクのタイトルと説明 - 完了状態に応じて取り消し線を表示 */} {/* タスクのタイトルと説明 - 完了状態に応じて取り消し線を表示 */}
<ListItemText <ListItemText
primary={`${tobuy.stuff_name} × ${tobuy.amount}`} primary={`${tobuy.stuffName} × ${tobuy.amount}`}
//secondary={tobuy.amount} //secondary={tobuy.amount}
sx={{ sx={{
textDecoration: false ? 'line-through' : 'none', textDecoration: false ? 'line-through' : 'none',
@ -181,8 +202,8 @@ const TaskListPage: React.FC = () => {
aria-label="食材情報追加" aria-label="食材情報追加"
onClick={() => { onClick={() => {
setOpenInfoDialog(true) setOpenInfoDialog(true)
setSelectedTask(tobuy.tobuy_id) setSelectedTask(tobuy.tobuyId)
// handleDeleteTask(tobuy.tobuy_id) // handleDeleteTask(tobuy.tobuyId)
}} }}
> >
<ShoppingBasketIcon /> <ShoppingBasketIcon />
@ -206,7 +227,7 @@ const TaskListPage: React.FC = () => {
<IconButton <IconButton
edge="end" edge="end"
aria-label="delete" aria-label="delete"
onClick={() => handleDeleteTask(tobuy.tobuy_id)} onClick={() => handleDeleteTask(tobuy.tobuyId)}
> >
<DeleteIcon /> <DeleteIcon />
</IconButton> </IconButton>
@ -279,12 +300,12 @@ const TaskListPage: React.FC = () => {
<InputLabel id="demo-simple-select-label"></InputLabel> <InputLabel id="demo-simple-select-label"></InputLabel>
<Select <Select
labelId="demo-simple-select-label" labelId="demo-simple-select-label"
value={newToBuy.stuff_id} value={newToBuy.stuffId}
onChange={(e) => setNewToBuy({ ...newToBuy, stuff_id: Number(e.target.value) })} onChange={(e) => setNewToBuy({ ...newToBuy, stuffId: Number(e.target.value) })}
> >
{stuffs.map((stuff) => ( {stuffs.map((stuff) => (
<MenuItem key={stuff.stuff_id} value={stuff.stuff_id}> <MenuItem key={stuff.stuffId} value={stuff.stuffId}>
{stuff.stuff_name} {stuff.stuffName}
</MenuItem> </MenuItem>
))} ))}
</Select> </Select>
@ -296,8 +317,8 @@ const TaskListPage: React.FC = () => {
margin="dense" margin="dense"
label="材料名" label="材料名"
fullWidth fullWidth
value={newToBuy.stuff_name} value={newToBuy.stuffName}
onChange={(e) => setNewToBuy({ ...newToBuy, stuff_name: e.target.value })} onChange={(e) => setNewToBuy({ ...newToBuy, stuffName: e.target.value })}
sx={{ marginBottom: 2 }} sx={{ marginBottom: 2 }}
/>} />}
{/* 数量入力フィールド */} {/* 数量入力フィールド */}
@ -339,6 +360,8 @@ const TaskListPage: React.FC = () => {
margin="dense" margin="dense"
label="価格" label="価格"
fullWidth fullWidth
value={newStock.price}
onChange={(e) => setNewStock({...newStock, price: parseInt(e.target.value)})}
/> />
{/* 消費・賞味期限入力フィールド */} {/* 消費・賞味期限入力フィールド */}
<TextField <TextField
@ -346,6 +369,8 @@ const TaskListPage: React.FC = () => {
label="消費・賞味期限(yyyy/MM/dd)" label="消費・賞味期限(yyyy/MM/dd)"
fullWidth fullWidth
multiline multiline
value={newStock.expDate}
onChange={(e) => setNewStock({...newStock, expDate: e.target.value})}
/> />
{/* 購入日入力フィールド */} {/* 購入日入力フィールド */}
<TextField <TextField
@ -353,6 +378,8 @@ const TaskListPage: React.FC = () => {
label="購入日(yyyy/MM/dd)" label="購入日(yyyy/MM/dd)"
fullWidth fullWidth
multiline multiline
value={newStock.buyDate}
onChange={(e) => setNewStock({...newStock, buyDate: e.target.value})}
/> />
</Box> </Box>
</DialogContent> </DialogContent>
@ -360,9 +387,9 @@ const TaskListPage: React.FC = () => {
<Button onClick={() => setOpenInfoDialog(false)}></Button> <Button onClick={() => setOpenInfoDialog(false)}></Button>
<Button onClick={() => { <Button onClick={() => {
if (selectedTask) { if (selectedTask) {
handleDeleteTask(selectedTask) handleBuy(selectedTask)
setOpenInfoDialog(false) setOpenInfoDialog(false)
setNewToBuy(EMPTY_TASK); // 入力内容をリセット setNewToBuy(EMPTY_TOBUY); // 入力内容をリセット
} }
}} }}
variant="contained"> variant="contained">

@ -3,8 +3,8 @@
* APIとの通信を担当するモジュール * APIとの通信を担当するモジュール
* *
*/ */
import { LoginCredentials, RegisterCredentials, AuthResponse, Task, ToBuy, Stuff, Stock } from '../types/types'; import { LoginCredentials, RegisterCredentials, AuthResponse, /* Task, */ ToBuy, Stuff, Stock } from '../types/types';
import { AUTH_ERRORS, TASK_ERRORS, TOBUY_ERRORS } from '../constants/errorMessages'; import { AUTH_ERRORS, TOBUY_ERRORS, STOCK_ERRORS } from '../constants/errorMessages';
// APIのベースURL - 環境変数から取得するか、デフォルト値を使用 // APIのベースURL - 環境変数から取得するか、デフォルト値を使用
const API_BASE_URL = process.env.REACT_APP_API_BASE_URL || 'http://localhost:8080'; const API_BASE_URL = process.env.REACT_APP_API_BASE_URL || 'http://localhost:8080';
@ -92,7 +92,7 @@ export const toBuyApi = {
* *
* @returns * @returns
*/ */
getToBuys: async (): Promise<{ "tobuy_array": ToBuy[] }> => { getToBuys: async (): Promise<ToBuy[]> => {
const response = await fetch(`${API_BASE_URL}/api/tobuy/get`, { const response = await fetch(`${API_BASE_URL}/api/tobuy/get`, {
headers: getHeaders(), // 認証トークンを含むヘッダー headers: getHeaders(), // 認証トークンを含むヘッダー
}); });
@ -101,27 +101,7 @@ export const toBuyApi = {
throw new Error(TOBUY_ERRORS.FETCH_FAILED); throw new Error(TOBUY_ERRORS.FETCH_FAILED);
} }
const tobuy_array = await response.json(); return 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
// }
// ]
// }
}, },
/** /**
@ -129,7 +109,7 @@ export const toBuyApi = {
* @param tobuy * @param tobuy
* @returns * @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`, { const response = await fetch(`${API_BASE_URL}/api/tobuy/add`, {
method: 'POST', method: 'POST',
headers: getHeaders(), headers: getHeaders(),
@ -140,13 +120,13 @@ export const toBuyApi = {
throw new Error(TOBUY_ERRORS.CREATE_FAILED); throw new Error(TOBUY_ERRORS.CREATE_FAILED);
} }
// return response.json(); return response.json();
return {result: true} // return {result: true}
// return { // return {
// "result": true, // "result": true,
// "tobuy_id": 1, // "tobuyId": 1,
// "stuff_id": 6, // "stuffId": 6,
// "message": "追加に成功しました", // "message": "追加に成功しました",
// } // }
@ -156,11 +136,11 @@ export const toBuyApi = {
* *
* @param id ID * @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`, { const response = await fetch(`${API_BASE_URL}/api/tobuy/delete`, {
method: 'DELETE', method: 'DELETE',
headers: getHeaders(), headers: getHeaders(),
body: JSON.stringify({tobuy_id}), body: JSON.stringify({tobuyId}),
}); });
if (!response.ok) { if (!response.ok) {
@ -173,189 +153,201 @@ export const toBuyApi = {
// "result": true // "result": true
// } // }
}, },
}
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
}
}
}
export const stockApi = {
/** /**
* *
* @returns
*/ */
getStocks: async (): Promise<{ "stock_array": Stock[] }> => { buy: async (req: {tobuyId: number, price: number, expDate: string, buyDate: string, lastUpdate: string}): Promise<{ result: boolean }> => {
// const response = await fetch(`${API_BASE_URL}/api/tobuy/get`, {
// headers: getHeaders(), // 認証トークンを含むヘッダー console.log('req: ', req)
// });
req.buyDate = makeDateObject(req.buyDate)?.toISOString()?.substring(0, 10) || ''
// if (!response.ok) { req.expDate = makeDateObject(req.expDate)?.toISOString()?.substring(0, 10) || ''
// throw new Error(TASK_ERRORS.FETCH_FAILED);
// } const response = await fetch(`${API_BASE_URL}/api/tobuy/buy`, {
method: 'POST',
headers: getHeaders(),
body: JSON.stringify(req),
});
// return response.json(); if (!response.ok) {
throw new Error(TOBUY_ERRORS.BUY_FAILED);
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": "野菜"
}
]
} }
return response.json()
}, },
}
}
/** export const stuffApi = {
* () getStuffs: async (category: string): Promise<Stuff[]> => {
* API機能を提供するオブジェクト const data = await fetch(`${API_BASE_URL}/api/stuff/get?category=${encodeURIComponent(category)}`, {
*
*/
export const taskApi = {
/**
*
* @returns
*/
getTasks: async (): Promise<Task[]> => {
const response = await fetch(`${API_BASE_URL}/api/tasks`, {
headers: getHeaders(), // 認証トークンを含むヘッダー headers: getHeaders(), // 認証トークンを含むヘッダー
}); });
if (!response.ok) { if (!data.ok) {
throw new Error(TASK_ERRORS.FETCH_FAILED); 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
}
}
return response.json();
},
export const stockApi = {
/** /**
* IDのタスクを取得 *
* @param id ID * @returns
* @returns
*/ */
getTask: async (id: number): Promise<Task> => { getStocks: async (): Promise<Stock[]> => {
const response = await fetch(`${API_BASE_URL}/api/tasks/${id}`, { const response = await fetch(`${API_BASE_URL}/api/stocks/get`, {
headers: getHeaders(), headers: getHeaders(), // 認証トークンを含むヘッダー
}); });
if (!response.ok) { if (!response.ok) {
throw new Error(TASK_ERRORS.FETCH_FAILED); throw new Error(STOCK_ERRORS.FETCH_FAILED);
} }
return response.json(); 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) { function makeDateObject(dateStr: String) {
throw new Error(TASK_ERRORS.UPDATE_FAILED); // 例: '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; // 無効な日付の場合
}
return response.json();
},
/** // /**
* // * (サンプル,実際には不要)
* @param id ID // * タスク管理関連のAPI機能を提供するオブジェクト
*/ // * タスクの取得、作成、更新、削除などの機能を含む
deleteTask: async (id: number): Promise<void> => { // */
const response = await fetch(`${API_BASE_URL}/api/tasks/${id}`, { // export const taskApi = {
method: 'DELETE', // /**
headers: getHeaders(), // * 全タスクを取得
}); // * @returns タスク一覧
// */
if (!response.ok) { // getTasks: async (): Promise<Task[]> => {
throw new Error(TASK_ERRORS.DELETE_FAILED); // 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 { // export interface Task {
id: number; // タスクの一意識別子 // id: number; // タスクの一意識別子
stuff_name: string; // タスクのタイトル // stuffName: string; // タスクのタイトル
amount: number; //材料の数量 // amount: number; //材料の数量
price: number; //材料の値段 // price: number; //材料の値段
buyDate:Date; //購入日時 // buyDate:Date; //購入日時
expirationDate: Date; //賞味・消費期限 // expirationDate: Date; //賞味・消費期限
//description?: string; // タスクの詳細説明(任意) // //description?: string; // タスクの詳細説明(任意)
completed: boolean; // タスクの完了状態 // completed: boolean; // タスクの完了状態
userId: number; // タスクの所有者ID // userId: number; // タスクの所有者ID
createdAt: string; // タスク作成日時 // createdAt: string; // タスク作成日時
newAddition: boolean //材料を新規追加するかどうか // newAddition: boolean //材料を新規追加するかどうか
//updatedAt: string; // タスク更新日時 // //updatedAt: string; // タスク更新日時
} // }
/** /**
* *
* *
*/ */
export interface ToBuy { export interface ToBuy {
tobuy_id: number, tobuyId: number,
stuff_id: number, stuffId: number,
stuff_name: string, stuffName: string,
amount: number, amount: number,
shop?: string, shop?: string,
} }
export interface Stuff { export interface Stuff {
stuff_id: number, stuffId: number,
stuff_name: string, stuffName: string,
category: string, category: string,
} }
@ -40,14 +40,14 @@ export interface Stuff {
* *
*/ */
export interface Stock { export interface Stock {
stock_id: number, stockId: number,
stuff_id: number, stuffId: number,
stuff_name: string, stuffName: string,
amount: number, amount: number,
price: number, price: number,
buy_date: string, buyDate: string,
last_update: string, lastUpdate: string,
exp_date: string, expDate: string,
category: string, category: string,
} }

Loading…
Cancel
Save