diff --git a/backend/src/main/java/com/example/todoapp/config/SecurityConfig.java b/backend/src/main/java/com/example/todoapp/config/SecurityConfig.java index 0c91b4f..326888d 100644 --- a/backend/src/main/java/com/example/todoapp/config/SecurityConfig.java +++ b/backend/src/main/java/com/example/todoapp/config/SecurityConfig.java @@ -5,6 +5,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -49,8 +50,10 @@ public class SecurityConfig { .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeHttpRequests() - .requestMatchers("/auth/**").permitAll() // 認証エンドポイントは認証不要 - .anyRequest().authenticated(); // その他のエンドポイントは認証必要 + // .requestMatchers("/auth/**").permitAll() // 認証エンドポイントは認証不要 + // .anyRequest().authenticated() + .anyRequest().permitAll(); + ; // その他のエンドポイントは認証必要 // JWTフィルターを認証フィルターの前に追加 http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); diff --git a/backend/src/main/java/com/example/todoapp/controller/ToBuysController.java b/backend/src/main/java/com/example/todoapp/controller/ToBuysController.java new file mode 100644 index 0000000..7b81ea3 --- /dev/null +++ b/backend/src/main/java/com/example/todoapp/controller/ToBuysController.java @@ -0,0 +1,140 @@ +//-------------------------------- +// ToBuysController.java +// +// 更新履歴:2025/06/05 新規作成 +// Copyright(c) 2025 IVIS All rights reserved. +//-------------------------------------------- + +package com.example.todoapp.controller; + +import com.example.todoapp.dto.DeleteToBuyRequest; +import com.example.todoapp.dto.ToBuyResponse; +import com.example.todoapp.dto.ToBuysDTO; +import com.example.todoapp.model.ToBuys; +import com.example.todoapp.model.User; +import com.example.todoapp.repository.UserRepository; +import com.example.todoapp.service.ToBuysService; +import jakarta.validation.Valid; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +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.*; + +/** + * 購入リストに関するRESTコントローラー + *

+ * このコントローラーは、購入リスト (to_buys) へのアイテム追加機能を提供します。 + * リクエストボディには ToBuyDTO 形式のデータが期待されます。 + *

+ */ +@RestController +@RequestMapping("/tobuy") +public class ToBuysController { + + @Autowired + private ToBuysService toBuysService; + + @Autowired + private UserRepository userRepository; + + /** + * 新しい購入アイテムを追加する + * + * @param dto 追加する購入アイテムのデータ(DTO) + * @return 成功時のレスポンスメッセージ + */ + + @PostMapping("/add") + public ResponseEntity addToBuys( + @Valid @RequestBody ToBuysDTO dto, + Authentication authentication) { + toBuysService.addToBuys(dto, authentication); + return ResponseEntity.ok("Item added to 'To Buys' successfully"); + } + + @PutMapping("/update") + public ResponseEntity updateToBuys( + @Valid @RequestBody ToBuysDTO dto, + Authentication authentication) { + toBuysService.updateToBuys(dto, authentication); + return ResponseEntity.ok("Item updated to 'To Buys' successfully"); + } + + + + /** + * 指定されたユーザーIDに基づいてすべての「買うもの」リストを取得する + * + * @param userId ユーザーID + * @return ユーザーに紐づく「買うもの」リスト + */ + @GetMapping("/get") + public ResponseEntity getAllToBuysByUserId(Authentication authentication) { + + // 認証されたユーザー名を取得 + String username = authentication.getName(); + + // ユーザー情報を取得 + User user = userRepository.findByUsername(username) + .orElseThrow(() -> new UsernameNotFoundException("User not found")); + + List toBuysList = toBuysService.getToBuysByUserId(user.getId()); + + // DTO形式に変換して返す + List 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()); + resp.setAmount(toBuy.getAmount()); + resp.setShop(toBuy.getStore()); + return resp; + }) + .collect(Collectors.toList()); + + Map responseBody = new HashMap<>(); + responseBody.put("tobuy_array", responseList); + + return ResponseEntity.ok(responseList); + } + + /** + * ユーザーが指定したIDの「買うもの」を削除する + * + * @param request 削除する「買うもの」の情報を含むリクエストボディ + * @return 削除が成功した場合にtrueを含むレスポンス + */ + @DeleteMapping("/delete") + public ResponseEntity> 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()); + + Map response = new HashMap<>(); + + + if (deletedCount > 0) { + response.put("result", true); + } else { + response.put("result", false); + } + + return ResponseEntity.ok(response); + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/example/todoapp/dto/DeleteToBuyRequest.java b/backend/src/main/java/com/example/todoapp/dto/DeleteToBuyRequest.java new file mode 100644 index 0000000..b19c196 --- /dev/null +++ b/backend/src/main/java/com/example/todoapp/dto/DeleteToBuyRequest.java @@ -0,0 +1,9 @@ +package com.example.todoapp.dto; + +import lombok.Data; + +@Data +public class DeleteToBuyRequest { + private Long user_id; + private int tobuy_id; +} \ No newline at end of file diff --git a/backend/src/main/java/com/example/todoapp/dto/ToBuyResponse.java b/backend/src/main/java/com/example/todoapp/dto/ToBuyResponse.java new file mode 100644 index 0000000..bf5730b --- /dev/null +++ b/backend/src/main/java/com/example/todoapp/dto/ToBuyResponse.java @@ -0,0 +1,12 @@ +package com.example.todoapp.dto; + +import lombok.Data; + +@Data +public class ToBuyResponse { + private int tobuy_id; + private Long stuff_id; + private String stuff_name; + private int amount; + private String shop; +} \ No newline at end of file diff --git a/backend/src/main/java/com/example/todoapp/dto/ToBuysDTO.java b/backend/src/main/java/com/example/todoapp/dto/ToBuysDTO.java new file mode 100644 index 0000000..90830fd --- /dev/null +++ b/backend/src/main/java/com/example/todoapp/dto/ToBuysDTO.java @@ -0,0 +1,21 @@ +package com.example.todoapp.dto; + +import lombok.Data; +/** + * カテゴリDTOクラス + * このクラスはタスクのカテゴリ情報を表します。 + * カテゴリは名前、色、所有ユーザーなどの情報を持ちます。 + * + */ + + @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; + } \ No newline at end of file diff --git a/backend/src/main/java/com/example/todoapp/model/RecipeStuffs.java b/backend/src/main/java/com/example/todoapp/model/RecipeStuffs.java new file mode 100644 index 0000000..fc94cd5 --- /dev/null +++ b/backend/src/main/java/com/example/todoapp/model/RecipeStuffs.java @@ -0,0 +1,74 @@ +//-------------------------------- +// RecipiesStuffs.java +// +// 分類:社員管理システムV2・ビジネスロジック層 +// +// 更新履歴:2025/06/02 新規作成 +// Copyright(c) 2025 IVIS All rights reserved. +//-------------------------------------------- + +package com.example.todoapp.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 料理に必要な材料 + */ +@Data +@NoArgsConstructor +@Entity +@Table(name = "recipe_stuffs") +public class RecipeStuffs { + + + /** + * 料理に必要リストの一意識別子 + */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int recipe_stuffs_id ; + + /** + * 料理の一意識別子 FK + */ + @NotBlank + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn( + name = "recipe_id", + referencedColumnName = "recipe_id", + nullable = false + ) + private Recipes recipes; + + + /** + * 材料の一意識別子 FK + */ + @NotBlank + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn( + name = "stuff_id", + referencedColumnName = "stuff_id", + nullable = false + ) + private Stuffs stuffs; + + /** + * 材料の数量 + */ + @NotBlank + @Column(nullable = false) + private int amount; + +} diff --git a/backend/src/main/java/com/example/todoapp/model/Recipes.java b/backend/src/main/java/com/example/todoapp/model/Recipes.java new file mode 100644 index 0000000..98d3936 --- /dev/null +++ b/backend/src/main/java/com/example/todoapp/model/Recipes.java @@ -0,0 +1,53 @@ +//-------------------------------- +// Recipes.java +// +// 分類:社員管理システムV2・ビジネスロジック層 +// +// 更新履歴:2025/06/02 新規作成 +// Copyright(c) 2025 IVIS All rights reserved. +//-------------------------------------------- + +package com.example.todoapp.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * カテゴリエンティティクラス + * このクラスはタスクのカテゴリ情報を表します。 + * カテゴリは名前、色、所有ユーザーなどの情報を持ちます。 + */ + +@Data +@NoArgsConstructor +@Entity +@Table(name = "recipes") +public class Recipes { + /** + * カテゴリID(主キー) + */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int recipe_id ; + + /** + * カテゴリ名 + */ + @NotNull + @Column(unique = true, length = 255, nullable = false) + private String recipie_name; + + /** + * カテゴリ + */ + @Column (columnDefinition = "TEXT") + private String summary; + +} \ No newline at end of file diff --git a/backend/src/main/java/com/example/todoapp/model/Stocks.java b/backend/src/main/java/com/example/todoapp/model/Stocks.java new file mode 100644 index 0000000..d9f3f42 --- /dev/null +++ b/backend/src/main/java/com/example/todoapp/model/Stocks.java @@ -0,0 +1,100 @@ +//-------------------------------- +// Stocks.java +// +// 分類:社員管理システムV2・ビジネスロジック層 +// +// 更新履歴:2025/06/03 新規作成 +// Copyright(c) 2025 IVIS All rights reserved. +//-------------------------------------------- + +package com.example.todoapp.model; + +import java.time.LocalDate; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.NoArgsConstructor; + + +/** + * 在庫にある食材 + */ + +@Data +@NoArgsConstructor +@Entity +@Table(name = "stocks") +public class Stocks { + /** + * 在庫リストの一意識別子 + */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int stock_id ; + + + /** + * 商品テーブル参照用の外部キー + */ + @NotNull + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn( + name = "stuff_id", + referencedColumnName = "stuff_id", + nullable = false + ) + private Stuffs stuffs; + + + /** + * ユーザーテーブル参照用の外部キー + */ + @NotNull + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn( + name = "user_id", + referencedColumnName = "id", + nullable = false + ) + private User user_id; + + /** + * 在庫数量(デフォルト値: 1) + */ + @Column(nullable = false) + private int amount = 1; + + /** + * シングルの値段(デフォルト値: 0) + */ + @Column(nullable = false) + private int price = 0; + + /** + * 購入日 + */ + @Column(nullable = false) + private LocalDate buy_date; + + /** + * 最後の操作時間 + */ + @Column(nullable = false) + private LocalDate last_update; + + /** + * 賞味期限 + */ + @Column(nullable = false) + private LocalDate exp_date; + +} \ No newline at end of file diff --git a/backend/src/main/java/com/example/todoapp/model/Stuffs.java b/backend/src/main/java/com/example/todoapp/model/Stuffs.java new file mode 100644 index 0000000..55fe1f9 --- /dev/null +++ b/backend/src/main/java/com/example/todoapp/model/Stuffs.java @@ -0,0 +1,59 @@ +//-------------------------------- +// Stuffs.java +// +// 分類:社員管理システムV2・ビジネスロジック層 +// +// 更新履歴:2025/06/02 新規作成 +// Copyright(c) 2025 IVIS All rights reserved. +//-------------------------------------------- + +package com.example.todoapp.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * カテゴリエンティティクラス + * このクラスはタスクのカテゴリ情報を表します。 + * カテゴリは名前、色、所有ユーザーなどの情報を持ちます。 + */ +@Data +@NoArgsConstructor +@Entity +@Table(name = "stuffs") + public class Stuffs { + + /** + * カテゴリID(主キー) + */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long stuff_id ; + + /** + * カテゴリ名 + */ + @NotNull + @Column(unique = true, length = 255, nullable = false) + private String stuff_name; + + /** + * カテゴリ + */ + @Column (columnDefinition = "TEXT") + private String summary; + + /** + * カテゴリ + */ + @NotNull + @Column(nullable = false, length = 225) + private String category = "その他" ; + } \ No newline at end of file diff --git a/backend/src/main/java/com/example/todoapp/model/ToBuys.java b/backend/src/main/java/com/example/todoapp/model/ToBuys.java new file mode 100644 index 0000000..0670392 --- /dev/null +++ b/backend/src/main/java/com/example/todoapp/model/ToBuys.java @@ -0,0 +1,79 @@ +//-------------------------------- +// ToBuys.java +// +// +// 更新履歴:2025/06/03 新規作成 +// Copyright(c) 2025 IVIS All rights reserved. +//-------------------------------------------- + +package com.example.todoapp.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * ユーザーの買うものリスト + */ + +@Data +@NoArgsConstructor +@Entity +@Table(name = "to_buys") +public class ToBuys { + + /** + * 購入項目の一意識別子 + */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int tobuy_id ; + + /** + * 材料の一意識別子 FK + */ + @NotNull + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn( + name = "stuff_id", + referencedColumnName = "stuff_id", + nullable = false + ) + private Stuffs stuffs; + + /** + * ユーザーテーブル参照用の外部キー + */ + // @NotNull + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn( + name = "user_id", + referencedColumnName = "id" + ) + private User user_id; + + /** + * 購入する数量 + */ + @NotNull + @Column(nullable = false) + private int amount = 1; + + /** + * 購入するお店 + */ + @NotNull + @Column(nullable = false) + private String store; + +} + diff --git a/backend/src/main/java/com/example/todoapp/repository/StuffsRepository.java b/backend/src/main/java/com/example/todoapp/repository/StuffsRepository.java new file mode 100644 index 0000000..1e2fa69 --- /dev/null +++ b/backend/src/main/java/com/example/todoapp/repository/StuffsRepository.java @@ -0,0 +1,25 @@ +//-------------------------------- +// StuffsRepository.java +// +// +// 更新履歴:2025/06/05 新規作成 +// Copyright(c) 2025 IVIS All rights reserved. +//-------------------------------------------- + +package com.example.todoapp.repository; + +import com.example.todoapp.model.Stuffs; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * 材料 (stuffs) テーブルへのアクセスを行うリポジトリ. + *

+ * このクラスは材料テーブル (stuffs) に対する基本的なCRUD操作を提供します。 + * Spring Data JPAによって自動的に実装されます。 + *

+ */ +@Repository +public interface StuffsRepository extends JpaRepository { + // 材料情報を主キーで取得するメソッド(必要に応じて追加) +} \ No newline at end of file diff --git a/backend/src/main/java/com/example/todoapp/repository/ToBuysRepository.java b/backend/src/main/java/com/example/todoapp/repository/ToBuysRepository.java new file mode 100644 index 0000000..57cd2cb --- /dev/null +++ b/backend/src/main/java/com/example/todoapp/repository/ToBuysRepository.java @@ -0,0 +1,49 @@ +//-------------------------------- +// ToBuysRepository.java +// +// +// 更新履歴:2025/06/05 新規作成 +// Copyright(c) 2025 IVIS All rights reserved. +//-------------------------------------------- +package com.example.todoapp.repository; + +import com.example.todoapp.model.ToBuys; + +import java.util.List; + +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; + +/** + * to_buys テーブルへのアクセスを行うリポジトリ + *

+ * このクラスは to_buys テーブルに対する基本的なCRUD操作を提供します。 + * Spring Data JPAによって自動的に実装されます。 + *

+ */ +@Repository +public interface ToBuysRepository extends JpaRepository { + + /** + * 指定されたユーザーIDに基づいて「買うもの」リストを取得する + * + * @param userId ユーザーID + * @return 「買うもの」リスト + */ + @Query("SELECT t FROM ToBuys t WHERE t.user_id.id = ?1") + List findByUserId(Long user_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); +} \ No newline at end of file diff --git a/backend/src/main/java/com/example/todoapp/service/ToBuysService.java b/backend/src/main/java/com/example/todoapp/service/ToBuysService.java new file mode 100644 index 0000000..3fcaee2 --- /dev/null +++ b/backend/src/main/java/com/example/todoapp/service/ToBuysService.java @@ -0,0 +1,154 @@ +//-------------------------------- +// ToBuysService.java +// +// +// 更新履歴:2025/06/05 新規作成 +// Copyright(c) 2025 IVIS All rights reserved. +//-------------------------------------------- + +package com.example.todoapp.service; + +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.StuffsRepository; +import com.example.todoapp.repository.ToBuysRepository; +import com.example.todoapp.repository.UserRepository; + +import jakarta.transaction.Transactional; + +import org.springframework.security.core.Authentication; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +/** + * 購入リストのサービスクラス + *

+ * このクラスは、購入リスト (to_buys) の登録処理を提供します。 + * 材料 (stuffs) の存在確認と新規作成、ユーザー情報の取得などを行います。 + *

+ */ + +@Service +public class ToBuysService { + + @Autowired + private ToBuysRepository toBuysRepository; + + @Autowired + private UserRepository userRepository; + + @Autowired + private StuffsRepository stuffsRepository; + + + /** + * 購入リストに新しいアイテムを追加する + * + * @param toBuyDTO 追加する購入アイテムのデータ(DTO) + */ + public void 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 = new Stuffs(); + stuffs.setStuff_name(toBuyDTO.getStuff_name()); + stuffs.setCategory(toBuyDTO.getCategory()); + stuffs = stuffsRepository.save(stuffs); + } else { + // 材料情報を取得 + Optional optionalStuffs = stuffsRepository.findById(toBuyDTO.getStuff_id()); + if (!optionalStuffs.isPresent()) { + throw new RuntimeException("材料がありません"); + } + stuffs = optionalStuffs.get(); + } + + ToBuys toBuys = new ToBuys(); + toBuys.setUser_id(user); + toBuys.setStuffs(stuffs); + toBuys.setAmount(toBuyDTO.getAmount()); + toBuys.setStore(toBuyDTO.getShop()); + + // データベースに保存 + toBuysRepository.save(toBuys); + + } + + /** + * 購入リストに新しいアイテムを変更する + * + * @param toBuyDTO 変更する購入アイテムのデータ(DTO) + */ + public void updateToBuys(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 = new Stuffs(); + stuffs.setStuff_name(toBuyDTO.getStuff_name()); + stuffs.setCategory(toBuyDTO.getCategory()); + stuffs = stuffsRepository.save(stuffs); + } else { + // 材料情報を取得 + Optional optionalStuffs = stuffsRepository.findById(toBuyDTO.getStuff_id()); + 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.setAmount(toBuyDTO.getAmount()); + toBuys.setStore(toBuyDTO.getShop()); + + // データベースに保存 + toBuysRepository.save(toBuys); + + } + + /** + * 指定されたユーザーIDに基づいてすべての「買うもの」リストを取得する + * + * @param userId ユーザーID + * @return ユーザーに紐づく「買うもの」リスト + */ + public List getToBuysByUserId(Long userId) { + return toBuysRepository.findByUserId(userId); + } + + /** + * 指定されたユーザーIDと購入リストIDに基づいて「買うもの」を削除する + * + * @param userId ユーザーID + * @param tobuyId 購入リストID + */ + @Transactional + public int deleteToBuyByIds(Long userId, int tobuyId) { + return toBuysRepository.deleteByUserIdAndTobuyId(userId, tobuyId); + } +} \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 82bce23..2f0c096 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -11,6 +11,9 @@ import RegisterPage from './pages/RegisterPage'; import TaskListPage from './pages/TaskListPage'; import StockPage from './pages/StockPage'; import './App.css'; +// 必要なインポートを追加 +import AddDishes1 from './pages/AddDishes1'; +import AddDishes2 from './pages/AddDishes2'; /** * アプリケーション全体のMaterial UIテーマを定義 @@ -94,6 +97,23 @@ const App: React.FC = () => { } /> + {/* テストページへのルートを追加 */} + + + + } + /> + + + + } + /> }> {/* ルートパスへのアクセスはタスク一覧にリダイレクト */} diff --git a/frontend/src/components/CategoryDropDown.tsx b/frontend/src/components/CategoryDropDown.tsx index fce3e55..74ded65 100644 --- a/frontend/src/components/CategoryDropDown.tsx +++ b/frontend/src/components/CategoryDropDown.tsx @@ -16,7 +16,7 @@ const CategoryDropDown = () => { onChange={(event) => setSelectedValue(event.target.value)} > 乳製品 - 肉・魚 + 魚・肉 野菜 調味料 その他 diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index 4ac785b..c4a8739 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -103,6 +103,13 @@ const Layout: React.FC = () => { {/* テストページへのリンクを追加 */} {/* 在庫リストへのリンクを追加 */} + handleNavigate('/add1')} + selected={isSelected('/add1')} + > + + + handleNavigate('/stock')} selected={isSelected('/stock')} diff --git a/frontend/src/constants/errorMessages.ts b/frontend/src/constants/errorMessages.ts index 3e7179d..909bbfa 100644 --- a/frontend/src/constants/errorMessages.ts +++ b/frontend/src/constants/errorMessages.ts @@ -21,3 +21,11 @@ export const TASK_ERRORS = { UPDATE_FAILED: 'タスクの更新に失敗しました', DELETE_FAILED: 'タスクの削除に失敗しました', }; + +// 買うものリスト関連のエラーメッセージ +export const TOBUY_ERRORS = { + FETCH_FAILED: '買うものリストの取得に失敗しました', + CREATE_FAILED: '買うものリストの作成に失敗しました', + UPDATE_FAILED: '買うものリストの更新に失敗しました', + DELETE_FAILED: '買うものリストの削除に失敗しました', +}; diff --git a/frontend/src/pages/AddDishes1.tsx b/frontend/src/pages/AddDishes1.tsx new file mode 100644 index 0000000..d003ebd --- /dev/null +++ b/frontend/src/pages/AddDishes1.tsx @@ -0,0 +1,82 @@ +import React, { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + AppBar, + Toolbar, + Typography, + Container, + Box, + Button, + Drawer, + List, + ListItemText, + ListItemIcon, + ListItemButton, + Divider, + IconButton, + TextField, + Paper, + Alert, + Link, + Grid, + } from '@mui/material'; + import { LoginCredentials } from '../types/types'; +import { authApi } from '../services/api'; +import { GENERAL_ERRORS } from '../constants/errorMessages'; + +const AddDishes1: React.FC = () => { + const navigate = useNavigate(); + const [dish, setDish] = useState(""); + // エラーメッセージの状態管理 + const [error, setError] = useState(false); + + const handleChange = (event: React.ChangeEvent) => { + setDish(event.target.value); + }; + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); // フォームのデフォルト送信動作を防止 + if (!dish.trim()) { + alert("エラー"); + setError(true); + } else { + alert("送信成功!"); + localStorage.setItem("dishName", dish); // ローカルストレージにフォームに入力された料理名を保存 + navigate('/add2', { state: dish }); // 料理名から材料名追加ページにリダイレクト + } + }; + + return ( +
+ + 料理の追加・編集 + + +
+ +
+
+ +
+
+
+ ); +}; + +export default AddDishes1; \ No newline at end of file diff --git a/frontend/src/pages/AddDishes2.tsx b/frontend/src/pages/AddDishes2.tsx new file mode 100644 index 0000000..d8aa62c --- /dev/null +++ b/frontend/src/pages/AddDishes2.tsx @@ -0,0 +1,279 @@ +/** + * テストページコンポーネント + * 白紙の状態で表示されるテスト用のページ + */ +import React, { useState, useEffect } from 'react'; +import { taskApi, toBuyApi } from '../services/api'; +import { + Container, + Typography, + Tooltip, + List, + ListItem, + ListItemText, + ListItemSecondaryAction, + IconButton, + Checkbox, + Fab, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + TextField, + Button, + Box, + MenuItem, + Select, + FormControl, + InputLabel +} from '@mui/material'; +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 { GENERAL_ERRORS } from '../constants/errorMessages'; +import CategoryDropDown from "../components/CategoryDropDown"; + +// 新規タスクの初期状態(画面表示用) +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 { + title: string; // 食材名 + amount: number; // 食材の個数 + completed: boolean; // +} + +const AddDishes2: React.FC = () => { + const receivedData = localStorage.getItem("dishName"); + // タスク一覧の状態管理 + const [tasks, setTasks] = useState([]); + + const [addtasks, setAddTasks] = useState([]); + // エラーメッセージの状態管理 + const [error, setError] = useState(false); + // 新規タスク作成ダイアログの表示状態 + const [openDialog, setOpenDialog] = useState(false); + // 新規タスクの入力内容 + const [newTask, setNewTask] = useState(EMPTY_TASK); + + // コンポーネントマウント時にタスク一覧を取得 + 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); + } + }; + /** + * タスクを削除するハンドラー + * 指定されたIDのタスクをAPIを通じて削除 + */ + const handleDeleteTask = async (index: number) => { + try { + let newAddTasks = [...addtasks]; // 配列をコピー + newAddTasks.splice(index, 1); + setAddTasks(newAddTasks); + // fetchTasks(); // 削除後のタスク一覧を再取得 + } catch (error) { + console.error(`${TASK_ERRORS.DELETE_FAILED}:`, error); + } + }; + + /** + * 新規タスクを作成するハンドラー + * 入力されたタスク情報をAPIに送信して新規作成 + * 作成後はダイアログを閉じ、入力内容をリセット + */ + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); // フォームのデフォルト送信動作を防止 + if (addtasks[0] == null) { + setError(true); + alert("食材を追加してください"); + } else { + alert("送信成功!"); + handleCreateTask_DataBase(); + // localStorage.setItem("dishName", dish); // ローカルストレージにフォームに入力された料理名を保存 + // navigate('/add2', { state: dish }); // 料理名から材料名追加ページにリダイレクト + } + }; + const handleCreateTask_Temp = async () => { + try { + // await taskApi.createTask(newTask); + let newAddTasks = [...addtasks]; // 配列をコピー + newAddTasks.push(newTask); + setAddTasks(newAddTasks); + setOpenDialog(false); // ダイアログを閉じる + setNewTask(EMPTY_TASK); // 入力内容をリセット + // fetchTasks(); // 作成後のタスク一覧を再取得 + } catch (error) { + console.error(`${TASK_ERRORS.CREATE_FAILED}:`, error); + } + }; + + const handleCreateTask_DataBase = async () => { + try { + // for (let i = 0; i < addtasks.length; i++) { + // // await taskApi.createTask(addtasks[i]); + // await toBuyApi.addToBuy(addtasks[i]); + // } + setOpenDialog(false); // ダイアログを閉じる + setNewTask(EMPTY_TASK); // 入力内容をリセット + // fetchTasks(); // 作成後のタスク一覧を再取得 + } catch (error) { + console.error(`${TASK_ERRORS.CREATE_FAILED}:`, error); + } + }; + + return ( + +
+

追加する料理名

+

{receivedData}

+
+ + {/* タスク一覧をマップして各タスクをリストアイテムとして表示 */} + {addtasks.map((task, index) => ( + + {/* タスク完了状態を切り替えるチェックボックス + handleToggleComplete(task.id)} + /> */} + {/* タスクのタイトルと説明 - 完了状態に応じて取り消し線を表示 */} + + {/* 食材の個数を表示 */} + 個数
+ {task.amount} + + } + // secondary={task.description} + primaryTypographyProps={{ align: "right", marginRight: "20%", fontSize: "20px" }} + /> + {/* 買い物リスト:食材情報記入ボタン */} + + + handleDeleteTask(task.id)} + > + + + + {/* 買い物リスト:食材削除ボタン */} + + + handleDeleteTask(index)} + > + + + + + +
+ ))} +
+
+ +
+
+ +
+ {/* 新規タスク作成ダイアログ */} + setOpenDialog(false)} disableScrollLock={true}> + 材料の追加 + + + {/*材料カテゴリ選択 */} + + {/* タスクタイトル入力フィールド */} + setNewTask({ ...newTask, title: e.target.value })} + sx={{ marginBottom: 2 }} + /> + {/* 数量入力フィールド */} + { + const value = e.target.value; + const parsedValue = parseInt(value, 10); // 数値に変換 + if (!isNaN(parsedValue)) { + setNewTask({ ...newTask, amount: parsedValue }); // number型で保存 + } + }} + sx={{ width: "20%" }} + type="number" + inputProps={{ inputMode: "numeric", pattern: "[0-9]*" }} // ここで整数のみ許可 + /> + + + + + + + +
+ ); +}; + +export default AddDishes2; \ No newline at end of file diff --git a/frontend/src/pages/StockPage.tsx b/frontend/src/pages/StockPage.tsx index 78b0a99..a634342 100644 --- a/frontend/src/pages/StockPage.tsx +++ b/frontend/src/pages/StockPage.tsx @@ -21,7 +21,6 @@ import { } from '@mui/material'; import { TASK_ERRORS } from '../constants/errorMessages'; -import CategoryDropDown from "../components/CategoryDropDown"; const StockPage: React.FC = () => { diff --git a/frontend/src/pages/TaskListPage.tsx b/frontend/src/pages/TaskListPage.tsx index 411b115..cc97ec7 100644 --- a/frontend/src/pages/TaskListPage.tsx +++ b/frontend/src/pages/TaskListPage.tsx @@ -36,8 +36,6 @@ import { import { Task, ToBuy, Stuff } from '../types/types'; import { TASK_ERRORS } from '../constants/errorMessages'; //import { FaCarrot } from "react-icons/fa6"; //エラー起きる いったん保留 -import CategoryDropDown from "../components/CategoryDropDown"; - @@ -127,6 +125,10 @@ const TaskListPage: React.FC = () => { */ const handleCreateTask = async () => { try { + if (newToBuy.newAddition) { + newToBuy.stuff_id = null; + } + console.log(newToBuy) await toBuyApi.addToBuy(newToBuy); setOpenDialog(false); // ダイアログを閉じる setNewToBuy(EMPTY_TASK); // 入力内容をリセット @@ -139,13 +141,13 @@ const TaskListPage: React.FC = () => { return ( - タスク一覧 + 買うものリスト {/* タスク一覧表示エリア - 青い背景のコンテナ */}
{/* タスク一覧をマップして各タスクをリストアイテムとして表示 */} - {tobuys.map((tobuy) => ( + {tobuys && tobuys.map((tobuy) => ( { onClick={() => { setOpenInfoDialog(true) setSelectedTask(tobuy.tobuy_id) - handleDeleteTask(tobuy.tobuy_id) + // handleDeleteTask(tobuy.tobuy_id) }} > @@ -260,7 +262,7 @@ const TaskListPage: React.FC = () => { onChange={(e) => onChangeCategory(e.target.value) } > 乳製品 - 肉・魚 + 魚・肉 野菜 調味料 その他 diff --git a/frontend/src/pages/TestPage.tsx b/frontend/src/pages/TestPage.tsx new file mode 100644 index 0000000..f6d70be --- /dev/null +++ b/frontend/src/pages/TestPage.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { Box } from '@mui/material'; + +const TestPage: React.FC = () => { + return ( + + {/* 白紙のページ - 何も表示しない */} + + ); +}; + +export default TestPage; \ No newline at end of file diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts index 5853f7c..d7423fc 100644 --- a/frontend/src/services/api.ts +++ b/frontend/src/services/api.ts @@ -4,7 +4,7 @@ * 認証、タスク管理などの機能を提供 */ import { LoginCredentials, RegisterCredentials, AuthResponse, Task, ToBuy, Stuff, Stock } from '../types/types'; -import { AUTH_ERRORS, TASK_ERRORS } from '../constants/errorMessages'; +import { AUTH_ERRORS, TASK_ERRORS, TOBUY_ERRORS } from '../constants/errorMessages'; // APIのベースURL - 環境変数から取得するか、デフォルト値を使用 const API_BASE_URL = process.env.REACT_APP_API_BASE_URL || 'http://localhost:8080'; @@ -93,34 +93,35 @@ export const toBuyApi = { * @returns 買うものリスト一覧 */ getToBuys: async (): Promise<{ "tobuy_array": ToBuy[] }> => { - // const response = await fetch(`${API_BASE_URL}/api/tobuy/get`, { - // headers: getHeaders(), // 認証トークンを含むヘッダー - // }); - - // if (!response.ok) { - // throw new Error(TASK_ERRORS.FETCH_FAILED); - // } - - // return response.json(); + const response = await fetch(`${API_BASE_URL}/api/tobuy/get`, { + headers: getHeaders(), // 認証トークンを含むヘッダー + }); - 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 - } - ] + if (!response.ok) { + 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 + // } + // ] + // } + }, /** @@ -129,24 +130,25 @@ export const toBuyApi = { * @returns 作成された材料情報 */ addToBuy: async (tobuy: Omit & { stuff_id: number | null, category: string }): Promise => { - // const response = await fetch(`${API_BASE_URL}/api/tasks`, { - // method: 'POST', - // headers: getHeaders(), - // body: JSON.stringify(tobuy), - // }); + const response = await fetch(`${API_BASE_URL}/api/tobuy/add`, { + method: 'POST', + headers: getHeaders(), + body: JSON.stringify(tobuy), + }); - // if (!response.ok) { - // throw new Error(TASK_ERRORS.CREATE_FAILED); - // } + if (!response.ok) { + throw new Error(TOBUY_ERRORS.CREATE_FAILED); + } // return response.json(); + return {result: true} - return { - "result": true, - "tobuy_id": 1, - "stuff_id": 6, - "message": "追加に成功しました", - } + // return { + // "result": true, + // "tobuy_id": 1, + // "stuff_id": 6, + // "message": "追加に成功しました", + // } }, @@ -155,19 +157,21 @@ export const toBuyApi = { * @param id 削除対象の買うものリストID */ deleteToBuy: async (tobuy_id: number): Promise<{ result: boolean }> => { - // const response = await fetch(`${API_BASE_URL}/api/tobuy/delete`, { - // method: 'DELETE', - // headers: getHeaders(), - // body: JSON.stringify({tobuy_id}), - // }); - - // if (!response.ok) { - // throw new Error(TASK_ERRORS.DELETE_FAILED); - // } + const response = await fetch(`${API_BASE_URL}/api/tobuy/delete`, { + method: 'DELETE', + headers: getHeaders(), + body: JSON.stringify({tobuy_id}), + }); - return { - "result": true + if (!response.ok) { + throw new Error(TOBUY_ERRORS.DELETE_FAILED); } + + return response.json() + + // return { + // "result": true + // } }, }