Skip to content

Commit

Permalink
Merge pull request #4 from Ropold/frontend2
Browse files Browse the repository at this point in the history
Frontend2
  • Loading branch information
Ropold authored Feb 21, 2025
2 parents b9acbbd + 5bba591 commit b537953
Show file tree
Hide file tree
Showing 17 changed files with 613 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,38 @@ public class MemoryController {
private final CloudinaryService cloudinaryService;
private final AppUserService appUserService;

@GetMapping("/favorites")
public List<MemoryModel> getUserFavorites(@AuthenticationPrincipal OAuth2User authentication) {
List<String> favoriteMemoryIds = appUserService.getUserFavorites(authentication.getName());
return memoryService.getMemoriesByIds(favoriteMemoryIds);
}

@PostMapping("/favorites/{memoryId}")
@ResponseStatus(HttpStatus.CREATED)
public void addMemoryToFavorites(@PathVariable String memoryId , @AuthenticationPrincipal OAuth2User authentication) {
String authenticatedUserId = authentication.getName();
appUserService.addMemoryToFavorites(authenticatedUserId, memoryId);
}

@DeleteMapping("/favorites/{memoryId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void removeMemoryFromFavorites(@PathVariable String memoryId, @AuthenticationPrincipal OAuth2User authentication) {
String authenticatedUserId = authentication.getName();
appUserService.removeMemoryFromFavorites(authenticatedUserId, memoryId);
}

@PutMapping("/{id}/toggle-active")
public MemoryModel toggleMemoryActive(@PathVariable String id, @AuthenticationPrincipal OAuth2User authentication) {
String authenticatedUserId = authentication.getName();
MemoryModel memoryModel = memoryService.getMemoryById(id);

if (!authenticatedUserId.equals(memoryModel.appUserGithubId())) {
throw new AccessDeniedException("You are not allowed to update memories for other users");
}

return memoryService.toggleMemoryActive(id);
}

@GetMapping()
public List<MemoryModel> getAllMemories() {
return memoryService.getAllMemories();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package ropold.backend.model;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public record MemoryModelDto(
@NotBlank
@Size (min = 3, message = "Name must contain at least 3 characters")
String name,

int matchId,
Category category,
String description,
Expand All @@ -10,6 +16,7 @@ public record MemoryModelDto(
String appUserUsername,
String appUserAvatarUrl,
String appUserGithubUrl,
@NotBlank(message = "Image URL must not be blank")
String imageUrl
) {
}
24 changes: 24 additions & 0 deletions backend/src/main/java/ropold/backend/service/AppUserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import ropold.backend.model.AppUser;
import ropold.backend.repository.AppUserRepository;

import java.util.List;

@Service
@RequiredArgsConstructor
public class AppUserService {
Expand All @@ -16,4 +18,26 @@ public AppUser getUserById(String userId) {
.orElseThrow(() -> new RuntimeException("User not found"));
}

public List<String> getUserFavorites(String userId) {
AppUser user = getUserById(userId);
return user.favorites();
}

public void addMemoryToFavorites(String userId, String memoryId) {
AppUser user = getUserById(userId);

if (!user.favorites().contains(memoryId)) {
user.favorites().add(memoryId);
appUserRepository.save(user);
}
}

public void removeMemoryFromFavorites(String userId, String memoryId) {
AppUser user = getUserById(userId);

if (user.favorites().contains(memoryId)) {
user.favorites().remove(memoryId);
appUserRepository.save(user);
}
}
}
34 changes: 34 additions & 0 deletions backend/src/main/java/ropold/backend/service/MemoryService.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,38 @@ public void deleteMemory(String id) {
}
memoryRepository.deleteById(id);
}

public List<MemoryModel> getMemoriesByIds(List<String> memoryIds) {
return memoryRepository.findAllById(memoryIds);
}


public MemoryModel toggleMemoryActive(String id) {
MemoryModel memory = memoryRepository.findById(id)
.orElseThrow(() -> new MemoryNotFoundException("No Memory found with id: " + id));

MemoryModel updatedMemoryModel = new MemoryModel(
id,
memory.name(),
memory.matchId(),
memory.category(),
memory.description(),
!memory.isActive(),
memory.appUserGithubId(),
memory.appUserUsername(),
memory.appUserAvatarUrl(),
memory.appUserGithubUrl(),
memory.imageUrl()
);
return memoryRepository.save(updatedMemoryModel);
}

//Not Used
public List<MemoryModel> getMemoriesByMatchId(int matchId) {
return memoryRepository.findAll().stream()
.filter(memory -> memory.matchId() == matchId)
.toList();
}


}
69 changes: 60 additions & 9 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Footer from "./components/Footer.tsx";
import Navbar from "./components/Navbar.tsx";
import {useEffect, useState} from "react";
import axios from "axios";
import {Route, Routes} from "react-router-dom";
import {Route, Routes, useLocation} from "react-router-dom";
import Home from "./components/Home.tsx";
import NotFound from "./components/NotFound.tsx";
import {MemoryModel} from "./components/model/MemoryModel.ts";
Expand All @@ -14,14 +14,27 @@ import AddMemoryCard from "./components/AddMemoryCard.tsx";
import MyMemories from "./components/MyMemories.tsx";
import {UserDetails} from "./components/model/UserDetailsModel.ts";
import Details from "./components/Details.tsx";
import Favorites from "./components/Favorites.tsx";

export default function App() {

const [user, setUser] = useState<string>("anonymousUser");
const [userDetails, setUserDetails] = useState<UserDetails | null>(null);
const [activeMemories, setActiveMemories] = useState<MemoryModel[]>([]);
const [allMemories, setAllMemories] = useState<MemoryModel[]>([]);
const [favorites, setFavorites] = useState<string[]>([]);
const [showSearch, setShowSearch] = useState<boolean>(false);
const [currentPage, setCurrentPage] = useState<number>(1);

const resetCurrentPage = () => {
setCurrentPage(1);
};

const location = useLocation();

const toggleSearchBar = () => {
setShowSearch((prevState) => !prevState); // Toggle die Sichtbarkeit der Suchleiste// Setzt die Suchanfrage zurück
};

function getUser() {
axios.get("/api/users/me")
Expand All @@ -45,6 +58,17 @@ export default function App() {
});
}

function getAppUserFavorites(){
axios.get(`/api/memory-hub/favorites`)
.then((response) => {
const favoriteIds = response.data.map((favorite: any) => favorite.id);
setFavorites(favoriteIds);
})
.catch((error) => {
console.error(error);
});
}

const getActiveMemories = () => {
axios
.get("/api/memory-hub/active")
Expand All @@ -67,33 +91,60 @@ export default function App() {
});
}

