210 lines
6.1 KiB
JavaScript
210 lines
6.1 KiB
JavaScript
// Copyright © 2026 Olaf Kolkman
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
(() => {
|
|
const labelAuthNotice = document.querySelector('#label-auth-notice');
|
|
const labelControls = document.querySelector('#label-controls');
|
|
const labelForm = document.querySelector('#label-form');
|
|
const labelNameInput = document.querySelector('#label-name');
|
|
const labelStatus = document.querySelector('#label-status');
|
|
const labelList = document.querySelector('#label-list');
|
|
const accessToken = localStorage.getItem('linklogAccessToken');
|
|
let currentUserId = null;
|
|
|
|
function authHeaders(includeJson = false) {
|
|
return {
|
|
...(includeJson ? {'Content-Type': 'application/json'} : {}),
|
|
...(accessToken ? {Authorization: `Bearer ${accessToken}`} : {}),
|
|
};
|
|
}
|
|
|
|
function setStatus(message, isError = false) {
|
|
if (!labelStatus) return;
|
|
labelStatus.textContent = message;
|
|
labelStatus.style.color = isError ? '#b91c1c' : '#166534';
|
|
}
|
|
|
|
async function responseError(response, fallback) {
|
|
try {
|
|
const result = await response.json();
|
|
return result.detail || result.message || fallback;
|
|
} catch {
|
|
return fallback;
|
|
}
|
|
}
|
|
|
|
function renderLabels(labels) {
|
|
if (!labelList) return;
|
|
if (!labels || labels.length === 0) {
|
|
labelList.innerHTML = '<p>No labels found.</p>';
|
|
return;
|
|
}
|
|
|
|
labelList.replaceChildren(...labels.map((label) => {
|
|
const row = document.createElement('div');
|
|
row.className = 'plugin-row';
|
|
|
|
const isOwned = label.created_by === currentUserId;
|
|
|
|
const contentContainer = document.createElement('div');
|
|
contentContainer.style.display = 'flex';
|
|
contentContainer.style.alignItems = 'center';
|
|
contentContainer.style.justifySpaceBetween = 'space-between';
|
|
contentContainer.style.width = '100%';
|
|
|
|
const text = document.createElement('span');
|
|
text.textContent = `${label.name}${isOwned ? '' : (label.creator ? ` (${label.creator})` : ' (default)')}`;
|
|
|
|
contentContainer.appendChild(text);
|
|
|
|
if (isOwned) {
|
|
const actions = document.createElement('div');
|
|
actions.style.display = 'flex';
|
|
actions.style.gap = '8px';
|
|
|
|
const editButton = document.createElement('button');
|
|
editButton.type = 'button';
|
|
editButton.textContent = 'Edit';
|
|
editButton.addEventListener('click', () => {
|
|
showInlineEdit(row, label);
|
|
});
|
|
|
|
const deleteButton = document.createElement('button');
|
|
deleteButton.type = 'button';
|
|
deleteButton.className = 'danger-button';
|
|
deleteButton.textContent = 'Delete';
|
|
deleteButton.addEventListener('click', async () => {
|
|
if (!confirm(`Are you sure you want to delete "${label.name}"?`)) return;
|
|
setStatus('');
|
|
const response = await fetch(`/api/user/labels/${label.id}`, {
|
|
method: 'DELETE',
|
|
headers: authHeaders(),
|
|
});
|
|
if (response.ok) {
|
|
setStatus(`Deleted label ${label.name}`);
|
|
loadLabels();
|
|
} else {
|
|
setStatus(await responseError(response, 'Could not delete label'), true);
|
|
}
|
|
});
|
|
|
|
actions.append(editButton, deleteButton);
|
|
contentContainer.appendChild(actions);
|
|
}
|
|
|
|
row.appendChild(contentContainer);
|
|
return row;
|
|
}));
|
|
}
|
|
|
|
function showInlineEdit(rowContainer, label) {
|
|
rowContainer.replaceChildren();
|
|
|
|
const editForm = document.createElement('form');
|
|
editForm.style.display = 'flex';
|
|
editForm.style.gap = '8px';
|
|
editForm.style.width = '100%';
|
|
editForm.style.alignItems = 'center';
|
|
|
|
const input = document.createElement('input');
|
|
input.type = 'text';
|
|
input.value = label.name;
|
|
input.required = true;
|
|
input.style.flex = '1';
|
|
|
|
const saveButton = document.createElement('button');
|
|
saveButton.type = 'submit';
|
|
saveButton.textContent = 'Save';
|
|
|
|
const cancelButton = document.createElement('button');
|
|
cancelButton.type = 'button';
|
|
cancelButton.textContent = 'Cancel';
|
|
cancelButton.addEventListener('click', () => {
|
|
loadLabels();
|
|
});
|
|
|
|
editForm.append(input, saveButton, cancelButton);
|
|
|
|
editForm.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
const newName = input.value.trim();
|
|
if (!newName) return;
|
|
setStatus('');
|
|
const response = await fetch(`/api/user/labels/${label.id}`, {
|
|
method: 'PUT',
|
|
headers: authHeaders(true),
|
|
body: JSON.stringify({ name: newName }),
|
|
});
|
|
if (response.ok) {
|
|
setStatus(`Updated label to ${newName}`);
|
|
loadLabels();
|
|
} else {
|
|
setStatus(await responseError(response, 'Could not update label'), true);
|
|
}
|
|
});
|
|
|
|
rowContainer.appendChild(editForm);
|
|
input.focus();
|
|
}
|
|
|
|
async function loadLabels() {
|
|
try {
|
|
const response = await fetch('/api/user/labels', { headers: authHeaders() });
|
|
if (!response.ok) throw new Error('Could not load labels');
|
|
const labels = await response.json();
|
|
renderLabels(labels);
|
|
} catch (error) {
|
|
setStatus('Error loading labels', true);
|
|
}
|
|
}
|
|
|
|
async function init() {
|
|
if (!accessToken) {
|
|
if (labelAuthNotice) labelAuthNotice.classList.remove('hidden');
|
|
if (labelControls) labelControls.classList.add('hidden');
|
|
return;
|
|
}
|
|
|
|
if (labelAuthNotice) labelAuthNotice.classList.add('hidden');
|
|
if (labelControls) labelControls.classList.remove('hidden');
|
|
|
|
try {
|
|
const meResponse = await fetch('/api/auth/me', { headers: authHeaders() });
|
|
if (meResponse.ok) {
|
|
const me = await meResponse.json();
|
|
currentUserId = me.id;
|
|
}
|
|
} catch {
|
|
// Proceed if me fails
|
|
}
|
|
|
|
await loadLabels();
|
|
|
|
if (labelForm) {
|
|
labelForm.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
const name = labelNameInput.value.trim();
|
|
if (!name) return;
|
|
setStatus('');
|
|
const response = await fetch('/api/user/labels', {
|
|
method: 'POST',
|
|
headers: authHeaders(true),
|
|
body: JSON.stringify({ name }),
|
|
});
|
|
if (response.ok) {
|
|
labelNameInput.value = '';
|
|
setStatus(`Added label ${name}`);
|
|
loadLabels();
|
|
} else {
|
|
setStatus(await responseError(response, 'Could not add label'), true);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
if (document.readyState !== 'loading') {
|
|
init();
|
|
}
|
|
})();
|