Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

수정사항 적용 #51

Merged
merged 10 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/src/main/java/hello/aimju/Board/domain/Board.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class Board {
private Long id;
private String title;
private LocalDate createdTime = LocalDate.now();
@Column(columnDefinition = "TEXT")
private String content;
@OneToMany(mappedBy = "board",cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package hello.aimju.Board.repository;

import hello.aimju.Board.domain.Board;
import hello.aimju.recipe.domain.Recipe;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
Expand All @@ -12,4 +11,8 @@ public interface BoardRepository extends JpaRepository<Board, Long> {
List<Board> findAllByUserId(Long userId);

Page<Board> findByTitleContaining(String searchKeyword, Pageable pageable);

Page<Board> findByUserIdAndTitleContaining(Long userId, String searchKeyword, Pageable pageable);

Page<Board> findAllByUserId(Long userId, Pageable pageable);
}
36 changes: 36 additions & 0 deletions backend/src/main/java/hello/aimju/Board/service/BoardService.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,42 @@ public ResponseEntity<RetrieveBoardListResponseDto> retrieveSearchBoard(int pag
return ResponseEntity.ok().body(new RetrieveBoardListResponseDto((long) boardLists.getTotalPages(), getBoardLists));
}

public ResponseEntity<RetrieveBoardListResponseDto> retrieveCurrentUserBoard(int page, int size, HttpSession session) {
Pageable pageable;
pageable = PageRequest.of(page, size, Sort.by("id").descending());
User user = getUserFromSession(session);
Page<Board> boardLists = boardRepository.findAllByUserId(user.getId(), pageable);
List<RetrieveBoardResponseDto> getBoardLists = boardLists.stream()
.map(tmp -> RetrieveBoardResponseDto.builder()
.id(tmp.getId())
.title(tmp.getTitle())
.createdTime(tmp.getCreatedTime())
.commentNum((long) tmp.getComments().size())
.username(tmp.getUser().getUserName())
.build())
.collect(Collectors.toList());

return ResponseEntity.ok().body(new RetrieveBoardListResponseDto((long) boardLists.getTotalPages(), getBoardLists));
}

public ResponseEntity<RetrieveBoardListResponseDto> retrieveCurrentUserSearchBoard(int page, int size, HttpSession session, String searchKeyword) {
Pageable pageable;
pageable = PageRequest.of(page, size, Sort.by("id").descending());
User user = getUserFromSession(session);
Page<Board> boardLists = boardRepository.findByUserIdAndTitleContaining(user.getId(), searchKeyword, pageable);
List<RetrieveBoardResponseDto> getBoardLists = boardLists.stream()
.map(tmp -> RetrieveBoardResponseDto.builder()
.id(tmp.getId())
.title(tmp.getTitle())
.createdTime(tmp.getCreatedTime())
.commentNum((long) tmp.getComments().size())
.username(tmp.getUser().getUserName())
.build())
.collect(Collectors.toList());

return ResponseEntity.ok().body(new RetrieveBoardListResponseDto((long) boardLists.getTotalPages(), getBoardLists));
}

public ResponseEntity<?> retrieveOneBoard(Long id) {
Board findBoard = boardRepository.findById(id).get();
RetrieveOneBoardResponseDto board = RetrieveOneBoardResponseDto.builder()
Expand Down
11 changes: 11 additions & 0 deletions backend/src/main/java/hello/aimju/controller/BoardController.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ public ResponseEntity<?> retrieve(@RequestParam(name = "page", defaultValue = "0
return boardService.retrieveSearchBoard(page, size,searchKeyword);
}
}
@GetMapping("/current-user")
public ResponseEntity<?> retrieveCurrentUser(@RequestParam(name = "page", defaultValue = "0") int page,
@RequestParam(name = "size", defaultValue = "5") int size,
@RequestParam(name = "searchKeyword", required = false) String searchKeyword,
HttpSession session){
if (searchKeyword == null) {
return boardService.retrieveCurrentUserBoard(page, size, session);
}else{
return boardService.retrieveCurrentUserSearchBoard(page, size, session, searchKeyword);
}
}

@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseEntity<?> retrieveOneBoard(@PathVariable("id") Long id){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ public List<GetAllRecipesResponseDto> getRecipes(HttpSession session) {
}

@RequestMapping(value = "/recipe/{recipeId}", method = RequestMethod.GET)
public GetRecipeResponseDto getRecipeDetails(@PathVariable("recipeId") Long recipeId) {
GetRecipeResponseDto recipeDetails = recipeService.getRecipeDetails(recipeId);
public GetRecipeResponseDto getRecipeDetails(@PathVariable("recipeId") Long recipeId ,HttpSession session) {
GetRecipeResponseDto recipeDetails = recipeService.getRecipeDetails(recipeId, session);
return recipeDetails;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,13 @@ public List<GetAllRecipesResponseDto> getAllRecipes(HttpSession session) {
return responseDtoList;
}

public GetRecipeResponseDto getRecipeDetails(Long recipeId) {
public GetRecipeResponseDto getRecipeDetails(Long recipeId, HttpSession session) {
User user = getUserFromSession(session);
Recipe recipe = recipeRepository.findById(recipeId)
.orElseThrow(() -> new IllegalArgumentException("레시피를 찾을 수 없습니다. ID: " + recipeId));

if (!Objects.equals(user.getId(), recipe.getUser().getId())){
throw new IllegalArgumentException("접근 권한이 없습니다");
}
GetRecipeResponseDto responseDto = new GetRecipeResponseDto();
responseDto.setRecipeId(recipeId);
responseDto.setMenu(recipe.getMenu());
Expand Down
4 changes: 1 addition & 3 deletions frontend/src/app/boards/[boardId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,7 @@ export default function BoardDetail({params}: {params: {boardId: string}}) {
<h2 className="text-2xl font-bold text-gray-900 dark:text-gray-100 mb-2">
{title}
</h2>
<p className="text-gray-700 dark:text-gray-400">
{content}
</p>
<div dangerouslySetInnerHTML={{__html: content.replace(/\n/g, '<br/>')}}/>
</div>
<hr className="mb-1"/>
<div className="mb-4">
Expand Down
69 changes: 58 additions & 11 deletions frontend/src/app/boards/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ To read more about using these font, please visit the Next.js documentation:
'use client'
import { Button } from "@/components/ui/button"
import Link from "next/link"
import { CardTitle, CardDescription, CardHeader, CardContent, CardFooter, Card } from "@/components/ui/card"
import { CardTitle, CardDescription, CardHeader, CardFooter, Card } from "@/components/ui/card"
import {Header} from "@/components/ui/header";
import React, {useEffect, useState} from "react";
import axios from "axios";
import useUserStore from "@/store/useUserStore";
import useBoardStore from '@/store/useBoardStore';

type Board = {
id: number;
Expand All @@ -40,6 +41,7 @@ export default function Board() {
const [searchKeyword, setSearchKeyword] = useState(null);
const [boards, setBoards] = useState<Board[]>([]);
const {getState} = useUserStore;
const ownBoard = useBoardStore((state) => state.ownBoard);

const getBoards = async (page = 0, searchKeyword = null)=>{
try {
Expand Down Expand Up @@ -74,18 +76,63 @@ export default function Board() {
}
};

useEffect(()=>{
getBoards();
}, []);
const getOwnBoards = async (page = 0, searchKeyword = null) => {
try {
let response;
if (searchKeyword === null) {
response = await axios.get('/api/boards/current-user', {
params: {
page: page,
}
});
} else {
response = await axios.get('/api/boards/current-user', {
params: {
page: page,
searchKeyword: searchKeyword
}
});
}

const boards: Board[] = await response.data.boardLists.map((b: any) => ({
id: b.id,
title: b.title,
commentNum: b.commentNum,
createdTime: b.createdTime,
username: b.username,
}));
setTotalPages((response?.data.totalPages === 0 ? 0 : response?.data.totalPages - 1));
setBoards(boards);
setSearchKeyword(searchKeyword);
} catch (error) {
console.log(error)
}
};

const handlePrevPage = ()=> {
getBoards(currentPage-1, searchKeyword);
setCurrentPage(currentPage-1);
useEffect(() => {
if (ownBoard === 1) {
getOwnBoards();
} else {
getBoards();
}
}, [ownBoard]);

const handlePrevPage = () => {
if (ownBoard === 1) {
getOwnBoards(currentPage - 1, searchKeyword);
} else {
getBoards(currentPage - 1, searchKeyword);
}
setCurrentPage(currentPage - 1);
}

const handleNextPage = ()=> {
getBoards(currentPage+1, searchKeyword);
setCurrentPage(currentPage+1);
const handleNextPage = () => {
if (ownBoard === 1) {
getOwnBoards(currentPage + 1, searchKeyword);
} else {
getBoards(currentPage + 1, searchKeyword);
}
setCurrentPage(currentPage + 1);
}

return (
Expand Down Expand Up @@ -122,7 +169,7 @@ export default function Board() {
</div>

<div className="fixed bottom-6 right-6">
<Link href={'/boards/writing'}>
<Link href={'/boards/writing/0'}>
<Button size="lg">
<PlusIcon className="h-6 w-6" />
<span className="sr-only">Add new</span>
Expand Down
110 changes: 110 additions & 0 deletions frontend/src/app/boards/writing/[recipeId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
'use client'
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Header } from '@/components/ui/header';
import { Textarea } from '@/components/ui/textarea';
import { useRouter } from 'next/navigation';

export default function PostWriting({ params }: { params: { recipeId: number } }) {
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const router = useRouter();
const [recipes, setRecipes] = useState<{ recipeId: number; menu: string; }[]>([]);
const [selectedRecipe, setSelectedRecipe] = useState<number | null>(null); // 선택된 레시피 ID

useEffect(() => {
if (params.recipeId !== 0) {
const fetchRecipeContent = async () => {
try {
const response = await axios.get(`/api/recipe/${params.recipeId}`);
setTitle(response.data.menu);
setContent(`재료: ${response.data.ingredients.join(', ')}\n레시피:\n${response.data.recipeInfoList.map((info: string, index:number) => `${index + 1}. ${info}`).join('\n')}`);
} catch (error) {
console.error("Error fetching recipe content:", error);
}
};
fetchRecipes();
fetchRecipeContent();
} else {
setTitle('제목을 입력해주세요');
setContent('내용을 입력해주세요');
}
}, [params.recipeId]);

const handleBoardSave = async () => {
try {
const response = await axios.post('/api/boards', {
title: title,
content: content,
});
router.push('/boards');
} catch (error) {
console.error("Error saving board:", error);
}
};

const fetchRecipes = async () => {
try {
const response = await axios.get('/api/recipes');
setRecipes(response.data);
} catch (error) {
console.error("Error fetching recipes:", error);
}
};

const handleRecipeSelection = (recipeId: number) => {
setSelectedRecipe(recipeId);
};

const handleRoutingBoards = (recipeId: number) => {
router.push(`/boards/writing/${recipeId}`);
};

return (
<>
<Header />
<main className="py-8 h-[calc(100vh-72px)]">
<div className="container mx-auto px-4">
<div className="grid grid-cols-1 gap-6">
<div>
<label className="block mb-2 font-bold text-2xl text-white">제목</label>
<Input value={title} onChange={(e) => setTitle(e.target.value)} />
</div>
<div>
<label className="block mb-2 font-bold text-2xl text-white">레시피 가져오기</label>
<select
className="w-full p-2 rounded border border-gray-300 focus:outline-none focus:border-indigo-500 bg-gray-300 text-black"
value={selectedRecipe || ''}
onChange={(e) => {
const selectedRecipeId = Number(e.target.value);
handleRecipeSelection(selectedRecipeId);
handleRoutingBoards(selectedRecipeId);
}}
>
<option value="">레시피 선택</option>
{recipes.map(recipe => (
<option key={recipe.recipeId} value={recipe.recipeId}>{recipe.menu}</option>
))}
</select>
</div>
<div>
<label className="block mb-2 font-bold text-2xl text-white">내용</label>
<Textarea
className="w-full h-full resize-none"
style={{minHeight: '200px', maxHeight: '60vh'}}
value={content}
onChange={(e) => setContent(e.target.value)}
/>
</div>
<div className="flex justify-center mt-6">
<Button onClick={() => router.push('/boards')} variant="outline">취소</Button>
<Button onClick={handleBoardSave}>저장</Button>
</div>
</div>
</div>
</main>
</>
)
}
Loading
Loading