Add multi-provider event discovery and improve sync UI

This commit is contained in:
ecki
2026-04-13 18:50:09 +02:00
parent 51aab152ff
commit d77d9c0c0f
13 changed files with 548 additions and 58 deletions
+12
View File
@@ -45,6 +45,12 @@
<span class="stat-label">Letzte Aktion</span>
<strong id="sync-status">Noch kein Sync ausgefuehrt</strong>
</div>
<div class="stat-card wide">
<span class="stat-label">Quellen</span>
<div id="provider-summary" class="provider-summary">
<span class="provider-chip">Noch keine Daten</span>
</div>
</div>
</aside>
</header>
@@ -104,6 +110,12 @@
<option value="ticketed">Nur mit Ticket</option>
<option value="open">Nur offen</option>
</select>
<select id="provider-filter">
<option value="all">Alle Quellen</option>
<option value="ticketmaster">Ticketmaster</option>
<option value="bandsintown">Bandsintown</option>
<option value="eventim">Eventim</option>
</select>
</div>
</div>
+62 -3
View File
@@ -9,8 +9,10 @@ const eventsEl = document.querySelector("#events");
const notificationsEl = document.querySelector("#notifications");
const watchForm = document.querySelector("#watch-form");
const eventFilter = document.querySelector("#event-filter");
const providerFilter = document.querySelector("#provider-filter");
const syncButton = document.querySelector("#sync-button");
const toastEl = document.querySelector("#toast");
const providerSummaryEl = document.querySelector("#provider-summary");
function showToast(message) {
toastEl.textContent = message;
@@ -79,6 +81,51 @@ function renderStats() {
document.querySelector("#ticket-count").textContent = state.events.filter(
(event) => event.is_ticket_purchased
).length;
renderProviderSummary();
}
function prettifyProviderName(value) {
const names = {
ticketmaster: "Ticketmaster",
bandsintown: "Bandsintown",
eventim: "Eventim",
};
return names[value] || value;
}
function getProviderClass(value) {
const classes = {
ticketmaster: "provider-ticketmaster",
bandsintown: "provider-bandsintown",
eventim: "provider-eventim",
};
return classes[value] || "";
}
function renderProviderSummary() {
if (!state.events.length) {
providerSummaryEl.innerHTML = '<span class="provider-chip">Noch keine Daten</span>';
return;
}
const counts = state.events.reduce((accumulator, event) => {
const key = event.source || "unknown";
accumulator[key] = (accumulator[key] || 0) + 1;
return accumulator;
}, {});
providerSummaryEl.innerHTML = Object.entries(counts)
.sort((left, right) => right[1] - left[1])
.map(
([provider, count]) => `
<span class="provider-chip ${getProviderClass(provider)}">
${escapeHtml(prettifyProviderName(provider))}: ${count}
</span>
`
)
.join("");
}
function renderWatchItems() {
@@ -126,13 +173,21 @@ function getWatchNameById(id) {
function renderEvents() {
const filterValue = eventFilter.value;
const providerValue = providerFilter.value;
const filteredEvents = state.events.filter((event) => {
if (filterValue === "ticketed") {
return event.is_ticket_purchased;
if (!event.is_ticket_purchased) {
return false;
}
}
if (filterValue === "open") {
return !event.is_ticket_purchased;
if (filterValue === "open" && event.is_ticket_purchased) {
return false;
}
if (providerValue !== "all" && event.source !== providerValue) {
return false;
}
return true;
});
@@ -153,6 +208,9 @@ function renderEvents() {
<div class="pill-row">
<span class="pill">${escapeHtml(getWatchNameById(event.watch_item_id))}</span>
<span class="pill">${escapeHtml(event.city || "ohne Stadt")}</span>
<span class="pill ${getProviderClass(event.source)}">
${escapeHtml(prettifyProviderName(event.source))}
</span>
<span class="pill ${event.is_ticket_purchased ? "success" : "warning"}">
${event.is_ticket_purchased ? "Ticket markiert" : "ohne Ticket"}
</span>
@@ -291,6 +349,7 @@ syncButton.addEventListener("click", async () => {
});
eventFilter.addEventListener("change", renderEvents);
providerFilter.addEventListener("change", renderEvents);
document.addEventListener("click", async (event) => {
const button = event.target.closest("button[data-action]");
+33
View File
@@ -143,6 +143,12 @@ h1 {
line-height: 1.5;
}
.provider-summary {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.dashboard-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
@@ -324,6 +330,33 @@ button:hover,
color: var(--danger);
}
.provider-chip {
display: inline-flex;
align-items: center;
min-height: 32px;
padding: 0 12px;
border-radius: 999px;
background: rgba(46, 39, 30, 0.08);
color: var(--text);
font-family: var(--mono);
font-size: 0.78rem;
}
.provider-ticketmaster {
background: rgba(36, 94, 63, 0.12);
color: var(--success);
}
.provider-bandsintown {
background: rgba(194, 77, 44, 0.12);
color: var(--primary-dark);
}
.provider-eventim {
background: rgba(86, 60, 153, 0.14);
color: #4f3790;
}
.muted {
color: var(--muted);
}