Files
urlshortener/web/assets/app.js
2026-05-19 06:31:43 +02:00

219 lines
6.0 KiB
JavaScript

const loginLink = document.querySelector("#login-link");
const logoutButton = document.querySelector("#logout-button");
const userChip = document.querySelector("#user-chip");
const loggedOutPanel = document.querySelector("#logged-out-panel");
const appPanel = document.querySelector("#app-panel");
const linksSection = document.querySelector("#links-section");
const form = document.querySelector("#shorten-form");
const input = document.querySelector("#url");
const button = document.querySelector("#submit-button");
const alertBox = document.querySelector("#alert");
const result = document.querySelector("#result");
const shortUrl = document.querySelector("#short-url");
const longUrl = document.querySelector("#long-url");
const copyButton = document.querySelector("#copy-button");
const refreshButton = document.querySelector("#refresh-button");
const linksList = document.querySelector("#links-list");
const emptyState = document.querySelector("#empty-state");
const template = document.querySelector("#link-template");
let currentUser = null;
async function api(path, options = {}) {
const response = await fetch(path, {
...options,
headers: {
"Content-Type": "application/json",
...(options.headers || {}),
},
});
if (response.status === 204) {
return null;
}
const payload = await response.json().catch(() => ({}));
if (!response.ok) {
throw new Error(payload.error || "Die Anfrage ist fehlgeschlagen.");
}
return payload;
}
function showError(message) {
alertBox.textContent = message;
alertBox.hidden = false;
}
function clearError() {
alertBox.textContent = "";
alertBox.hidden = true;
}
function setLoading(isLoading) {
button.disabled = isLoading;
button.textContent = isLoading ? "Kürze..." : "Kürzen";
}
function setAuthenticated(user) {
currentUser = user.authenticated ? user : null;
if (currentUser) {
userChip.textContent = currentUser.email
? `${currentUser.username} · ${currentUser.email}`
: currentUser.username;
userChip.hidden = false;
logoutButton.hidden = false;
loginLink.hidden = true;
loggedOutPanel.hidden = true;
appPanel.hidden = false;
linksSection.hidden = false;
loadLinks();
} else {
userChip.hidden = true;
logoutButton.hidden = true;
loginLink.hidden = false;
loggedOutPanel.hidden = false;
appPanel.hidden = true;
linksSection.hidden = true;
}
}
async function loadMe() {
const user = await api("/api/me");
setAuthenticated(user);
}
async function loadLinks() {
if (!currentUser) return;
linksList.innerHTML = "";
emptyState.hidden = true;
try {
const links = await api("/api/links");
emptyState.hidden = links.length !== 0;
links.forEach(renderLink);
} catch (error) {
showError(error.message);
}
}
function renderLink(link) {
const node = template.content.cloneNode(true);
const card = node.querySelector(".link-card");
const short = node.querySelector(".link-short");
const long = node.querySelector(".link-long");
const form = node.querySelector(".edit-form");
const editInput = node.querySelector(".edit-input");
const saveButton = node.querySelector(".save-button");
const copyButton = node.querySelector(".copy-link-button");
const deleteButton = node.querySelector(".delete-button");
short.href = link.short_url;
short.textContent = link.short_url;
long.textContent = link.long_url;
editInput.value = link.long_url;
form.addEventListener("submit", async (event) => {
event.preventDefault();
clearError();
saveButton.disabled = true;
saveButton.textContent = "Speichere...";
try {
const updated = await api(`/api/links/${encodeURIComponent(link.code)}`, {
method: "PUT",
body: JSON.stringify({ url: editInput.value.trim() }),
});
long.textContent = updated.long_url;
editInput.value = updated.long_url;
saveButton.textContent = "Gespeichert";
setTimeout(() => {
saveButton.textContent = "Speichern";
}, 1200);
} catch (error) {
showError(error.message);
saveButton.textContent = "Speichern";
} finally {
saveButton.disabled = false;
}
});
copyButton.addEventListener("click", async () => {
await copyToClipboard(link.short_url, copyButton);
});
deleteButton.addEventListener("click", async () => {
const ok = confirm("Diesen Kurzlink wirklich löschen?");
if (!ok) return;
clearError();
try {
await api(`/api/links/${encodeURIComponent(link.code)}`, { method: "DELETE" });
card.remove();
if (!linksList.children.length) emptyState.hidden = false;
} catch (error) {
showError(error.message);
}
});
linksList.appendChild(node);
}
async function copyToClipboard(value, targetButton) {
try {
await navigator.clipboard.writeText(value);
const old = targetButton.textContent;
targetButton.textContent = "Kopiert!";
setTimeout(() => {
targetButton.textContent = old;
}, 1500);
} catch {
showError("Kopieren ist fehlgeschlagen. Bitte kopiere den Link manuell.");
}
}
form.addEventListener("submit", async (event) => {
event.preventDefault();
clearError();
result.hidden = true;
setLoading(true);
try {
const payload = await api("/api/shorten", {
method: "POST",
body: JSON.stringify({ url: input.value.trim() }),
});
shortUrl.textContent = payload.short_url;
shortUrl.href = payload.short_url;
longUrl.textContent = payload.long_url;
result.hidden = false;
copyButton.textContent = "Kopieren";
input.value = "";
await loadLinks();
} catch (error) {
showError(error.message);
} finally {
setLoading(false);
}
});
copyButton.addEventListener("click", async () => {
await copyToClipboard(shortUrl.href, copyButton);
});
refreshButton.addEventListener("click", loadLinks);
logoutButton.addEventListener("click", async () => {
await fetch("/auth/logout", { method: "POST" });
window.location.href = "/";
});
loadMe().catch((error) => {
console.error(error);
});