diff --git a/backend/src/main/java/org/example/backend/calendar/CalendarService.java b/backend/src/main/java/org/example/backend/calendar/CalendarService.java index 9ccabbd6..40efb08e 100644 --- a/backend/src/main/java/org/example/backend/calendar/CalendarService.java +++ b/backend/src/main/java/org/example/backend/calendar/CalendarService.java @@ -46,14 +46,18 @@ private List getVocabIdsOfMonth(YearMonth yearMonth, Language la } public VocabIdsOfDate getVocabIdsOfDate(LocalDate date, Language language, String userName) { + List ids = getVocabIdsOfDateAsList(date, language, userName); + return new VocabIdsOfDate(date, ids); + } + + public List getVocabIdsOfDateAsList(LocalDate date, Language language, String userName) { List allVocabs = vocabRepo.findAll(); List vocabsOfDate = allVocabs.stream() .filter(vocab -> vocab.getLanguage().equals(language)) .filter(vocab -> vocab.getDatesPerUser() != null && vocab.getDatesPerUser().containsKey(userName)) .filter(vocab -> vocab.getDatesPerUser().get(userName).stream() - .anyMatch(reviewDate -> reviewDate.equals(date))).toList(); - List ids = vocabsOfDate.stream().map(Vocab::getId).toList(); - return new VocabIdsOfDate(date, ids); + .anyMatch(reviewDate -> reviewDate.equals(date))).toList(); + return vocabsOfDate.stream().map(Vocab::getId).toList(); } private VocabIdsOfDate[][] createEmptyCalendar() { diff --git a/backend/src/main/java/org/example/backend/review/ReviewDayRepo.java b/backend/src/main/java/org/example/backend/review/ReviewDayRepo.java index 8a083af6..3ff3aad9 100644 --- a/backend/src/main/java/org/example/backend/review/ReviewDayRepo.java +++ b/backend/src/main/java/org/example/backend/review/ReviewDayRepo.java @@ -11,4 +11,6 @@ public interface ReviewDayRepo extends MongoRepository { ReviewDay getByDayAndUserIdAndLanguage(LocalDate day, String userId, Language language); + + } diff --git a/backend/src/main/java/org/example/backend/review/ReviewDayService.java b/backend/src/main/java/org/example/backend/review/ReviewDayService.java index 47e4cc21..03c12b31 100644 --- a/backend/src/main/java/org/example/backend/review/ReviewDayService.java +++ b/backend/src/main/java/org/example/backend/review/ReviewDayService.java @@ -2,12 +2,10 @@ import lombok.RequiredArgsConstructor; +import org.example.backend.calendar.CalendarService; import org.example.backend.exception.LanguageNotFoundException; import org.example.backend.vocab.Language; -import org.example.backend.vocab.Vocab; - -import org.example.backend.vocab.VocabService; import org.springframework.stereotype.Service; @@ -16,80 +14,57 @@ import java.util.*; + @RequiredArgsConstructor @Service public class ReviewDayService { private final ReviewDayRepo reviewDayRepo; - private final VocabService vocabService; + private final CalendarService calendarService; - public ReviewDay getReviewDay(String languageString, String userId, LocalDate day) throws LanguageNotFoundException { + public ReviewDay getReviewDay(String languageString, String userId, LocalDate date) throws LanguageNotFoundException { + if (date.getDayOfMonth() == 1){clearReviewDayRepo();} Language language = Language.getEnumByString(languageString); - Optional optionalReviewDay = Optional.ofNullable(reviewDayRepo.getByDayAndUserIdAndLanguage(day, userId, language)); + List idList = calendarService.getVocabIdsOfDateAsList (date, language, userId); + Optional optionalReviewDay = Optional.ofNullable(reviewDayRepo.getByDayAndUserIdAndLanguage (date, userId, language)); ReviewDay reviewDay = optionalReviewDay.map(oldReviewDay -> { - try { - return createReviewDay(oldReviewDay.id(), language, userId, day); - } catch (LanguageNotFoundException languageNotFoundException) { - throw new RuntimeException("Couldn't create ReviewDay because Language was not found.", languageNotFoundException); - } - }).orElseGet(() -> { - try { - return createReviewDay(language, userId, day); - } catch (LanguageNotFoundException languageNotFoundException) { - throw new RuntimeException("Couldn't create ReviewDay because Language was not found.", languageNotFoundException); - } - }); + List idsOfReviewedVocabs = oldReviewDay.idsOfVocabsToReview().entrySet().stream() + .filter(Map.Entry::getValue) + .map(Map.Entry::getKey) + .toList(); + Map idMap = new HashMap<>(); + for (String id : idList) { + if (idsOfReviewedVocabs.contains(id)) + {idMap.put(id, true);} else { + idMap.put(id, false); + } + } + ReviewDay newReviewDay = new ReviewDay(oldReviewDay.id(), oldReviewDay.day(), + oldReviewDay.language(), oldReviewDay.userId(), idMap); + return reviewDayRepo.save(newReviewDay); + }) + .orElseGet(() -> { + Map idMap = new HashMap<>(); + for (String id : idList) { + idMap.put(id, false); + } + ReviewDay newReviewDay = new ReviewDay(null, date, + language, userId, idMap); + return reviewDayRepo.save(newReviewDay); + }); return reviewDayRepo.save(reviewDay); } - public ReviewDay createReviewDay(String reviewDayId, Language language, String userId, LocalDate day) throws LanguageNotFoundException { - List allVocabsOfLanguage = vocabService.getAllVocabsOfLanguage(language.getStringOfEnum(), userId); - if (allVocabsOfLanguage.isEmpty()) { - return new ReviewDay(reviewDayId, day, language, userId, new HashMap<>()); - } - List activeVocabs = allVocabsOfLanguage.stream() - .filter(vocab -> vocab.getDatesPerUser().containsKey(userId)) - .toList(); - if (activeVocabs.isEmpty()) { - return new ReviewDay(reviewDayId, day, language, userId, new HashMap<>()); - } - List todaysVocabs = activeVocabs.stream() - .filter(vocab -> vocab.getDatesPerUser().get(userId).contains(day)) - .toList(); - Map idsToReview = new HashMap<>(); - for (Vocab vocab : todaysVocabs) { - idsToReview.put(vocab.getId(), false); - } - return new ReviewDay(reviewDayId, day, language, userId, idsToReview); - } - - public ReviewDay createReviewDay(Language language, String userId, LocalDate day) throws LanguageNotFoundException { - List allVocabsOfLanguage = vocabService.getAllVocabsOfLanguage(language.getStringOfEnum(), userId); - if (allVocabsOfLanguage.isEmpty()) { - return new ReviewDay(null, day, language, userId, new HashMap<>()); - } - List activeVocabs = allVocabsOfLanguage.stream() - .filter(vocab -> vocab.getDatesPerUser().containsKey(userId)) - .toList(); - if (activeVocabs.isEmpty()) { - return new ReviewDay(null, day, language, userId, new HashMap<>()); - } - List todaysVocabs = activeVocabs.stream() - .filter(vocab -> vocab.getDatesPerUser().get(userId).contains(day)) - .toList(); - Map idsToReview = new HashMap<>(); - for (Vocab vocab : todaysVocabs) { - idsToReview.put(vocab.getId(), false); - } - return new ReviewDay(null, day, language, userId, idsToReview); - } - - public ReviewDay setVocabReviewed(String vocabId, String languageString, String userId, LocalDate day) throws LanguageNotFoundException { + public ReviewDay setVocabReviewed(String vocabId, String languageString, String userId, LocalDate date) throws LanguageNotFoundException { Language language = Language.getEnumByString(languageString); - Optional oldReviewDay = Optional.of(reviewDayRepo.getByDayAndUserIdAndLanguage(day, userId, language)); + Optional oldReviewDay = Optional.of(reviewDayRepo.getByDayAndUserIdAndLanguage (date, userId, language)); Map newIdsOfVocabsToReview = oldReviewDay.get().idsOfVocabsToReview(); newIdsOfVocabsToReview.put(vocabId, true); ReviewDay updatedReviewDay = new ReviewDay(oldReviewDay.get().id(), oldReviewDay.get().day(), language, userId, newIdsOfVocabsToReview); return reviewDayRepo.save(updatedReviewDay); } + + public void clearReviewDayRepo(){ + reviewDayRepo.deleteAll(); + } } diff --git a/backend/src/test/java/org/example/backend/review/ReviewDayServiceTest.java b/backend/src/test/java/org/example/backend/review/ReviewDayServiceTest.java index 9c8f9cb6..0f293501 100644 --- a/backend/src/test/java/org/example/backend/review/ReviewDayServiceTest.java +++ b/backend/src/test/java/org/example/backend/review/ReviewDayServiceTest.java @@ -1,9 +1,8 @@ package org.example.backend.review; +import org.example.backend.calendar.CalendarService; import org.example.backend.exception.LanguageNotFoundException; import org.example.backend.vocab.Language; -import org.example.backend.vocab.Vocab; -import org.example.backend.vocab.VocabService; import org.junit.jupiter.api.Test; import java.time.LocalDate; @@ -17,24 +16,21 @@ class ReviewDayServiceTest { private final ReviewDayRepo mockReviewRepo = mock(ReviewDayRepo.class); - private final VocabService mockVocabService = mock(VocabService.class); - private final ReviewDayService reviewService = new ReviewDayService(mockReviewRepo, mockVocabService); + private final CalendarService mockCalendarService = mock(CalendarService.class); + private final ReviewDayService reviewService = new ReviewDayService(mockReviewRepo, mockCalendarService); @Test void getReviewDay_shouldReturnReviewDayOfCurrentDate_ifItExists_whenCalled() throws LanguageNotFoundException { LocalDate date = LocalDate.of(2025, 1, 1); - Map> datesPerUser = new HashMap<>(); - datesPerUser.put("user id", List.of(date)); - Vocab testVocab = new Vocab("vocab id", "la prueba", - "test", "", Language.SPANISH, datesPerUser, "Wordio"); Map idsOfVocabsToReview = new HashMap<>(); idsOfVocabsToReview.put("vocab id", false); + idsOfVocabsToReview.put("vocab id 2", true); ReviewDay expected = new ReviewDay( "000", date, Language.SPANISH, "user id", idsOfVocabsToReview); when(mockReviewRepo.getByDayAndUserIdAndLanguage(date, "user id", Language.SPANISH)) .thenReturn(expected); - when(mockVocabService.getAllVocabsOfLanguage("Spanish", "user id")) - .thenReturn(List.of(testVocab)); + when(mockCalendarService.getVocabIdsOfDateAsList(date,Language.SPANISH, "user id")) + .thenReturn(List.of("vocab id", "vocab id 2")); when(mockReviewRepo.save(expected)).thenReturn(expected); ReviewDay actual = reviewService.getReviewDay("Spanish", "user id", date); assertEquals(expected, actual); @@ -55,42 +51,6 @@ void getReviewDay_shouldThrowLanguageNotFoundException_whenCalledWithNonExistent reviewService.getReviewDay("Esperanto", "user id", date)); } - @Test - void createReviewDay_shouldCreateANewReviewDay_whenCalledWithAReviewDayId() throws LanguageNotFoundException { - LocalDate date = LocalDate.of(2025, 1, 1); - Map> datesPerUser = new HashMap<>(); - datesPerUser.put("user id", List.of(date)); - Vocab testVocab1 = new Vocab("vocab id", "la prueba", - "test", "", Language.SPANISH, datesPerUser, "Wordio"); - Vocab testVocab2 = new Vocab("vocab id 2", "la prueba", - "test", "", Language.SPANISH, new HashMap<>(), "Wordio"); - Map oldIdsOfVocabsToReview = new HashMap<>(); - oldIdsOfVocabsToReview.put("vocab id", false); - oldIdsOfVocabsToReview.put("vocab id 2", false); - ReviewDay oldReviewDay = new ReviewDay("123", date,Language.SPANISH, "user id", oldIdsOfVocabsToReview); - when(mockVocabService.getAllVocabsOfLanguage("Spanish", "user id")).thenReturn(List.of(testVocab1, testVocab2)); - Map idsOfVocabsToReview = new HashMap<>(); - idsOfVocabsToReview.put("vocab id", false); - ReviewDay expected = new ReviewDay(null, date,Language.SPANISH, "user id", idsOfVocabsToReview); - ReviewDay actual = reviewService.createReviewDay(Language.SPANISH,"user id", date); - assertEquals(expected, actual); - } - - @Test - void createReviewDay_shouldCreateANewReviewDay_whenCalledWithoutAReviewDayId() throws LanguageNotFoundException { - LocalDate date = LocalDate.of(2025, 1, 1); - Map> datesPerUser = new HashMap<>(); - datesPerUser.put("user id", List.of(date)); - Vocab testVocab = new Vocab("vocab id", "la prueba", - "test", "", Language.SPANISH, datesPerUser, "Wordio"); - when(mockVocabService.getAllVocabsOfLanguage("Spanish", "user id")).thenReturn(List.of(testVocab)); - Map idsOfVocabsToReview = new HashMap<>(); - idsOfVocabsToReview.put("vocab id", false); - ReviewDay expected = new ReviewDay(null, date,Language.SPANISH, "user id", idsOfVocabsToReview); - ReviewDay actual = reviewService.createReviewDay( Language.SPANISH,"user id", date); - assertEquals(expected, actual); - } - @Test void setVocabReviewed_shouldSetBooleanToTrue_whenCalledWithIdOfVocab() throws LanguageNotFoundException { diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index afa8c3c4..78f8e680 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -29,6 +29,8 @@ function App() { const [displayNewVocabsPopUp, setDisplayNewVocabsPopUp] = useState(false) const navigate = useNavigate() + console.log(vocabsToReview) + useEffect(() => { getUserId() }, []); @@ -58,7 +60,6 @@ function App() { axios.get(`/api/vocab?language=${language}`) .then(response => { setVocabs(response.data) - console.log("Successfully updated vocabs state.") }) .catch(error => console.error(error)) } @@ -94,6 +95,7 @@ function App() { response.data.idsOfVocabsToReview) .filter(innerArray => innerArray[1] === false) .map(innerArray => innerArray[0]); + console.log(response.data) if (vocabs.length < 1) { console.error( "Couldn't get vocabs to review because vocabs was empty."); @@ -104,7 +106,6 @@ function App() { }) .filter((vocab): vocab is Vocab => vocab !== undefined) setVocabsToReview(vocabsToReview as Vocab[]) - console.log("Successfully updated vocabsToReview state.") } }) .catch(error => { @@ -119,7 +120,9 @@ function App() { axios.put(`/api/review/${id}?language=${language}`) .then(() => { console.log(`Vocab with ID ${id} was marked as reviewed for today.`) - getAllVocabsOfLanguage() + // setTimeout(() => { + getVocabsToReview() + // }, 10000); }) .catch(error => { console.error(error) @@ -127,10 +130,13 @@ function App() { } function activateVocab(id: string): void { + if (toast.isActive(`activate-vocab-${id}`)) { + return; + } axios.put(`api/vocab/activate/${id}`) .then(() => { console.log(`Successfully activated vocab with ID ${id}.`) - toast.success("Vocab successfully activated.") + toast.success("Vocab successfully activated.", { toastId: `activate-vocab-${id}` }) getAllVocabsOfLanguage() }) .catch(error => { @@ -140,10 +146,13 @@ function App() { } function deactivateVocab(id: string): void { + if (toast.isActive(`deactivate-vocab-${id}`)) { + return; + } axios.put(`api/vocab/deactivate/${id}`) .then(() => { console.log(`Successfully deactivated vocab with ID ${id}.`) - toast.success("Vocab successfully deactivated.") + toast.success("Vocab successfully deactivated.", { toastId: `deactivate-vocab-${id}` }) getAllVocabsOfLanguage() }) .catch(error => { @@ -169,10 +178,13 @@ function App() { } function deleteVocab(id: string): void { + if (toast.isActive(`delete-vocab-${id}`)) { + return; + } axios.delete(`api/vocab/${id}`) .then(() => { console.log(`Successfully deleted vocab with ID ${id}.`) - toast.success("Vocab successfully deleted") + toast.success("Vocab successfully deleted", { toastId: `delete-vocab-${id}` }) getAllVocabsOfLanguage() }) .catch(error => { diff --git a/frontend/src/components/Header/Header.tsx b/frontend/src/components/Header/Header.tsx index 376315f7..67708069 100644 --- a/frontend/src/components/Header/Header.tsx +++ b/frontend/src/components/Header/Header.tsx @@ -1,5 +1,6 @@ import './Header.css' -import {useNavigate} from "react-router-dom"; +import {useLocation, useNavigate, useParams} from "react-router-dom"; +import {useEffect, useState} from "react"; type Props = { userId: string @@ -9,7 +10,15 @@ type Props = { } export default function Header(props: Readonly) { -const navigate = useNavigate() + // const [onReviewOrDisplayPage, setOnReviewOrDisplayPage] = useState(false) + const navigate = useNavigate() + // const location = useLocation(); + // useEffect(() => { + // if (location.pathname === "/review" || location.pathname === "/display"){ + // setOnReviewOrDisplayPage(true) + // } + // }, []); + function handleChange(event: React.ChangeEvent){ props.setLanguage(event.target.value) } @@ -21,7 +30,9 @@ const navigate = useNavigate() aria-live={"polite"}> Wordio - {props.userId && props.language && ( + {props.userId && props.language && + // !onReviewOrDisplayPage && + (
)} - {props.userId &&