From be810d4a66971e4337814076833ca726dee90942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Efe=20Ak=C3=A7a?= Date: Mon, 16 Dec 2024 20:15:11 +0300 Subject: [PATCH 1/4] fix(backend): difficulty level issues --- .../QuestionDifficultyRateRepository.java | 2 +- .../QuestionDifficultyRateService.java | 37 +- .../Services/QuestionService.java | 460 +++++++++--------- frontend/src/components/SubtypeCard.tsx | 26 +- frontend/src/routes/home.tsx | 2 +- frontend/src/routes/question.tsx | 48 +- mobile/components/QuestionCard.tsx | 29 +- 7 files changed, 323 insertions(+), 281 deletions(-) diff --git a/backend/src/main/java/com/group1/programminglanguagesforum/Repositories/QuestionDifficultyRateRepository.java b/backend/src/main/java/com/group1/programminglanguagesforum/Repositories/QuestionDifficultyRateRepository.java index 63ec5b54..9a8edfc2 100644 --- a/backend/src/main/java/com/group1/programminglanguagesforum/Repositories/QuestionDifficultyRateRepository.java +++ b/backend/src/main/java/com/group1/programminglanguagesforum/Repositories/QuestionDifficultyRateRepository.java @@ -15,5 +15,5 @@ public interface QuestionDifficultyRateRepository extends JpaRepository findByQuestionAndUser(Question question, User user); - long countByDifficulty(DifficultyLevel difficultyLevel); + long countByDifficultyAndQuestion(DifficultyLevel difficultyLevel, Question question); } diff --git a/backend/src/main/java/com/group1/programminglanguagesforum/Services/QuestionDifficultyRateService.java b/backend/src/main/java/com/group1/programminglanguagesforum/Services/QuestionDifficultyRateService.java index 2f59d1d3..86e62a7a 100644 --- a/backend/src/main/java/com/group1/programminglanguagesforum/Services/QuestionDifficultyRateService.java +++ b/backend/src/main/java/com/group1/programminglanguagesforum/Services/QuestionDifficultyRateService.java @@ -8,23 +8,22 @@ import com.group1.programminglanguagesforum.Exceptions.UnauthorizedAccessException; import com.group1.programminglanguagesforum.Repositories.QuestionDifficultyRateRepository; import com.group1.programminglanguagesforum.Repositories.QuestionRepository; +import java.util.NoSuchElementException; +import java.util.Optional; import lombok.RequiredArgsConstructor; import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; -import java.util.NoSuchElementException; -import java.util.Optional; - @Service @RequiredArgsConstructor public class QuestionDifficultyRateService { + private final QuestionDifficultyRateRepository questionDifficultyRateRepository; private final UserContextService userContextService; private final QuestionRepository questionRepository; - public QuestionRateResponseDto rateQuestion(Long id, DifficultyLevel difficultyLevel) throws UnauthorizedAccessException { - Question question = questionRepository.findById(id).orElseThrow(() -> new NoSuchElementException("Question not found")); + Question question = questionRepository.findById(id).orElseThrow(() -> new NoSuchElementException("Question not found")); User user = userContextService.getCurrentUser(); Optional questionDifficultyRateOptional = getQuestionDifficultyRate(question, user); QuestionDifficultyRate questionDifficultyRate = questionDifficultyRateOptional.orElseGet(() -> { @@ -35,7 +34,21 @@ public QuestionRateResponseDto rateQuestion(Long id, DifficultyLevel difficultyL }); questionDifficultyRate.setDifficulty(difficultyLevel); questionDifficultyRateRepository.save(questionDifficultyRate); - QuestionRateCounts result = getResult(); + QuestionRateCounts result = getResult(id); + + DifficultyLevel newDifficulty = question.getDifficulty(); + + if (result.easyCount() > result.mediumCount() && result.easyCount() > result.hardCount()) { + newDifficulty = DifficultyLevel.EASY; + } else if (result.mediumCount() > result.easyCount() && result.mediumCount() > result.hardCount()) { + newDifficulty = DifficultyLevel.MEDIUM; + } else if (result.hardCount() > result.easyCount() && result.hardCount() > result.mediumCount()) { + newDifficulty = DifficultyLevel.HARD; + } + + question.setDifficulty(newDifficulty); + questionRepository.save(question); + return QuestionRateResponseDto.builder() .questionId(id) .easyCount(result.easyCount()) @@ -44,8 +57,6 @@ public QuestionRateResponseDto rateQuestion(Long id, DifficultyLevel difficultyL .totalCount(result.easyCount() + result.mediumCount() + result.hardCount()) .build(); - - } public Optional getQuestionDifficultyRate(Question question, User user) { @@ -53,13 +64,15 @@ public Optional getQuestionDifficultyRate(Question quest } @NonNull - public QuestionRateCounts getResult() { - long easyCount = questionDifficultyRateRepository.countByDifficulty(DifficultyLevel.EASY); - long mediumCount = questionDifficultyRateRepository.countByDifficulty(DifficultyLevel.MEDIUM); - long hardCount = questionDifficultyRateRepository.countByDifficulty(DifficultyLevel.HARD); + public QuestionRateCounts getResult(Long questionId) { + Question question = questionRepository.findById(questionId).orElseThrow(() -> new NoSuchElementException("Question not found")); + long easyCount = questionDifficultyRateRepository.countByDifficultyAndQuestion(DifficultyLevel.EASY, question); + long mediumCount = questionDifficultyRateRepository.countByDifficultyAndQuestion(DifficultyLevel.MEDIUM, question); + long hardCount = questionDifficultyRateRepository.countByDifficultyAndQuestion(DifficultyLevel.HARD, question); return new QuestionRateCounts(easyCount, mediumCount, hardCount); } public record QuestionRateCounts(long easyCount, long mediumCount, long hardCount) { + } } diff --git a/backend/src/main/java/com/group1/programminglanguagesforum/Services/QuestionService.java b/backend/src/main/java/com/group1/programminglanguagesforum/Services/QuestionService.java index 35efcf9e..31298f71 100644 --- a/backend/src/main/java/com/group1/programminglanguagesforum/Services/QuestionService.java +++ b/backend/src/main/java/com/group1/programminglanguagesforum/Services/QuestionService.java @@ -1,21 +1,5 @@ package com.group1.programminglanguagesforum.Services; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.Objects; - -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.stereotype.Service; - import com.group1.programminglanguagesforum.DTOs.Requests.CreateQuestionRequestDto; import com.group1.programminglanguagesforum.DTOs.Requests.UpdateQuestionRequestDto; import com.group1.programminglanguagesforum.DTOs.Responses.AuthorDto; @@ -32,244 +16,258 @@ import com.group1.programminglanguagesforum.Repositories.BookmarkRepository; import com.group1.programminglanguagesforum.Repositories.QuestionRepository; import com.group1.programminglanguagesforum.Repositories.VoteRepository; - +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor public class QuestionService { - private final QuestionRepository questionRepository; - private final UserContextService userContextService; - private final TagService tagService; - private final BookmarkRepository bookmarkRepository; - private final VoteRepository voteRepository; - private final QuestionDifficultyRateService questionDifficultyRateService; - public Optional findById(Long id) { - return questionRepository.findById(id); - } + private final QuestionRepository questionRepository; + private final UserContextService userContextService; + private final TagService tagService; + private final BookmarkRepository bookmarkRepository; + private final VoteRepository voteRepository; + private final QuestionDifficultyRateService questionDifficultyRateService; - public List findByAuthorId(Long authorId) { - return questionRepository.findByAuthorId(authorId).stream() - .map(QuestionService::mapToQuestionSummary) - .collect(Collectors.toList()); - } + public Optional findById(Long id) { + return questionRepository.findById(id); + } - public CreateQuestionResponseDto createQuestion(CreateQuestionRequestDto dto) - throws UnauthorizedAccessException { - List tagIds = dto.getTagIds(); - Set existingTags = new HashSet<>(tagService.findAllByIdIn(tagIds)); - User currentUser = userContextService.getCurrentUser(); - Date date = new Date(); - Question question = Question.builder() - .title(dto.getTitle()) - .questionBody(dto.getContent()) - .difficulty(dto.getDifficulty()) - .askedBy(currentUser) - .createdAt(date) - .updatedAt(date) - .likeCount(0L) - .commentCount(0L) - .tags(existingTags) - .votes(new ArrayList<>()) - .build(); - questionRepository.save(question); - List tags = existingTags.stream().map(tag -> TagDto.builder() - .id(tag.getId()) - .name(tag.getTagName()) - .build()).toList(); + public List findByAuthorId(Long authorId) { + return questionRepository.findByAuthorId(authorId).stream() + .map(QuestionService::mapToQuestionSummary) + .collect(Collectors.toList()); + } - return CreateQuestionResponseDto.builder() - .id(question.getId()) - .title(question.getTitle()) - .content(question.getQuestionBody()) - .difficulty(question.getDifficulty()) - .tags(tags) - .author(AuthorDto.builder() - .id(currentUser.getId()) - .username(currentUser.getUsername()) - .reputationPoints(currentUser.getReputationPoints()) - .name(currentUser.getFirstName() + " " + currentUser.getLastName()) - .build()) - .createdAt(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(question.getCreatedAt())) - .updatedAt(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(question.getUpdatedAt())) - .upvoteCount(0L) - .downvoteCount(0L) - .build(); + public CreateQuestionResponseDto createQuestion(CreateQuestionRequestDto dto) + throws UnauthorizedAccessException { + List tagIds = dto.getTagIds(); + Set existingTags = new HashSet<>(tagService.findAllByIdIn(tagIds)); + User currentUser = userContextService.getCurrentUser(); + Date date = new Date(); + Question question = Question.builder() + .title(dto.getTitle()) + .questionBody(dto.getContent()) + .difficulty(dto.getDifficulty()) + .askedBy(currentUser) + .createdAt(date) + .updatedAt(date) + .likeCount(0L) + .commentCount(0L) + .tags(existingTags) + .votes(new ArrayList<>()) + .build(); + questionRepository.save(question); + List tags = existingTags.stream().map(tag -> TagDto.builder() + .id(tag.getId()) + .name(tag.getTagName()) + .build()).toList(); - } + return CreateQuestionResponseDto.builder() + .id(question.getId()) + .title(question.getTitle()) + .content(question.getQuestionBody()) + .difficulty(question.getDifficulty()) + .tags(tags) + .author(AuthorDto.builder() + .id(currentUser.getId()) + .username(currentUser.getUsername()) + .reputationPoints(currentUser.getReputationPoints()) + .name(currentUser.getFirstName() + " " + currentUser.getLastName()) + .build()) + .createdAt(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(question.getCreatedAt())) + .updatedAt(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(question.getUpdatedAt())) + .upvoteCount(0L) + .downvoteCount(0L) + .build(); - public GetQuestionDetailsResponseDto getQuestion(Long id) throws NoSuchElementException { - User currentUser; - try { - currentUser = userContextService.getCurrentUser(); - } catch (UnauthorizedAccessException e) { - currentUser = null; - } + } - Optional questionOptional = questionRepository.findById(id); - if (questionOptional.isEmpty()) { - throw new NoSuchElementException("Question not found"); - } + public GetQuestionDetailsResponseDto getQuestion(Long id) throws NoSuchElementException { + User currentUser; + try { + currentUser = userContextService.getCurrentUser(); + } catch (UnauthorizedAccessException e) { + currentUser = null; + } - Question question = questionOptional.get(); - boolean selfQuestion = (currentUser != null - && currentUser.getId().equals(question.getAskedBy().getId())); - boolean isBookmarked = (currentUser != null - && bookmarkRepository.existsByUserAndQuestion(currentUser, question)); - Integer selfVoted = (currentUser != null - && voteRepository.findByUserAndQuestion(currentUser, question).isPresent() - ? voteRepository.findByUserAndQuestion(currentUser, question).get() - .isUpvote() ? 1 : -1 - : 0); - QuestionDifficultyRateService.QuestionRateCounts record = questionDifficultyRateService.getResult(); - long easyCount = record.easyCount(); - long mediumCount = record.mediumCount(); - long hardCount = record.hardCount(); - DifficultyLevel lastRatedDifficulty = questionDifficultyRateService - .getQuestionDifficultyRate(questionOptional.get(), currentUser) - .map(QuestionDifficultyRate::getDifficulty).orElse(null); + Optional questionOptional = questionRepository.findById(id); + if (questionOptional.isEmpty()) { + throw new NoSuchElementException("Question not found"); + } - return GetQuestionDetailsResponseDto.builder() - .id(question.getId()) - .title(question.getTitle()) - .content(question.getQuestionBody()) - .difficulty(question.getDifficulty()) - .selfDifficultyVote(lastRatedDifficulty) - .likeCount(question.getUpvoteCount()) - .dislikeCount(question.getDownvoteCount()) - .commentCount(question.getCommentCount()) - .selfQuestion(selfQuestion) - .selfVoted(selfVoted) - .easyCount(easyCount) - .mediumCount(mediumCount) - .hardCount(hardCount) - .createdAt(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(question.getCreatedAt())) - .updatedAt(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(question.getUpdatedAt())) - .author(AuthorDto.builder() - .id(question.getAskedBy().getId()) - .username(question.getAskedBy().getUsername()) - .reputationPoints(question.getAskedBy().getReputationPoints()) - .name(question.getAskedBy().getFirstName() + " " - + question.getAskedBy().getLastName()) - .build()) - .rating(0L) - .tags(question.getTags().stream().map(tag -> TagDto.builder() - .id(tag.getId()) - .name(tag.getTagName()) - .build()).toList()) - .answerCount((long) question.getAnswers().size()) - .bookmarked(isBookmarked) - .build(); + Question question = questionOptional.get(); + boolean selfQuestion = (currentUser != null + && currentUser.getId().equals(question.getAskedBy().getId())); + boolean isBookmarked = (currentUser != null + && bookmarkRepository.existsByUserAndQuestion(currentUser, question)); + Integer selfVoted = (currentUser != null + && voteRepository.findByUserAndQuestion(currentUser, question).isPresent() + ? voteRepository.findByUserAndQuestion(currentUser, question).get() + .isUpvote() ? 1 : -1 + : 0); + QuestionDifficultyRateService.QuestionRateCounts record = questionDifficultyRateService.getResult(question.getId()); + long easyCount = record.easyCount(); + long mediumCount = record.mediumCount(); + long hardCount = record.hardCount(); + DifficultyLevel lastRatedDifficulty = questionDifficultyRateService + .getQuestionDifficultyRate(questionOptional.get(), currentUser) + .map(QuestionDifficultyRate::getDifficulty).orElse(null); - } + return GetQuestionDetailsResponseDto.builder() + .id(question.getId()) + .title(question.getTitle()) + .content(question.getQuestionBody()) + .difficulty(question.getDifficulty()) + .selfDifficultyVote(lastRatedDifficulty) + .likeCount(question.getUpvoteCount()) + .dislikeCount(question.getDownvoteCount()) + .commentCount(question.getCommentCount()) + .selfQuestion(selfQuestion) + .selfVoted(selfVoted) + .easyCount(easyCount) + .mediumCount(mediumCount) + .hardCount(hardCount) + .createdAt(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(question.getCreatedAt())) + .updatedAt(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(question.getUpdatedAt())) + .author(AuthorDto.builder() + .id(question.getAskedBy().getId()) + .username(question.getAskedBy().getUsername()) + .reputationPoints(question.getAskedBy().getReputationPoints()) + .name(question.getAskedBy().getFirstName() + " " + + question.getAskedBy().getLastName()) + .build()) + .rating(0L) + .tags(question.getTags().stream().map(tag -> TagDto.builder() + .id(tag.getId()) + .name(tag.getTagName()) + .build()).toList()) + .answerCount((long) question.getAnswers().size()) + .bookmarked(isBookmarked) + .build(); - public String deleteQuestion(Long id) { - Optional questionOptional = questionRepository.findById(id); - if (questionOptional.isEmpty()) { - throw new NoSuchElementException("Question not found"); - } - Question question = questionOptional.get(); - questionRepository.delete(question); - return "Question deleted successfully"; - } + } - public CreateQuestionResponseDto updateQuestion(Long id, UpdateQuestionRequestDto dto) { - Optional questionOptional = questionRepository.findById(id); - if (questionOptional.isEmpty()) { - throw new NoSuchElementException("Question not found"); - } - Date date = new Date(); - Question question = questionOptional.get(); - List tagIds = dto.getTags(); - Set existingTags = new HashSet<>(tagService.findAllByIdIn(tagIds)); - question.setTitle(dto.getTitle()); - question.setQuestionBody(dto.getContent()); - question.setTags(existingTags); - question.setUpdatedAt(date); - question.setDifficulty(dto.getDifficulty()); - questionRepository.save(question); - List tags = existingTags.stream().map(tag -> TagDto.builder() - .id(tag.getId()) - .name(tag.getTagName()) - .build()).toList(); - return CreateQuestionResponseDto.builder() - .id(question.getId()) - .title(question.getTitle()) - .content(question.getQuestionBody()) - .difficulty(question.getDifficulty()) - .tags(tags) - .author(AuthorDto.builder() - .id(question.getAskedBy().getId()) - .username(question.getAskedBy().getUsername()) - .reputationPoints(question.getAskedBy().getReputationPoints()) - .name(question.getAskedBy().getFirstName() + " " - + question.getAskedBy().getLastName()) - .build()) - .createdAt(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(question.getCreatedAt())) - .updatedAt(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(question.getUpdatedAt())) - .upvoteCount(question.getUpvoteCount()) - .downvoteCount(question.getDownvoteCount()) - .build(); + public String deleteQuestion(Long id) { + Optional questionOptional = questionRepository.findById(id); + if (questionOptional.isEmpty()) { + throw new NoSuchElementException("Question not found"); + } + Question question = questionOptional.get(); + questionRepository.delete(question); + return "Question deleted successfully"; + } + public CreateQuestionResponseDto updateQuestion(Long id, UpdateQuestionRequestDto dto) { + Optional questionOptional = questionRepository.findById(id); + if (questionOptional.isEmpty()) { + throw new NoSuchElementException("Question not found"); } + Date date = new Date(); + Question question = questionOptional.get(); + List tagIds = dto.getTags(); + Set existingTags = new HashSet<>(tagService.findAllByIdIn(tagIds)); + question.setTitle(dto.getTitle()); + question.setQuestionBody(dto.getContent()); + question.setTags(existingTags); + question.setUpdatedAt(date); + question.setDifficulty(dto.getDifficulty()); + questionRepository.save(question); + List tags = existingTags.stream().map(tag -> TagDto.builder() + .id(tag.getId()) + .name(tag.getTagName()) + .build()).toList(); + return CreateQuestionResponseDto.builder() + .id(question.getId()) + .title(question.getTitle()) + .content(question.getQuestionBody()) + .difficulty(question.getDifficulty()) + .tags(tags) + .author(AuthorDto.builder() + .id(question.getAskedBy().getId()) + .username(question.getAskedBy().getUsername()) + .reputationPoints(question.getAskedBy().getReputationPoints()) + .name(question.getAskedBy().getFirstName() + " " + + question.getAskedBy().getLastName()) + .build()) + .createdAt(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(question.getCreatedAt())) + .updatedAt(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(question.getUpdatedAt())) + .upvoteCount(question.getUpvoteCount()) + .downvoteCount(question.getDownvoteCount()) + .build(); - public Page searchQuestions( - String query, - String tagIdsStr, - DifficultyLevel difficulty, - int page, - int pageSize, - String sortBy) { + } - List tagIds = null; - if (tagIdsStr != null && !tagIdsStr.isEmpty()) { - tagIds = Arrays.stream(tagIdsStr.split(",")) - .map(Long::parseLong) - .collect(Collectors.toList()); - } - User currentUser; - try { - currentUser = userContextService.getCurrentUser(); - } catch (UnauthorizedAccessException e) { - currentUser = null; - } + public Page searchQuestions( + String query, + String tagIdsStr, + DifficultyLevel difficulty, + int page, + int pageSize, + String sortBy) { - PageRequest pageable = PageRequest.of(page - 1, pageSize); - if (Objects.equals(sortBy, "default") || Objects.equals(sortBy, null) || Objects.equals(currentUser, null)) { - return questionRepository.searchQuestions(query, tagIds, difficulty, pageable); - } else { - List authorIds = currentUser.getFollowing().stream() - .map(User::getId) // Map each User to its ID - .collect(Collectors.toList()); // Collect the IDs into a List - return questionRepository.searchQuestionsByRecommended(query, authorIds, tagIds, difficulty, pageable); - } - + List tagIds = null; + if (tagIdsStr != null && !tagIdsStr.isEmpty()) { + tagIds = Arrays.stream(tagIdsStr.split(",")) + .map(Long::parseLong) + .collect(Collectors.toList()); + } + User currentUser; + try { + currentUser = userContextService.getCurrentUser(); + } catch (UnauthorizedAccessException e) { + currentUser = null; } - public static QuestionSummaryDto mapToQuestionSummary(Question question) { - return QuestionSummaryDto.builder() - .id(question.getId()) - .title(question.getTitle()) - .content(question.getQuestionBody()) - .difficulty(question.getDifficulty()) - .upvoteCount(question.getUpvoteCount()) - .downvoteCount(question.getDownvoteCount()) - .answerCount((long) question.getAnswers().size()) - .createdAt(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(question.getCreatedAt())) - .author(AuthorDto.builder() - .id(question.getAskedBy().getId()) - .username(question.getAskedBy().getUsername()) - .name(question.getAskedBy().getFirstName() + " " - + question.getAskedBy().getLastName()) - .reputationPoints(question.getAskedBy().getReputationPoints()) - .build()) - .tags(question.getTags().stream() - .map(tag -> TagDto.builder() - .id(tag.getId()) - .name(tag.getTagName()) - .build()) - .collect(Collectors.toList())) - .build(); + PageRequest pageable = PageRequest.of(page - 1, pageSize); + if (Objects.equals(sortBy, "default") || Objects.equals(sortBy, null) || Objects.equals(currentUser, null)) { + return questionRepository.searchQuestions(query, tagIds, difficulty, pageable); + } else { + List authorIds = currentUser.getFollowing().stream() + .map(User::getId) // Map each User to its ID + .collect(Collectors.toList()); // Collect the IDs into a List + return questionRepository.searchQuestionsByRecommended(query, authorIds, tagIds, difficulty, pageable); } + + } + + public static QuestionSummaryDto mapToQuestionSummary(Question question) { + return QuestionSummaryDto.builder() + .id(question.getId()) + .title(question.getTitle()) + .content(question.getQuestionBody()) + .difficulty(question.getDifficulty()) + .upvoteCount(question.getUpvoteCount()) + .downvoteCount(question.getDownvoteCount()) + .answerCount((long) question.getAnswers().size()) + .createdAt(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(question.getCreatedAt())) + .author(AuthorDto.builder() + .id(question.getAskedBy().getId()) + .username(question.getAskedBy().getUsername()) + .name(question.getAskedBy().getFirstName() + " " + + question.getAskedBy().getLastName()) + .reputationPoints(question.getAskedBy().getReputationPoints()) + .build()) + .tags(question.getTags().stream() + .map(tag -> TagDto.builder() + .id(tag.getId()) + .name(tag.getTagName()) + .build()) + .collect(Collectors.toList())) + .build(); + } } diff --git a/frontend/src/components/SubtypeCard.tsx b/frontend/src/components/SubtypeCard.tsx index f8ac9a3e..2dff6f7d 100644 --- a/frontend/src/components/SubtypeCard.tsx +++ b/frontend/src/components/SubtypeCard.tsx @@ -18,22 +18,24 @@ export const TagSubtypeCard = React.forwardRef< >(({ tagSubtype }, ref) => { return ( -
- {/* Subtype Name */} -

- {tagSubtype.typeId} -

+
+
+ {/* Subtype Name */} +

+ {tagSubtype.typeId} +

- {/* Description */} -

{tagSubtype.description}

+ {/* Description */} +

{tagSubtype.description}

- {/* Number of Tags */} -
- - {tagSubtype.tagCount} tags + {/* Number of Tags */} +
+ + {tagSubtype.tagCount} tags +
{/* Navigation Link */} diff --git a/frontend/src/routes/home.tsx b/frontend/src/routes/home.tsx index e8e2dbe6..7217df97 100644 --- a/frontend/src/routes/home.tsx +++ b/frontend/src/routes/home.tsx @@ -5,7 +5,7 @@ export function IndexRoute() { <>

- Welcome to Programming Languages Forum + Welcome to the Programming Languages Forum

diff --git a/frontend/src/routes/question.tsx b/frontend/src/routes/question.tsx index 2fd8e29e..926ffc95 100644 --- a/frontend/src/routes/question.tsx +++ b/frontend/src/routes/question.tsx @@ -27,7 +27,14 @@ import { } from "@/services/api/programmingForumComponents"; import useAuthStore from "@/services/auth"; import { convertTagToTrack, useExercismSearch } from "@/services/exercism"; -import { Flag, MessageSquare, ThumbsDown, ThumbsUp, Trash } from "lucide-react"; +import { + Flag, + MessageSquare, + Pencil, + ThumbsDown, + ThumbsUp, + Trash, +} from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { Link, useParams } from "react-router-dom"; @@ -112,9 +119,15 @@ export default function QuestionPage() { const titleRef = useRef(null); const contentRef = useRef(null); - const [tags, setTags] = useState( - question.tags?.map((tag) => Number(tag.id)) || [], - ); // Tag IDs state + const [tags, setTags] = useState([]); + + useEffect(() => { + if (!isEditing && question.tags) { + console.log("set tags", question.tags); + setTags(question.tags.map((t) => Number(t.id))); + } + }, [question?.tags, isEditing]); + const [availableTags, setAvailableTags] = useState< { tagId: string; name: string }[] >([]); // Available tags @@ -220,6 +233,15 @@ export default function QuestionPage() { }} /> )} + {!isEditing && selfProfile?.id === question.author.id && ( + + )}
@@ -294,6 +316,7 @@ export default function QuestionPage() { value: String(tag.tagId), label: tag.name || "Loading...", }))} + defaultValue={tags.map((tag) => String(tag))} value={tags.map((tag) => String(tag))} onValueChange={(selectedIds) => { const selectedTags = selectedIds.map((id) => Number(id)); // Convert back to numbers @@ -318,8 +341,8 @@ export default function QuestionPage() { {/* Question Content */} {isEditing ? ( -
-
+ <> +
{isPreviewMode ? ( -
+
) : (