Added admin dashboard, refactored user dashboard. Removed old reviews route.
All checks were successful
CI - Build Tonehaus Docker image / tonehaus-ci-build (push) Successful in 1m53s
All checks were successful
CI - Build Tonehaus Docker image / tonehaus-ci-build (push) Successful in 1m53s
This commit is contained in:
@@ -24,6 +24,7 @@ class AlbumController extends AbstractController
|
||||
$query = trim((string) $request->query->get('q', ''));
|
||||
$albumName = trim($request->query->getString('album', ''));
|
||||
$artist = trim($request->query->getString('artist', ''));
|
||||
$source = $request->query->getString('source', 'all'); // 'all' | 'spotify' | 'user'
|
||||
// Accept empty strings and validate manually to avoid FILTER_NULL_ON_FAILURE issues
|
||||
$yearFromRaw = trim((string) $request->query->get('year_from', ''));
|
||||
$yearToRaw = trim((string) $request->query->get('year_to', ''));
|
||||
@@ -53,11 +54,11 @@ class AlbumController extends AbstractController
|
||||
$q = implode(' ', $parts);
|
||||
}
|
||||
|
||||
if ($q !== '') {
|
||||
if ($q !== '' || $source === 'user') {
|
||||
$result = $spotifyClient->searchAlbums($q, 20);
|
||||
$searchItems = $result['albums']['items'] ?? [];
|
||||
$logger->info('Album search results received', ['query' => $q, 'items' => is_countable($searchItems) ? count($searchItems) : 0]);
|
||||
if ($searchItems) {
|
||||
if ($searchItems && ($source === 'all' || $source === 'spotify')) {
|
||||
// Build ordered list of IDs from search results
|
||||
$ids = [];
|
||||
foreach ($searchItems as $it) {
|
||||
@@ -84,7 +85,9 @@ class AlbumController extends AbstractController
|
||||
$logger->info('Albums upserted to DB', ['upserted' => $upserted]);
|
||||
|
||||
if ($ids) {
|
||||
$stats = $reviewRepository->getAggregatesForAlbumIds($ids);
|
||||
if ($source === 'spotify' || $source === 'all') {
|
||||
$stats = $reviewRepository->getAggregatesForAlbumIds($ids);
|
||||
}
|
||||
$existing = $albumsRepo->findBySpotifyIdsKeyed($ids);
|
||||
$savedIds = array_keys($existing);
|
||||
// Preserve Spotify order and render from DB
|
||||
@@ -96,6 +99,25 @@ class AlbumController extends AbstractController
|
||||
}
|
||||
}
|
||||
}
|
||||
// User-created search results
|
||||
if ($source === 'user' || $source === 'all') {
|
||||
$userAlbums = $albumsRepo->searchUserAlbums($albumName, $artist, $yearFrom, $yearTo, 20);
|
||||
if ($userAlbums) {
|
||||
$entityIds = array_values(array_map(static fn($a) => $a->getId(), $userAlbums));
|
||||
$userStatsByEntityId = $reviewRepository->getAggregatesForAlbumEntityIds($entityIds);
|
||||
// Merge into stats keyed by localId
|
||||
foreach ($userAlbums as $ua) {
|
||||
$localId = (string) $ua->getLocalId();
|
||||
$entityId = (int) $ua->getId();
|
||||
if (isset($userStatsByEntityId[$entityId])) {
|
||||
$stats[$localId] = $userStatsByEntityId[$entityId];
|
||||
}
|
||||
}
|
||||
$userAlbumPayloads = array_map(static fn($a) => $a->toTemplateArray(), $userAlbums);
|
||||
// Prepend user albums to list
|
||||
$albums = array_merge($userAlbumPayloads, $albums);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('album/search.html.twig', [
|
||||
@@ -107,6 +129,7 @@ class AlbumController extends AbstractController
|
||||
'albums' => $albums,
|
||||
'stats' => $stats,
|
||||
'savedIds' => $savedIds,
|
||||
'source' => $source,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -123,6 +146,8 @@ class AlbumController extends AbstractController
|
||||
$artistsCsv = (string) $form->get('artistsCsv')->getData();
|
||||
$artists = array_values(array_filter(array_map(static fn($s) => trim((string) $s), explode(',', $artistsCsv)), static fn($s) => $s !== ''));
|
||||
$album->setArtists($artists);
|
||||
// Normalize release date to YYYY-MM-DD
|
||||
$album->setReleaseDate($this->normalizeReleaseDate($album->getReleaseDate()));
|
||||
// Assign createdBy and generate unique localId
|
||||
$u = $this->getUser();
|
||||
if ($u instanceof \App\Entity\User) {
|
||||
@@ -154,6 +179,14 @@ class AlbumController extends AbstractController
|
||||
}
|
||||
$isSaved = $albumEntity !== null;
|
||||
$album = $albumEntity->toTemplateArray();
|
||||
$isAdmin = $this->isGranted('ROLE_ADMIN');
|
||||
$current = $this->getUser();
|
||||
$isOwner = false;
|
||||
if ($current instanceof \App\Entity\User) {
|
||||
$isOwner = ($albumEntity->getCreatedBy()?->getId() === $current->getId());
|
||||
}
|
||||
$allowedEdit = $isAdmin || ($albumEntity->getSource() === 'user' && $isOwner);
|
||||
$allowedDelete = $isAdmin || ($albumEntity->getSource() === 'user' && $isOwner);
|
||||
|
||||
$existing = $reviews->findBy(['album' => $albumEntity], ['createdAt' => 'DESC']);
|
||||
$count = count($existing);
|
||||
@@ -183,6 +216,8 @@ class AlbumController extends AbstractController
|
||||
'album' => $album,
|
||||
'albumId' => $id,
|
||||
'isSaved' => $isSaved,
|
||||
'allowedEdit' => $allowedEdit,
|
||||
'allowedDelete' => $allowedDelete,
|
||||
'reviews' => $existing,
|
||||
'avg' => $avg,
|
||||
'count' => $count,
|
||||
@@ -255,6 +290,75 @@ class AlbumController extends AbstractController
|
||||
} while ($albumsRepo->findOneByLocalId($id) !== null);
|
||||
return $id;
|
||||
}
|
||||
|
||||
private function normalizeReleaseDate(?string $input): ?string
|
||||
{
|
||||
if ($input === null || trim($input) === '') {
|
||||
return null;
|
||||
}
|
||||
$s = trim($input);
|
||||
// YYYY
|
||||
if (preg_match('/^\d{4}$/', $s)) {
|
||||
return $s . '-01-01';
|
||||
}
|
||||
// YYYY-MM
|
||||
if (preg_match('/^\d{4}-\d{2}$/', $s)) {
|
||||
return $s . '-01';
|
||||
}
|
||||
// YYYY-MM-DD
|
||||
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $s)) {
|
||||
return $s;
|
||||
}
|
||||
// Fallback: attempt to parse
|
||||
try {
|
||||
$dt = new \DateTimeImmutable($s);
|
||||
return $dt->format('Y-m-d');
|
||||
} catch (\Throwable) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#[IsGranted('ROLE_USER')]
|
||||
#[Route('/albums/{id}/edit', name: 'album_edit', methods: ['GET', 'POST'])]
|
||||
public function edit(string $id, Request $request, AlbumRepository $albumsRepo, EntityManagerInterface $em): Response
|
||||
{
|
||||
$album = str_starts_with($id, 'u_') ? $albumsRepo->findOneByLocalId($id) : $albumsRepo->findOneBySpotifyId($id);
|
||||
if (!$album) {
|
||||
throw $this->createNotFoundException('Album not found');
|
||||
}
|
||||
$isAdmin = $this->isGranted('ROLE_ADMIN');
|
||||
$current = $this->getUser();
|
||||
$isOwner = false;
|
||||
if ($current instanceof \App\Entity\User) {
|
||||
$isOwner = ($album->getCreatedBy()?->getId() === $current->getId());
|
||||
}
|
||||
if ($album->getSource() === 'user') {
|
||||
if (!$isAdmin && !$isOwner) {
|
||||
throw $this->createAccessDeniedException();
|
||||
}
|
||||
} else {
|
||||
if (!$isAdmin) {
|
||||
throw $this->createAccessDeniedException();
|
||||
}
|
||||
}
|
||||
$form = $this->createForm(AlbumType::class, $album);
|
||||
// Prepopulate artistsCsv
|
||||
$form->get('artistsCsv')->setData(implode(', ', $album->getArtists()));
|
||||
$form->handleRequest($request);
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$artistsCsv = (string) $form->get('artistsCsv')->getData();
|
||||
$artists = array_values(array_filter(array_map(static fn($s) => trim((string) $s), explode(',', $artistsCsv)), static fn($s) => $s !== ''));
|
||||
$album->setArtists($artists);
|
||||
$album->setReleaseDate($this->normalizeReleaseDate($album->getReleaseDate()));
|
||||
$em->flush();
|
||||
$this->addFlash('success', 'Album updated.');
|
||||
return $this->redirectToRoute('album_show', ['id' => $id]);
|
||||
}
|
||||
return $this->render('album/edit.html.twig', [
|
||||
'form' => $form->createView(),
|
||||
'albumId' => $id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user