Files
pingu-concerts/app/templates/new_concert.html
T
2026-08-25 04:08:13 +00:00

816 lines
15 KiB
HTML

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
>
<title>Konzert hinzufügen · Pingu Concerts</title>
<link
rel="stylesheet"
href="/static/css/style.css"
>
<style>
.flyer-upload {
margin-top: 25px;
}
.flyer-upload label {
display: block;
margin-bottom: 8px;
}
.flyer-help {
color: #8b949e;
font-size: 0.9rem;
margin-top: 6px;
}
.flyer-preview-container {
margin-top: 15px;
display: none;
}
.flyer-preview {
max-width: 300px;
max-height: 400px;
display: block;
border-radius: 10px;
border: 1px solid #30363d;
}
.flyer-preview-name {
margin-top: 8px;
color: #8b949e;
font-size: 0.9rem;
}
.selected-venue {
margin-top: 10px;
padding: 12px;
background: #0d1117;
border: 1px solid #30363d;
border-radius: 8px;
color: #b8c1cc;
display: none;
}
</style>
</head>
<body>
<header>
<div class="header-inner">
<a href="/" class="logo">
🎸 Pingu <span>Concerts</span>
</a>
</div>
</header>
<main>
<div class="page-title">
<h1>🎤 Konzert hinzufügen</h1>
<p>
Trag ein, worauf wir uns freuen.
</p>
</div>
<section class="empty">
<form
method="post"
action="/concerts"
enctype="multipart/form-data"
>
<!-- ================================================= -->
<!-- Künstler -->
<!-- ================================================= -->
<p>
<label>
Künstler / Band<br>
<input
type="text"
name="artist"
required
placeholder="z. B. Iron Maiden"
>
</label>
</p>
<!-- ================================================= -->
<!-- Veranstaltungsort -->
<!-- ================================================= -->
<div class="venue-search">
<label for="venue-search">
Veranstaltungsort
</label>
<input
type="text"
id="venue-search"
autocomplete="off"
placeholder="z. B. Turbinenhalle"
>
<div
id="venue-results"
class="venue-results"
></div>
<div
id="selected-venue"
class="selected-venue"
></div>
<input
type="hidden"
name="venue_id"
id="venue-id"
>
<input
type="hidden"
name="venue_name"
id="venue-name"
>
<input
type="hidden"
name="city"
id="venue-city"
>
<input
type="hidden"
name="street"
id="venue-street"
>
<input
type="hidden"
name="postal_code"
id="venue-postal-code"
>
<input
type="hidden"
name="country"
id="venue-country"
>
<input
type="hidden"
name="latitude"
id="venue-latitude"
>
<input
type="hidden"
name="longitude"
id="venue-longitude"
>
</div>
<!-- ================================================= -->
<!-- Datum -->
<!-- ================================================= -->
<p>
<label>
Datum und Uhrzeit<br>
<input
type="datetime-local"
name="start_datetime"
required
>
</label>
</p>
<!-- ================================================= -->
<!-- Beschreibung -->
<!-- ================================================= -->
<p>
<label>
Beschreibung<br>
<textarea
name="description"
rows="5"
placeholder="Optional: Infos zum Konzert, Tour, Supportbands usw."
></textarea>
</label>
</p>
<!-- ================================================= -->
<!-- Ticket-Link -->
<!-- ================================================= -->
<p>
<label>
Ticket-Link<br>
<input
type="url"
name="ticket_url"
placeholder="https://..."
>
</label>
</p>
<!-- ================================================= -->
<!-- Ticketpreis -->
<!-- ================================================= -->
<p>
<label>
Ticketpreis<br>
<input
type="number"
name="ticket_price"
step="0.01"
min="0"
placeholder="z. B. 59.90"
>
</label>
</p>
<!-- ================================================= -->
<!-- Flyer -->
<!-- ================================================= -->
<div class="flyer-upload">
<label for="flyer">
🎨 Konzert-Flyer
</label>
<input
type="file"
id="flyer"
name="flyer"
accept="image/jpeg,image/png,image/webp"
>
<div class="flyer-help">
JPG, PNG oder WEBP · maximal 10 MB
</div>
<div
id="flyer-preview-container"
class="flyer-preview-container"
>
<img
id="flyer-preview"
class="flyer-preview"
alt="Flyer Vorschau"
>
<div
id="flyer-preview-name"
class="flyer-preview-name"
></div>
</div>
</div>
<!-- ================================================= -->
<!-- Buttons -->
<!-- ================================================= -->
<div style="margin-top: 30px;">
<button
type="submit"
class="button"
>
💾 Konzert speichern
</button>
<a
href="/"
class="button button-secondary"
>
Abbrechen
</a>
</div>
</form>
</section>
</main>
<script>
/* ============================================================
VENUE SEARCH
============================================================ */
const venueSearch =
document.getElementById(
"venue-search"
);
const venueResults =
document.getElementById(
"venue-results"
);
const selectedVenue =
document.getElementById(
"selected-venue"
);
let searchTimeout = null;
venueSearch.addEventListener(
"input",
() => {
clearTimeout(
searchTimeout
);
const query =
venueSearch.value.trim();
venueResults.innerHTML = "";
selectedVenue.style.display =
"none";
if (query.length < 2) {
return;
}
searchTimeout = setTimeout(
() => {
searchVenues(
query
);
},
300
);
}
);
/* ============================================================
VENUE SEARCH REQUEST
============================================================ */
async function searchVenues(
query
) {
try {
const response =
await fetch(
`/api/venues/search?q=${encodeURIComponent(query)}`
);
if (!response.ok) {
return;
}
const venues =
await response.json();
venueResults.innerHTML =
"";
if (!venues.length) {
const empty =
document.createElement(
"div"
);
empty.className =
"venue-result";
empty.textContent =
"Keine Veranstaltungsorte gefunden";
venueResults.appendChild(
empty
);
return;
}
venues.forEach(
venue => {
const result =
document.createElement(
"button"
);
result.type =
"button";
result.className =
"venue-result";
const address = [
venue.street,
venue.postal_code,
venue.city
]
.filter(Boolean)
.join(" ");
result.innerHTML = `
<strong>
📍 ${escapeHtml(
venue.name
)}
</strong>
<span>
${escapeHtml(
address
)}
</span>
`;
result.addEventListener(
"click",
() => {
selectVenue(
venue
);
}
);
venueResults.appendChild(
result
);
}
);
} catch (error) {
console.error(
"Venue search failed:",
error
);
}
}
/* ============================================================
VENUE SELECT
============================================================ */
function selectVenue(
venue
) {
document.getElementById(
"venue-id"
).value =
venue.id ?? "";
document.getElementById(
"venue-name"
).value =
venue.name ?? "";
document.getElementById(
"venue-city"
).value =
venue.city ?? "";
document.getElementById(
"venue-street"
).value =
venue.street ?? "";
document.getElementById(
"venue-postal-code"
).value =
venue.postal_code ?? "";
document.getElementById(
"venue-country"
).value =
venue.country ?? "";
document.getElementById(
"venue-latitude"
).value =
venue.latitude ?? "";
document.getElementById(
"venue-longitude"
).value =
venue.longitude ?? "";
venueSearch.value =
venue.name;
venueResults.innerHTML =
"";
const address = [
venue.street,
venue.postal_code,
venue.city
]
.filter(Boolean)
.join(" ");
selectedVenue.innerHTML = `
<strong>
📍 ${escapeHtml(
venue.name
)}
</strong>
${
address
? `<br>${escapeHtml(address)}`
: ""
}
`;
selectedVenue.style.display =
"block";
}
/* ============================================================
FLYER PREVIEW
============================================================ */
const flyerInput =
document.getElementById(
"flyer"
);
const flyerPreviewContainer =
document.getElementById(
"flyer-preview-container"
);
const flyerPreview =
document.getElementById(
"flyer-preview"
);
const flyerPreviewName =
document.getElementById(
"flyer-preview-name"
);
flyerInput.addEventListener(
"change",
() => {
const file =
flyerInput.files[0];
if (!file) {
flyerPreviewContainer.style.display =
"none";
flyerPreview.src =
"";
flyerPreviewName.textContent =
"";
return;
}
if (
![
"image/jpeg",
"image/png",
"image/webp"
].includes(
file.type
)
) {
alert(
"Bitte JPG, PNG oder WEBP auswählen."
);
flyerInput.value =
"";
return;
}
if (
file.size >
10 * 1024 * 1024
) {
alert(
"Der Flyer darf maximal 10 MB groß sein."
);
flyerInput.value =
"";
return;
}
const reader =
new FileReader();
reader.onload =
event => {
flyerPreview.src =
event.target.result;
flyerPreviewName.textContent =
file.name;
flyerPreviewContainer.style.display =
"block";
};
reader.readAsDataURL(
file
);
}
);
/* ============================================================
HTML ESCAPING
============================================================ */
function escapeHtml(
value
) {
const div =
document.createElement(
"div"
);
div.textContent =
value ?? "";
return div.innerHTML;
}
</script>
</body>
</html>