Files
Ticketsystem/public/ticketview/ticket-admin.js
T

311 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
fetchTicket, fetchComments, fetchHistory, createComment, logTime,
formatDateTime, formatMinutes, getHistoryIcon
} from './ticket-shared.js';
document.addEventListener('DOMContentLoaded', async () => {
const ticketId = new URLSearchParams(window.location.search).get('id');
if (!ticketId) return window.location.href = '../admin.html';
const statusSelect = document.getElementById('status-select');
const priorityInput = document.getElementById('priority-text');
const categorySelect = document.getElementById('category-select');
const commentForm = document.getElementById('comment-form');
const titleText = document.getElementById('ticket-title-text');
const titleInput = document.getElementById('ticket-title-input');
const descText = document.getElementById('ticket-description-text');
const descInput = document.getElementById('ticket-description-input');
const commentAuthorInput = document.getElementById('comment-author');
const totalTimeDisplay = document.getElementById('total-time');
const timeForm = document.getElementById('time-booking-form');
let webhookConfig = null;
// Fetch Webhook Configuration
async function loadWebhookConfig() {
try {
const response = await fetch('/api/webhooks');
if (response.ok) {
webhookConfig = await response.json();
console.log('Webhook configuration loaded:', webhookConfig.configured);
} else {
console.warn('Failed to load webhook configuration');
webhookConfig = { admin: null, user: null, configured: { admin: false, user: false } };
}
} catch (error) {
console.error('Error loading webhook configuration:', error);
webhookConfig = { admin: null, user: null, configured: { admin: false, user: false } };
}
}
// Send Webhook Notification
async function sendWebhook(message) {
if (!webhookConfig?.user) {
console.log('User webhook not configured, skipping notification');
return;
}
try {
const res = await fetch(webhookConfig.user, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: message })
});
if (!res.ok) {
console.error('User webhook failed:', res.statusText);
}
} catch (err) {
console.error('Error sending user webhook:', err);
}
}
let originalStatus = '', originalPriority = '', originalTitle = '', originalDesc = '', originalCategory = '';
async function loadPage() {
loadWebhookConfig().then(() => {
loadTicketDetails();
loadComments();
loadHistory();
});
}
async function loadTicketDetails() {
const ticket = await fetchTicket(ticketId);
if (!ticket) return alert('Ticket not found');
titleText.textContent = ticket.title;
descText.textContent = ticket.description;
priorityInput.value = ticket.priority;
statusSelect.value = ticket.status;
categorySelect.value = ticket.category || '';
document.getElementById('ticket-id-val').textContent = ticket.id;
document.getElementById('ticket-requester').textContent = ticket.requester;
document.getElementById('ticket-created').textContent = formatDateTime(ticket.created_at);
document.getElementById('ticket-updated').textContent = formatDateTime(ticket.updated_at);
const totalMins = typeof ticket.total_time === 'number' ? ticket.total_time : 0;
totalTimeDisplay.textContent = formatMinutes(totalMins);
statusSelect.className = 'form-control status-' + (ticket.status || '').toLowerCase().replace(/\s+/g, '-');
originalStatus = ticket.status;
originalPriority = ticket.priority;
originalTitle = ticket.title;
originalDesc = ticket.description;
originalCategory = ticket.category || '';
}
async function updateTicket({ status, priority, title, description, category, actor = 'Admin' }) {
try {
const ticket = await fetchTicket(ticketId); // Fetch ticket to get the requester
const requester = ticket.requester || ''; // Ensure requester is included in the update request
const res = await fetch(`/api/tickets/${ticketId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ status, priority, title, description, category, actor, requester }) // Add requester
});
if (!res.ok) throw new Error('Failed to update');
alert('Ticket updated successfully');
if (status !== originalStatus) {
await sendWebhook(`Ticket #${ticketId} status changed from "${originalStatus}" to "${status}"`);
originalStatus = status;
}
if (priority !== originalPriority) {
await sendWebhook(`Ticket #${ticketId} priority changed from "${originalPriority}" to "${priority}"`);
originalPriority = priority;
}
if (title !== originalTitle) {
await sendWebhook(`Ticket #${ticketId} title changed by ${actor}`);
originalTitle = title;
}
if (description !== originalDesc) {
await sendWebhook(`Ticket #${ticketId} description changed by ${actor}`);
originalDesc = description;
}
if (category !== originalCategory) {
await sendWebhook(`Ticket #${ticketId} category changed from "${originalCategory}" to "${category}"`);
originalCategory = category;
}
titleText.textContent = title;
descText.textContent = description;
await loadPage();
} catch (err) {
console.error('Update failed:', err);
alert('Error updating ticket');
}
}
document.getElementById('update-ticket-btn').addEventListener('click', async () => {
const isEditingTitle = titleInput.style.display === 'block';
const isEditingDesc = descInput.style.display === 'block';
const newTitle = isEditingTitle ? titleInput.value.trim() : titleText.textContent;
const newDesc = isEditingDesc ? descInput.value.trim() : descText.textContent;
const newStatus = statusSelect.value;
const newPriority = priorityInput.value;
const newCategory = categorySelect.value;
const confirmed = confirm('Möchten Sie die Änderungen wirklich speichern?');
if (!confirmed) return;
await updateTicket({ status: newStatus, priority: newPriority, title: newTitle, description: newDesc, category: newCategory });
titleInput.style.display = 'none';
titleText.style.display = 'block';
descInput.style.display = 'none';
descText.style.display = 'block';
});
document.getElementById('close-ticket-btn').addEventListener('click', async () => {
const confirmed = confirm('Möchtest du das Ticket wirklich schließen?');
if (!confirmed) return;
const newStatus = 'Closed';
const newPriority = priorityInput.value;
const newTitle = titleText.textContent;
const newDesc = descText.textContent;
const newCategory = categorySelect.value;
await updateTicket({ status: newStatus, priority: newPriority, title: newTitle, description: newDesc, category: newCategory });
});
titleText.addEventListener('dblclick', () => {
titleInput.value = titleText.textContent;
titleText.style.display = 'none';
titleInput.style.display = 'block';
titleInput.focus();
});
descText.addEventListener('dblclick', () => {
descInput.value = descText.textContent;
descText.style.display = 'none';
descInput.style.display = 'block';
descInput.focus();
});
const savedAuthor = localStorage.getItem('commentAuthor');
if (savedAuthor) commentAuthorInput.value = savedAuthor;
async function loadComments() {
const comments = await fetchComments(ticketId);
const container = document.getElementById('comments-container');
container.innerHTML = comments.map(comment => {
const role = comment.role || 'user';
const isAdmin = role === 'admin';
const avatarUrl = isAdmin ? '/assets/admin.jpg' : '/assets/user.jpg';
return `
<div class="comment ${isAdmin ? 'comment-admin' : ''}">
<img class="comment-avatar" src="${avatarUrl}" alt="${isAdmin ? 'Admin' : 'User'} Avatar">
<div class="comment-content">
<div class="comment-header">
<span class="comment-author">${comment.author}</span>
${isAdmin ? '<span class="comment-role-badge">Admin</span>' : ''}
<span class="comment-date">${formatDateTime(comment.date)}</span>
</div>
<div class="comment-text">${comment.content}</div>
</div>
</div>`;
}).join('');
}
async function loadHistory() {
const history = await fetchHistory(ticketId);
const container = document.getElementById('history-container');
container.innerHTML = history.map(h => `
<div class="history-item">
<div class="history-icon"><i class="fas fa-${getHistoryIcon(h.action)}"></i></div>
<div class="history-content">
<div class="history-action">${h.action}</div>
<div class="history-details">${h.message || ''}</div>
<div class="history-meta">
<span>By ${h.author || h.actor || 'unknown'}</span>
<span>${formatDateTime(h.date || h.timestamp)}</span>
</div>
</div>
</div>`).join('');
}
commentForm.addEventListener('submit', async (e) => {
e.preventDefault();
const author = commentAuthorInput.value.trim();
if (!author) return alert('Bitte gib deinen Namen ein');
const content = document.getElementById('comment-content').value.trim();
if (!content) return alert('Comment required');
const role = 'admin';
const timestamp = new Date().toISOString();
try {
await createComment(ticketId, { author, role, content, timestamp });
await sendWebhook(`New comment added by ${author} (${role}): "${content}" on ticket #${ticketId}`);
localStorage.setItem('commentAuthor', author);
document.getElementById('comment-content').value = '';
await loadComments();
await loadHistory();
} catch (err) {
console.error('Failed to add comment:', err);
alert('Failed to add comment');
}
});
timeForm.addEventListener('submit', async (e) => {
e.preventDefault();
const minutes = parseInt(document.getElementById('time-minutes').value);
const comment = document.getElementById('time-comment').value.trim();
const actor = commentAuthorInput.value || 'Admin';
if (isNaN(minutes) || minutes <= 0) return alert('Gültige Zeit in Minuten eingeben');
try {
// Save the time log
await logTime(ticketId, { minutes, comment, author: actor });
// Also add it as a comment
const timeComment = `⏱ Zeit gebucht: ${formatMinutes(minutes)}${comment ? ` – ${comment}` : ''}`;
await createComment(ticketId, {
author: actor,
role: 'admin',
content: timeComment,
timestamp: new Date().toISOString()
});
// Webhook notification
await sendWebhook(`Time booked: ${formatMinutes(minutes)} by ${actor} on ticket #${ticketId}${comment ? ` – "${comment}"` : ''}`);
// Clear inputs and reload
document.getElementById('time-minutes').value = '';
document.getElementById('time-comment').value = '';
await loadPage();
} catch (err) {
console.error('Fehler beim Speichern der Zeit:', err);
alert('Fehler beim Speichern');
}
});
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.addEventListener('click', () => {
const tab = btn.dataset.tab;
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active'));
document.getElementById(`${tab}-tab`).classList.add('active');
});
});
await loadPage();
});