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 `
${isAdmin ? 'Admin' : 'User'} Avatar
${comment.author} ${isAdmin ? 'Admin' : ''} ${formatDateTime(comment.date)}
${comment.content}
`; }).join(''); } async function loadHistory() { const history = await fetchHistory(ticketId); const container = document.getElementById('history-container'); container.innerHTML = history.map(h => `
${h.action}
${h.message || ''}
By ${h.author || h.actor || 'unknown'} ${formatDateTime(h.date || h.timestamp)}
`).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(); });