function toggleFavorite(memoryId: string) {
const isFavorite = favorites.includes(memoryId);

if (isFavorite) {
axios.delete(`/api/memory-hub/favorites/${memoryId}`)
.then(() => {
setFavorites((prevFavorites) =>
prevFavorites.filter((id) => id !== memoryId)
);
})
.catch((error) => console.error(error));
} else {
axios.post(`/api/memory-hub/favorites/${memoryId}`)
.then(() => {
setFavorites((prevFavorites) => [...prevFavorites, memoryId]);
})
.catch((error) => console.error(error));
}
}

const handleNewMemorySubmit = (newMemory: MemoryModel) => {
setAllMemories((prevMemories) => [...prevMemories, newMemory]);
}

useEffect(() => {
getUser();
getActiveMemories();
getAllMemories();
}, []);

useEffect(() => {
if (user !== "anonymousUser") {
getUserDetails();
getAllMemories();
getAppUserFavorites();
}
}, [user]);

useEffect(() => {
window.scroll(0, 0);
}, [location]);

return (
<>
<Navbar
user={user}
getUser={getUser}
/>
<Navbar user={user} getUser={getUser} getActiveMemories={getActiveMemories} getAllMemories={getAllMemories} toggleSearchBar={toggleSearchBar} showSearch={showSearch} resetCurrentPage={resetCurrentPage}/>
<Routes>
<Route path="*" element={<NotFound />} />
<Route path="/" element={<Home activeMemories={activeMemories} />} />
<Route path="/" element={<Home activeMemories={activeMemories} toggleFavorite={toggleFavorite} favorites={favorites} user={user} showSearch={showSearch} currentPage={currentPage} paginate={setCurrentPage}/>} />
<Route path="/play" element={<Play activeMemories={activeMemories} />} />
<Route path="/memory/:id" element={<Details allMemories={allMemories} />} />

<Route element={<ProtectedRoute user={user} />}>
<Route path="/my-memories" element={<MyMemories />} />
<Route path="/add" element={<AddMemoryCard userDetails={userDetails}/>} />
<Route path="/favorites" element={<Favorites favorites={favorites} user={user} toggleFavorite={toggleFavorite}/>} />
<Route path="/my-memories" element={<MyMemories userDetails={userDetails} user={user} favorites={favorites} toggleFavorite={toggleFavorite} allMemories={allMemories} setAllMemories={setAllMemories}/>} />
<Route path="/add" element={<AddMemoryCard userDetails={userDetails} handleSubmit={handleNewMemorySubmit} />} />
<Route path="/profile" element={<Profile userDetails={userDetails} />} />
</Route>
</Routes>
Expand Down
38 changes: 21 additions & 17 deletions frontend/src/components/AddMemoryCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ import { UserDetails } from "./model/UserDetailsModel.ts";
import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import "./styles/AddMemoryCard.css";
import "./styles/Buttons.css";
import "./styles/Popup.css";
import {MemoryModel} from "./model/MemoryModel.ts";

type MemoryCardProps = {
userDetails: UserDetails | null;
handleSubmit: (memory: MemoryModel) => void;
}

export default function AddMemoryCard(props: Readonly<MemoryCardProps>) {
Expand Down Expand Up @@ -83,7 +88,9 @@ export default function AddMemoryCard(props: Readonly<MemoryCardProps>) {

const onFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files) {
setImage(e.target.files[0]);
const file = e.target.files[0];
setImage(file);
setImageUrl("temp-image"); // Temporärer Wert, um die Validierung zu bestehen
}
}

Expand All @@ -92,14 +99,6 @@ export default function AddMemoryCard(props: Readonly<MemoryCardProps>) {
setErrorMessages([]);
};

// onChange für den matchId-Input
const onMatchIdChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = parseInt(e.target.value); // Umwandlung von String in Zahl
if (!isNaN(value)) {
setMatchId(Math.min(20, Math.max(1, value))); // Stellen sicher, dass der Wert im Bereich 1 bis 20 liegt
}
};

