-
Wird geladen…
+
+
+
+
+
+
+
+
+
+
Veranstaltung bearbeiten
+
+
🗓
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
@@ -178,6 +182,8 @@
if (user) {
document.getElementById('greeting').textContent = 'Willkommen zurück, ' + user.name + '!';
loadVisitors();
+ loadMyEvents();
+ loadLocEvents();
if (user.datingAktiv) {
loadWhoLikesMe();
loadMatches();
@@ -257,6 +263,47 @@
return String(s).replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"');
}
+ function renderEventCards(events, listId, sectionId) {
+ if (!events.length) return;
+ const list = document.getElementById(listId);
+ list.innerHTML = events.map(e => {
+ const thumb = e.imageData
+ ? `

`
+ : '🗓';
+ const date = new Date(e.startAt);
+ const dateStr = date.toLocaleDateString('de-DE', { weekday:'short', day:'numeric', month:'short' })
+ + ', ' + date.toLocaleTimeString('de-DE', { hour:'2-digit', minute:'2-digit' }) + ' Uhr';
+ return `
+
+ ${thumb}
+
+
${esc(e.locationName)}
+
${esc(e.title)}
+
${dateStr}
+
+ `;
+ }).join('');
+ document.getElementById(sectionId).style.display = '';
+ }
+
+ async function loadMyEvents() {
+ try {
+ const res = await fetch('/location-events/attending-next');
+ if (!res.ok) return;
+ const events = await res.json();
+ renderEventCards(events, 'myEventsList', 'myEventsSection');
+ } catch (_) {}
+ }
+
+ async function loadLocEvents() {
+ try {
+ const res = await fetch('/location-events/followed-next');
+ if (!res.ok) return;
+ const events = await res.json();
+ renderEventCards(events, 'locEventsList', 'locEventsSection');
+ } catch (_) {}
+ }
+
async function loadVisitors() {
try {
const res = await fetch('/social/profile-visits/my-visitors');
diff --git a/src/main/java/de/oaa/xxx/feed/FeedController.java b/src/main/java/de/oaa/xxx/feed/FeedController.java
index f4007e1..2a65fbe 100644
--- a/src/main/java/de/oaa/xxx/feed/FeedController.java
+++ b/src/main/java/de/oaa/xxx/feed/FeedController.java
@@ -8,6 +8,8 @@ import java.util.List;
import java.util.UUID;
import java.util.stream.Stream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.http.ResponseEntity;
@@ -47,9 +49,6 @@ import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
@RestController
@RequestMapping("/feed")
public class FeedController {
@@ -365,7 +364,8 @@ public class FeedController {
p.getCreatedAt(),
likeCount, likedByMe, kommentarCount,
optionen, myVoteOptionIds,
- p.isPublic()
+ p.isPublic(),
+ p.getTargetUrl()
);
}
@@ -402,7 +402,8 @@ public class FeedController {
b.getCreatedAt(),
likeCount, likedByMe, kommentarCount,
optionen, myVoteOptionIds,
- false
+ false,
+ null
);
}
}
diff --git a/src/main/java/de/oaa/xxx/feed/dto/FeedItemDto.java b/src/main/java/de/oaa/xxx/feed/dto/FeedItemDto.java
index 67e1122..f2e1c7b 100644
--- a/src/main/java/de/oaa/xxx/feed/dto/FeedItemDto.java
+++ b/src/main/java/de/oaa/xxx/feed/dto/FeedItemDto.java
@@ -24,5 +24,6 @@ public record FeedItemDto(
long kommentarCount,
List
optionen,
List myVoteOptionIds,
- boolean isPublic
+ boolean isPublic,
+ String targetUrl
) {}
diff --git a/src/main/java/de/oaa/xxx/feed/entity/FeedPostEntity.java b/src/main/java/de/oaa/xxx/feed/entity/FeedPostEntity.java
index cb682f8..fc0a6ec 100644
--- a/src/main/java/de/oaa/xxx/feed/entity/FeedPostEntity.java
+++ b/src/main/java/de/oaa/xxx/feed/entity/FeedPostEntity.java
@@ -42,4 +42,7 @@ public class FeedPostEntity {
@Column(nullable = false)
private LocalDateTime createdAt;
+
+ @Column(columnDefinition = "TEXT")
+ private String targetUrl;
}
diff --git a/src/main/java/de/oaa/xxx/location/LocationAdminController.java b/src/main/java/de/oaa/xxx/location/LocationAdminController.java
new file mode 100644
index 0000000..3ad47fc
--- /dev/null
+++ b/src/main/java/de/oaa/xxx/location/LocationAdminController.java
@@ -0,0 +1,176 @@
+package de.oaa.xxx.location;
+
+import de.oaa.xxx.location.entity.LocationAdminEntity;
+import de.oaa.xxx.location.repository.LocationAdminRepository;
+import de.oaa.xxx.location.repository.LocationRepository;
+import de.oaa.xxx.user.UserRepository;
+import de.oaa.xxx.user.UserService;
+import jakarta.transaction.Transactional;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.security.Principal;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+@RestController
+@RequestMapping("/locations/{locationId}/admins")
+public class LocationAdminController {
+
+ record AdminDto(UUID userId, String name, String profilePicture, boolean isOwner) {}
+ record AddAdminRequest(UUID userId) {}
+ record TransferOwnerRequest(UUID userId) {}
+
+ private final LocationRepository locationRepo;
+ private final LocationAdminRepository adminRepo;
+ private final UserRepository userRepo;
+ private final UserService userService;
+
+ public LocationAdminController(LocationRepository locationRepo,
+ LocationAdminRepository adminRepo,
+ UserRepository userRepo,
+ UserService userService) {
+ this.locationRepo = locationRepo;
+ this.adminRepo = adminRepo;
+ this.userRepo = userRepo;
+ this.userService = userService;
+ }
+
+ // ── Hilfsmethoden ────────────────────────────────────────────────────────
+
+ private boolean isAdmin(UUID locationId, UUID userId, de.oaa.xxx.location.entity.LocationEntity loc) {
+ return loc.getOwnerId().equals(userId)
+ || adminRepo.existsByLocationIdAndUserId(locationId, userId);
+ }
+
+ private AdminDto toDto(UUID userId, de.oaa.xxx.location.entity.LocationEntity loc) {
+ return userRepo.findById(userId).map(u -> new AdminDto(
+ u.getUserId(), u.getName(), u.getProfilePicture(),
+ u.getUserId().equals(loc.getOwnerId())))
+ .orElse(null);
+ }
+
+ // ── Admins auflisten ─────────────────────────────────────────────────────
+
+ @GetMapping
+ public ResponseEntity> list(
+ @PathVariable UUID locationId,
+ Principal principal) {
+
+ userService.requireUser(principal);
+ var locOpt = locationRepo.findById(locationId);
+ if (locOpt.isEmpty()) return ResponseEntity.notFound().build();
+ var loc = locOpt.get();
+
+ // Inhaber ist immer erster Eintrag
+ List admins = new java.util.ArrayList<>();
+ userRepo.findById(loc.getOwnerId()).ifPresent(owner ->
+ admins.add(new AdminDto(owner.getUserId(), owner.getName(), owner.getProfilePicture(), true)));
+
+ adminRepo.findByLocationId(locationId).stream()
+ .filter(a -> !a.getUserId().equals(loc.getOwnerId())) // Inhaber nicht doppelt
+ .map(a -> toDto(a.getUserId(), loc))
+ .filter(java.util.Objects::nonNull)
+ .forEach(admins::add);
+
+ return ResponseEntity.ok(admins);
+ }
+
+ // ── Admin hinzufügen ──────────────────────────────────────────────────────
+
+ @PostMapping
+ public ResponseEntity add(
+ @PathVariable UUID locationId,
+ @RequestBody AddAdminRequest req,
+ Principal principal) {
+
+ UUID myId = userService.requireUser(principal).getUserId();
+ var locOpt = locationRepo.findById(locationId);
+ if (locOpt.isEmpty()) return ResponseEntity.notFound().build();
+ var loc = locOpt.get();
+
+ if (!isAdmin(locationId, myId, loc)) return ResponseEntity.status(403).build();
+ if (req.userId() == null || !userRepo.existsById(req.userId()))
+ return ResponseEntity.badRequest().build();
+
+ // Inhaber muss nicht eingetragen werden
+ if (req.userId().equals(loc.getOwnerId()))
+ return ResponseEntity.badRequest().build();
+
+ // Bereits Admin?
+ if (adminRepo.existsByLocationIdAndUserId(locationId, req.userId()))
+ return ResponseEntity.status(409).build();
+
+ LocationAdminEntity entity = new LocationAdminEntity();
+ entity.setAdminId(UUID.randomUUID());
+ entity.setLocationId(locationId);
+ entity.setUserId(req.userId());
+ entity.setAddedAt(LocalDateTime.now());
+ adminRepo.save(entity);
+
+ return ResponseEntity.status(201).body(toDto(req.userId(), loc));
+ }
+
+ // ── Admin entfernen ───────────────────────────────────────────────────────
+
+ @Transactional
+ @DeleteMapping("/{userId}")
+ public ResponseEntity remove(
+ @PathVariable UUID locationId,
+ @PathVariable UUID userId,
+ Principal principal) {
+
+ UUID myId = userService.requireUser(principal).getUserId();
+ var locOpt = locationRepo.findById(locationId);
+ if (locOpt.isEmpty()) return ResponseEntity.notFound().build();
+ var loc = locOpt.get();
+
+ if (!isAdmin(locationId, myId, loc)) return ResponseEntity.status(403).build();
+
+ // Inhaber darf nicht entfernt werden
+ if (userId.equals(loc.getOwnerId())) return ResponseEntity.status(403).build();
+
+ adminRepo.deleteByLocationIdAndUserId(locationId, userId);
+ return ResponseEntity.noContent().build();
+ }
+
+ // ── Inhaberwechsel ────────────────────────────────────────────────────────
+
+ @Transactional
+ @PutMapping("/transfer-owner")
+ public ResponseEntity