367 lines
12 KiB
PHP
367 lines
12 KiB
PHP
|
|
<?php
|
||
|
|
$pageTitle = $title ?? 'Releases';
|
||
|
|
$releases = is_array($releases ?? null) ? $releases : [];
|
||
|
|
$releaseCount = (int)($total_releases ?? count($releases));
|
||
|
|
$artistFilter = trim((string)($artist_filter ?? ''));
|
||
|
|
$artistOptions = is_array($artist_options ?? null) ? $artist_options : [];
|
||
|
|
$search = trim((string)($search ?? ''));
|
||
|
|
$sort = trim((string)($sort ?? 'newest'));
|
||
|
|
$currentPage = max(1, (int)($current_page ?? 1));
|
||
|
|
$totalPages = max(1, (int)($total_pages ?? 1));
|
||
|
|
|
||
|
|
$buildReleaseUrl = static function (int $page) use ($search, $artistFilter, $sort): string {
|
||
|
|
$params = [];
|
||
|
|
if ($search !== '') {
|
||
|
|
$params['q'] = $search;
|
||
|
|
}
|
||
|
|
if ($artistFilter !== '') {
|
||
|
|
$params['artist'] = $artistFilter;
|
||
|
|
}
|
||
|
|
if ($sort !== 'newest') {
|
||
|
|
$params['sort'] = $sort;
|
||
|
|
}
|
||
|
|
if ($page > 1) {
|
||
|
|
$params['p'] = $page;
|
||
|
|
}
|
||
|
|
$qs = http_build_query($params);
|
||
|
|
return '/releases' . ($qs !== '' ? ('?' . $qs) : '');
|
||
|
|
};
|
||
|
|
|
||
|
|
ob_start();
|
||
|
|
?>
|
||
|
|
|
||
|
|
<div class="ac-releases-page">
|
||
|
|
<section class="card ac-releases-shell">
|
||
|
|
<div class="ac-releases-header">
|
||
|
|
<div class="badge">Releases</div>
|
||
|
|
<h1>Latest Drops</h1>
|
||
|
|
<p>Singles, EPs, and albums from the AudioCore catalog.</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<form method="get" action="/releases" class="ac-release-controls">
|
||
|
|
<div class="ac-search-wrap">
|
||
|
|
<span class="ac-search-icon"><i class="fa-solid fa-magnifying-glass"></i></span>
|
||
|
|
<input class="ac-search-input" type="text" name="q" value="<?= htmlspecialchars($search, ENT_QUOTES, 'UTF-8') ?>" placeholder="Search releases, artists, catalog number">
|
||
|
|
</div>
|
||
|
|
<select class="ac-control-input" name="artist">
|
||
|
|
<option value="">All artists</option>
|
||
|
|
<?php foreach ($artistOptions as $artist): ?>
|
||
|
|
<option value="<?= htmlspecialchars((string)$artist, ENT_QUOTES, 'UTF-8') ?>" <?= $artistFilter === (string)$artist ? 'selected' : '' ?>>
|
||
|
|
<?= htmlspecialchars((string)$artist, ENT_QUOTES, 'UTF-8') ?>
|
||
|
|
</option>
|
||
|
|
<?php endforeach; ?>
|
||
|
|
</select>
|
||
|
|
<select class="ac-control-input" name="sort">
|
||
|
|
<option value="newest" <?= $sort === 'newest' ? 'selected' : '' ?>>Newest first</option>
|
||
|
|
<option value="oldest" <?= $sort === 'oldest' ? 'selected' : '' ?>>Oldest first</option>
|
||
|
|
<option value="title_asc" <?= $sort === 'title_asc' ? 'selected' : '' ?>>Title A-Z</option>
|
||
|
|
<option value="title_desc" <?= $sort === 'title_desc' ? 'selected' : '' ?>>Title Z-A</option>
|
||
|
|
</select>
|
||
|
|
<button type="submit" class="ac-btn ac-btn-primary">Apply</button>
|
||
|
|
<a href="/releases" class="ac-btn ac-btn-ghost">Reset</a>
|
||
|
|
</form>
|
||
|
|
|
||
|
|
<?php if ($artistFilter !== '' || $search !== '' || $sort !== 'newest'): ?>
|
||
|
|
<div class="ac-active-filters">
|
||
|
|
<?php if ($artistFilter !== ''): ?><div class="ac-chip">Artist: <?= htmlspecialchars($artistFilter, ENT_QUOTES, 'UTF-8') ?></div><?php endif; ?>
|
||
|
|
<?php if ($search !== ''): ?><div class="ac-chip">Search: <?= htmlspecialchars($search, ENT_QUOTES, 'UTF-8') ?></div><?php endif; ?>
|
||
|
|
<?php if ($sort !== 'newest'): ?><div class="ac-chip">Sort: <?= htmlspecialchars(str_replace('_', ' ', $sort), ENT_QUOTES, 'UTF-8') ?></div><?php endif; ?>
|
||
|
|
</div>
|
||
|
|
<?php endif; ?>
|
||
|
|
|
||
|
|
<?php if (!$releases): ?>
|
||
|
|
<div class="ac-empty">No releases published yet.</div>
|
||
|
|
<?php else: ?>
|
||
|
|
<div class="ac-release-grid">
|
||
|
|
<?php foreach ($releases as $release): ?>
|
||
|
|
<a class="ac-release-card" href="/release?slug=<?= htmlspecialchars((string)$release['slug'], ENT_QUOTES, 'UTF-8') ?>">
|
||
|
|
<div class="ac-release-cover">
|
||
|
|
<?php if (!empty($release['cover_url'])): ?>
|
||
|
|
<img src="<?= htmlspecialchars((string)$release['cover_url'], ENT_QUOTES, 'UTF-8') ?>" alt="">
|
||
|
|
<?php else: ?>
|
||
|
|
<div class="ac-release-placeholder">AC</div>
|
||
|
|
<?php endif; ?>
|
||
|
|
<?php if (!empty($release['release_date'])): ?>
|
||
|
|
<span class="ac-release-date"><?= htmlspecialchars((string)$release['release_date'], ENT_QUOTES, 'UTF-8') ?></span>
|
||
|
|
<?php endif; ?>
|
||
|
|
</div>
|
||
|
|
<div class="ac-release-meta">
|
||
|
|
<div class="ac-release-title"><?= htmlspecialchars((string)$release['title'], ENT_QUOTES, 'UTF-8') ?></div>
|
||
|
|
<?php if (!empty($release['artist_name'])): ?>
|
||
|
|
<div class="ac-release-artist"><?= htmlspecialchars((string)$release['artist_name'], ENT_QUOTES, 'UTF-8') ?></div>
|
||
|
|
<?php endif; ?>
|
||
|
|
</div>
|
||
|
|
</a>
|
||
|
|
<?php endforeach; ?>
|
||
|
|
</div>
|
||
|
|
<?php endif; ?>
|
||
|
|
|
||
|
|
<?php if ($totalPages > 1): ?>
|
||
|
|
<nav class="ac-release-pagination">
|
||
|
|
<?php $prevPage = max(1, $currentPage - 1); ?>
|
||
|
|
<?php $nextPage = min($totalPages, $currentPage + 1); ?>
|
||
|
|
<a class="ac-btn ac-btn-ghost<?= $currentPage <= 1 ? ' is-disabled' : '' ?>" href="<?= $currentPage <= 1 ? '#' : htmlspecialchars($buildReleaseUrl($prevPage), ENT_QUOTES, 'UTF-8') ?>">Prev</a>
|
||
|
|
<div class="ac-pagination-meta">Page <?= $currentPage ?> of <?= $totalPages ?> · <?= $releaseCount ?> total</div>
|
||
|
|
<a class="ac-btn ac-btn-ghost<?= $currentPage >= $totalPages ? ' is-disabled' : '' ?>" href="<?= $currentPage >= $totalPages ? '#' : htmlspecialchars($buildReleaseUrl($nextPage), ENT_QUOTES, 'UTF-8') ?>">Next</a>
|
||
|
|
</nav>
|
||
|
|
<?php endif; ?>
|
||
|
|
</section>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<style>
|
||
|
|
.ac-releases-page .ac-releases-shell {
|
||
|
|
margin-top: 14px;
|
||
|
|
display: grid;
|
||
|
|
gap: 14px;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-releases-header {
|
||
|
|
border-bottom: 1px solid rgba(255,255,255,0.06);
|
||
|
|
padding-bottom: 10px;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-releases-header h1 {
|
||
|
|
margin: 8px 0 0;
|
||
|
|
font-size: 52px;
|
||
|
|
line-height: 1.05;
|
||
|
|
letter-spacing: -0.02em;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-releases-header p {
|
||
|
|
margin: 8px 0 0;
|
||
|
|
color: var(--muted);
|
||
|
|
font-size: 14px;
|
||
|
|
}
|
||
|
|
|
||
|
|
.ac-releases-page .ac-release-controls {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: minmax(260px, 1fr) 180px 180px auto auto;
|
||
|
|
gap: 10px;
|
||
|
|
align-items: center;
|
||
|
|
padding: 8px;
|
||
|
|
border: 1px solid rgba(255,255,255,0.07);
|
||
|
|
border-radius: 14px;
|
||
|
|
background: rgba(8, 12, 19, 0.16);
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-search-wrap {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 8px;
|
||
|
|
min-width: 0;
|
||
|
|
border: 1px solid rgba(255,255,255,0.07);
|
||
|
|
border-radius: 10px;
|
||
|
|
background: rgba(7,10,16,0.10);
|
||
|
|
height: 36px;
|
||
|
|
padding: 0 10px;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-search-icon {
|
||
|
|
color: rgba(255,255,255,0.72);
|
||
|
|
font-size: 12px;
|
||
|
|
width: 16px;
|
||
|
|
display: inline-flex;
|
||
|
|
justify-content: center;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-search-input,
|
||
|
|
.ac-releases-page .ac-control-input {
|
||
|
|
width: 100%;
|
||
|
|
height: 36px;
|
||
|
|
border-radius: 10px;
|
||
|
|
border: 1px solid rgba(255,255,255,0.07);
|
||
|
|
background: rgba(7,10,16,0.10);
|
||
|
|
color: rgba(233,237,247,0.94);
|
||
|
|
padding: 0 10px;
|
||
|
|
font-size: 13px;
|
||
|
|
outline: none;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-search-input {
|
||
|
|
border: 0;
|
||
|
|
background: transparent;
|
||
|
|
padding: 0;
|
||
|
|
min-width: 0;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-search-input::placeholder {
|
||
|
|
color: rgba(220,228,245,.45);
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-search-input:focus,
|
||
|
|
.ac-releases-page .ac-control-input:focus {
|
||
|
|
box-shadow: 0 0 0 2px rgba(34,242,165,.12);
|
||
|
|
border-color: rgba(34,242,165,.38);
|
||
|
|
}
|
||
|
|
|
||
|
|
.ac-releases-page .ac-btn {
|
||
|
|
display: inline-flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
height: 36px;
|
||
|
|
padding: 0 14px;
|
||
|
|
border-radius: 10px;
|
||
|
|
border: 1px solid rgba(255,255,255,.10);
|
||
|
|
text-decoration: none;
|
||
|
|
font-size: 10px;
|
||
|
|
text-transform: uppercase;
|
||
|
|
letter-spacing: .12em;
|
||
|
|
font-weight: 700;
|
||
|
|
cursor: pointer;
|
||
|
|
transition: all .2s ease;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-btn-primary {
|
||
|
|
background: rgba(34,242,165,.14);
|
||
|
|
color: #87f2cb;
|
||
|
|
border-color: rgba(34,242,165,.34);
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-btn-primary:hover {
|
||
|
|
background: rgba(34,242,165,.20);
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-btn-ghost {
|
||
|
|
color: #a1acc4;
|
||
|
|
background: transparent;
|
||
|
|
border-color: rgba(255,255,255,.10);
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-btn-ghost:hover {
|
||
|
|
color: #e5ebf7;
|
||
|
|
border-color: rgba(255,255,255,.18);
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-btn.is-disabled {
|
||
|
|
opacity: .45;
|
||
|
|
pointer-events: none;
|
||
|
|
}
|
||
|
|
|
||
|
|
.ac-releases-page .ac-active-filters {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 8px;
|
||
|
|
flex-wrap: wrap;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-chip {
|
||
|
|
border-radius: 999px;
|
||
|
|
padding: 6px 10px;
|
||
|
|
border: 1px solid rgba(255,255,255,.12);
|
||
|
|
font-size: 10px;
|
||
|
|
letter-spacing: .12em;
|
||
|
|
text-transform: uppercase;
|
||
|
|
color: var(--muted);
|
||
|
|
font-family: 'IBM Plex Mono', monospace;
|
||
|
|
}
|
||
|
|
|
||
|
|
.ac-releases-page .ac-release-grid {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: repeat(auto-fill, minmax(220px, 240px));
|
||
|
|
justify-content: start;
|
||
|
|
gap: 16px;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-release-card {
|
||
|
|
display: grid;
|
||
|
|
gap: 10px;
|
||
|
|
color: inherit;
|
||
|
|
text-decoration: none;
|
||
|
|
padding: 10px;
|
||
|
|
border-radius: 18px;
|
||
|
|
border: 1px solid rgba(255,255,255,.08);
|
||
|
|
background: rgba(0,0,0,.14);
|
||
|
|
transition: border-color .2s ease, transform .2s ease;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-release-card:hover {
|
||
|
|
border-color: rgba(255,255,255,.18);
|
||
|
|
transform: translateY(-2px);
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-release-cover {
|
||
|
|
width: 100%;
|
||
|
|
aspect-ratio: 1 / 1;
|
||
|
|
border-radius: 16px;
|
||
|
|
overflow: hidden;
|
||
|
|
position: relative;
|
||
|
|
background: rgba(255,255,255,.03);
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-release-cover img {
|
||
|
|
width: 100%;
|
||
|
|
height: 100%;
|
||
|
|
object-fit: cover;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-release-date {
|
||
|
|
position: absolute;
|
||
|
|
left: 10px;
|
||
|
|
bottom: 10px;
|
||
|
|
border-radius: 999px;
|
||
|
|
padding: 6px 10px;
|
||
|
|
font-size: 11px;
|
||
|
|
color: #fff;
|
||
|
|
background: rgba(0,0,0,.56);
|
||
|
|
border: 1px solid rgba(255,255,255,.18);
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-release-placeholder {
|
||
|
|
width: 100%;
|
||
|
|
height: 100%;
|
||
|
|
display: grid;
|
||
|
|
place-items: center;
|
||
|
|
color: var(--muted);
|
||
|
|
letter-spacing: .16em;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-release-meta {
|
||
|
|
display: grid;
|
||
|
|
gap: 6px;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-release-title {
|
||
|
|
font-weight: 600;
|
||
|
|
font-size: 18px;
|
||
|
|
line-height: 1.2;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-release-artist {
|
||
|
|
font-size: 13px;
|
||
|
|
color: var(--muted);
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-empty {
|
||
|
|
padding: 16px;
|
||
|
|
border: 1px solid rgba(255,255,255,.08);
|
||
|
|
border-radius: 12px;
|
||
|
|
color: var(--muted);
|
||
|
|
}
|
||
|
|
|
||
|
|
.ac-releases-page .ac-release-pagination {
|
||
|
|
margin-top: 2px;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
gap: 10px;
|
||
|
|
flex-wrap: wrap;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-pagination-meta {
|
||
|
|
font-size: 12px;
|
||
|
|
color: var(--muted);
|
||
|
|
}
|
||
|
|
|
||
|
|
@media (max-width: 1180px) {
|
||
|
|
.ac-releases-page .ac-release-controls {
|
||
|
|
grid-template-columns: 1fr 160px 160px auto auto;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-release-grid {
|
||
|
|
grid-template-columns: repeat(auto-fill, minmax(210px, 230px));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
@media (max-width: 900px) {
|
||
|
|
.ac-releases-page .ac-release-controls {
|
||
|
|
grid-template-columns: 1fr 1fr 1fr;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-release-controls .ac-search-wrap {
|
||
|
|
grid-column: 1 / -1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
@media (max-width: 760px) {
|
||
|
|
.ac-releases-page .ac-releases-header h1 {
|
||
|
|
font-size: 40px;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-release-controls {
|
||
|
|
grid-template-columns: 1fr;
|
||
|
|
}
|
||
|
|
.ac-releases-page .ac-release-grid {
|
||
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
|
|
justify-content: stretch;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
@media (max-width: 520px) {
|
||
|
|
.ac-releases-page .ac-release-grid {
|
||
|
|
grid-template-columns: 1fr;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</style>
|
||
|
|
|
||
|
|
<?php
|
||
|
|
$content = ob_get_clean();
|
||
|
|
require __DIR__ . '/../../../../views/site/layout.php';
|