return (
<div className="edit-form">
<h2>Add Memory Card</h2>
Expand All @@ -115,7 +114,7 @@ export default function AddMemoryCard(props: Readonly<MemoryCardProps>) {
</label>

<label>
Category:
Bildquelle:
<select
className="input-small"
value={category}
Expand All @@ -137,17 +136,22 @@ export default function AddMemoryCard(props: Readonly<MemoryCardProps>) {
</label>

<label>
Match ID (default 1, max 20):
<input
Match ID:
<select
className="input-small"
type="number"
value={matchId}
onChange={onMatchIdChange} // Verwendung der angepassten Methode
min="1"
max="20"
/>
onChange={(e) => setMatchId(Number(e.target.value))}
required
>
{[...Array(20).keys()].map((n) => n + 1).map((n) => (
<option key={n} value={n}>
{n}
</option>
))}
</select>
</label>


<label>
Image:
<input
Expand Down
1 change: 0 additions & 1 deletion frontend/src/components/Details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export default function Details(props: Readonly<DetailsProps>) {

return (
<div>
<h2>Memory Details</h2>
{memory ? (
<div>
<h3>{memory.name}</h3>
Expand Down
38 changes: 38 additions & 0 deletions frontend/src/components/Favorites.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {MemoryModel} from "./model/MemoryModel.ts";
import {useEffect, useState} from "react";
import axios from "axios";
import MemoryCard from "./MemoryCard.tsx";

type FavoritesProps = {
favorites: string[];
user: string;
toggleFavorite: (memoryId: string) => void;
}

export default function Favorites(props: Readonly<FavoritesProps>) {
const [favoritesMemories, setFavoritesMemories] = useState<MemoryModel[]>([]);

useEffect(() => {
axios
.get(`/api/memories/favorites`)
.then((response) => {
setFavoritesMemories(response.data);
})
.catch((error) => {
console.error(error);
});
}, [props.user, props.favorites]);

return (
<div className={"memory-card-container"}>
{favoritesMemories.length > 0 ? (
favoritesMemories.map((memory) => (
<MemoryCard key={memory.id} memory={memory} user={props.user} favorites={props.favorites} toggleFavorite={props.toggleFavorite} />
))
) : (
<p>No memories in favorites.</p>
)}

</div>
);
}
Loading

0 comments on commit b537953

Please sign in to comment.