| {% extends "shared/base.html" %} |
|
|
| {% block title %}Report an Issue - AniCove{% endblock %} |
| {% block meta_description %}Report bugs, issues, or request features on AniCove.{% endblock %} |
|
|
| {% block extra_css %} |
| <style> |
| :root { |
| --bug-bg: #0a0a0a; |
| --bug-surface: #121212; |
| --bug-border: #1e1e1e; |
| --bug-text: #e0e0e0; |
| --bug-muted: #777; |
| --bug-accent: #3b82f6; |
| --bug-accent-glow: rgba(59,130,246,0.25); |
| --bug-success: #22c55e; |
| --bug-warning: #f59e0b; |
| --bug-error: #ef4444; |
| } |
| |
| .bug-page { |
| max-width: 800px; |
| margin: 0 auto; |
| padding: 32px 20px; |
| } |
| |
| .bug-header { |
| margin-bottom: 32px; |
| } |
| |
| .bug-header-icon { |
| width: 48px; |
| height: 48px; |
| border-radius: 14px; |
| background: rgba(239, 68, 68, 0.15); |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| margin-bottom: 16px; |
| color: #ef4444; |
| } |
| |
| .bug-header h1 { |
| font-size: 1.6rem; |
| font-weight: 700; |
| margin: 0 0 6px 0; |
| color: #fff; |
| } |
| |
| .bug-header p { |
| font-size: 0.9rem; |
| color: var(--bug-muted); |
| margin: 0; |
| line-height: 1.5; |
| } |
| |
| |
| .bug-form { |
| background: var(--bug-surface); |
| border: 1px solid var(--bug-border); |
| border-radius: 12px; |
| padding: 24px; |
| margin-bottom: 28px; |
| } |
| |
| .bug-form-group { |
| margin-bottom: 18px; |
| } |
| |
| .bug-form-label { |
| display: block; |
| font-size: 0.82rem; |
| font-weight: 600; |
| color: #bbb; |
| margin-bottom: 6px; |
| } |
| |
| .bug-form-label .required { |
| color: #ef4444; |
| margin-left: 2px; |
| } |
| |
| .bug-form-input, |
| .bug-form-textarea { |
| width: 100%; |
| padding: 10px 14px; |
| background: rgba(0,0,0,0.3); |
| border: 1px solid var(--bug-border); |
| border-radius: 8px; |
| color: #fff; |
| font-size: 0.9rem; |
| transition: border-color 0.2s, box-shadow 0.2s; |
| font-family: inherit; |
| box-sizing: border-box; |
| } |
| |
| .bug-form-input:focus, |
| .bug-form-textarea:focus { |
| outline: none; |
| border-color: var(--bug-accent); |
| box-shadow: 0 0 0 3px var(--bug-accent-glow); |
| } |
| |
| .bug-form-textarea { |
| min-height: 120px; |
| resize: vertical; |
| } |
| |
| .bug-form-hint { |
| font-size: 0.75rem; |
| color: var(--bug-muted); |
| margin-top: 4px; |
| } |
| |
| |
| .bug-category-group { |
| display: flex; |
| gap: 8px; |
| flex-wrap: wrap; |
| } |
| |
| .bug-category-pill { |
| padding: 8px 18px; |
| border-radius: 999px; |
| border: 1px solid var(--bug-border); |
| background: rgba(0,0,0,0.2); |
| color: #999; |
| font-size: 0.82rem; |
| font-weight: 500; |
| cursor: pointer; |
| transition: all 0.2s; |
| font-family: inherit; |
| } |
| |
| .bug-category-pill:hover { |
| border-color: rgba(59,130,246,0.3); |
| color: #fff; |
| } |
| |
| .bug-category-pill.active { |
| border-color: var(--bug-accent); |
| background: rgba(59,130,246,0.15); |
| color: var(--bug-accent); |
| } |
| |
| .bug-category-pill[data-category="bug"].active { |
| border-color: #ef4444; |
| background: rgba(239,68,68,0.15); |
| color: #ef4444; |
| } |
| |
| .bug-category-pill[data-category="feature"].active { |
| border-color: #22c55e; |
| background: rgba(34,197,94,0.15); |
| color: #22c55e; |
| } |
| |
| .bug-category-pill[data-category="other"].active { |
| border-color: #f59e0b; |
| background: rgba(245,158,11,0.15); |
| color: #f59e0b; |
| } |
| |
| |
| .bug-submit-btn { |
| display: inline-flex; |
| align-items: center; |
| gap: 8px; |
| padding: 12px 28px; |
| background: var(--bug-accent); |
| color: #fff; |
| border: none; |
| border-radius: 8px; |
| font-size: 0.9rem; |
| font-weight: 600; |
| cursor: pointer; |
| transition: background 0.2s, transform 0.15s, box-shadow 0.2s; |
| font-family: inherit; |
| } |
| |
| .bug-submit-btn:hover { |
| background: #2563eb; |
| transform: translateY(-1px); |
| box-shadow: 0 4px 15px var(--bug-accent-glow); |
| } |
| |
| .bug-submit-btn:disabled { |
| opacity: 0.5; |
| cursor: not-allowed; |
| transform: none; |
| } |
| |
| |
| .bug-success { |
| display: none; |
| background: rgba(34,197,94,0.1); |
| border: 1px solid rgba(34,197,94,0.25); |
| border-radius: 10px; |
| padding: 16px 20px; |
| margin-bottom: 20px; |
| align-items: center; |
| gap: 12px; |
| } |
| |
| .bug-success.visible { |
| display: flex; |
| } |
| |
| .bug-success-icon { |
| color: var(--bug-success); |
| flex-shrink: 0; |
| } |
| |
| .bug-success-text { |
| color: #bbb; |
| font-size: 0.9rem; |
| line-height: 1.5; |
| } |
| |
| .bug-success-text strong { |
| color: #fff; |
| } |
| |
| |
| .bug-error { |
| display: none; |
| background: rgba(239,68,68,0.1); |
| border: 1px solid rgba(239,68,68,0.25); |
| border-radius: 8px; |
| padding: 10px 14px; |
| margin-bottom: 14px; |
| color: #fca5a5; |
| font-size: 0.85rem; |
| } |
| |
| .bug-error.visible { |
| display: block; |
| } |
| |
| |
| .bug-my-reports { |
| background: var(--bug-surface); |
| border: 1px solid var(--bug-border); |
| border-radius: 12px; |
| overflow: hidden; |
| } |
| |
| .bug-my-reports-header { |
| padding: 16px 20px; |
| border-bottom: 1px solid var(--bug-border); |
| display: flex; |
| align-items: center; |
| justify-content: space-between; |
| } |
| |
| .bug-my-reports-header h2 { |
| font-size: 1rem; |
| font-weight: 600; |
| margin: 0; |
| color: #fff; |
| } |
| |
| .bug-my-reports-count { |
| font-size: 0.8rem; |
| color: var(--bug-muted); |
| background: rgba(255,255,255,0.05); |
| padding: 2px 10px; |
| border-radius: 999px; |
| } |
| |
| .bug-report-item { |
| border-bottom: 1px solid rgba(255,255,255,0.04); |
| transition: background 0.15s; |
| } |
| |
| .bug-report-item:last-child { |
| border-bottom: none; |
| } |
| |
| .bug-report-summary { |
| padding: 16px 20px; |
| cursor: pointer; |
| display: flex; |
| align-items: flex-start; |
| gap: 10px; |
| transition: background 0.15s; |
| } |
| |
| .bug-report-summary:hover { |
| background: rgba(255,255,255,0.03); |
| } |
| |
| .bug-report-expand-icon { |
| flex-shrink: 0; |
| color: #555; |
| transition: transform 0.2s; |
| margin-top: 4px; |
| } |
| |
| .bug-report-item.expanded .bug-report-expand-icon { |
| transform: rotate(90deg); |
| } |
| |
| .bug-report-summary-content { |
| flex: 1; |
| min-width: 0; |
| } |
| |
| .bug-report-title { |
| font-size: 0.9rem; |
| font-weight: 600; |
| color: #ddd; |
| margin-bottom: 4px; |
| display: flex; |
| align-items: center; |
| gap: 8px; |
| } |
| |
| .bug-report-body-preview { |
| font-size: 0.82rem; |
| color: var(--bug-muted); |
| line-height: 1.5; |
| margin-bottom: 6px; |
| display: -webkit-box; |
| -webkit-line-clamp: 2; |
| -webkit-box-orient: vertical; |
| overflow: hidden; |
| } |
| |
| .bug-report-meta { |
| display: flex; |
| align-items: center; |
| gap: 10px; |
| flex-wrap: wrap; |
| } |
| |
| .bug-status-badge { |
| display: inline-flex; |
| align-items: center; |
| gap: 4px; |
| font-size: 0.7rem; |
| font-weight: 600; |
| padding: 2px 10px; |
| border-radius: 999px; |
| text-transform: uppercase; |
| letter-spacing: 0.04em; |
| } |
| |
| .bug-status-badge.open { |
| background: rgba(239,68,68,0.12); |
| color: #ef4444; |
| } |
| |
| .bug-status-badge.in_progress { |
| background: rgba(59,130,246,0.12); |
| color: var(--bug-accent); |
| } |
| |
| .bug-status-badge.resolved { |
| background: rgba(34,197,94,0.12); |
| color: var(--bug-success); |
| } |
| |
| .bug-status-badge.closed { |
| background: rgba(255,255,255,0.05); |
| color: var(--bug-muted); |
| } |
| |
| .bug-report-date { |
| font-size: 0.75rem; |
| color: #555; |
| } |
| |
| .bug-report-reply-count { |
| font-size: 0.75rem; |
| color: var(--bug-muted); |
| display: flex; |
| align-items: center; |
| gap: 4px; |
| } |
| |
| .bug-report-reply-count svg { |
| width: 12px; |
| height: 12px; |
| } |
| |
| |
| .bug-report-detail { |
| display: none; |
| padding: 0 20px 16px; |
| } |
| |
| .bug-report-item.expanded .bug-report-detail { |
| display: block; |
| } |
| |
| .bug-report-full-body { |
| background: rgba(0,0,0,0.2); |
| border: 1px solid rgba(255,255,255,0.04); |
| border-radius: 8px; |
| padding: 12px 14px; |
| font-size: 0.85rem; |
| color: #bbb; |
| line-height: 1.6; |
| white-space: pre-wrap; |
| margin-bottom: 12px; |
| } |
| |
| |
| .bug-thread { |
| display: flex; |
| flex-direction: column; |
| gap: 8px; |
| margin-bottom: 12px; |
| } |
| |
| .bug-thread-message { |
| display: flex; |
| gap: 10px; |
| align-items: flex-start; |
| } |
| |
| .bug-thread-message.admin { |
| flex-direction: row-reverse; |
| } |
| |
| .bug-thread-avatar { |
| width: 28px; |
| height: 28px; |
| border-radius: 50%; |
| flex-shrink: 0; |
| background: #222; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-size: 0.7rem; |
| font-weight: 700; |
| color: #fff; |
| } |
| |
| .bug-thread-avatar.admin-avatar { |
| background: var(--bug-accent); |
| } |
| |
| .bug-thread-avatar.user-avatar { |
| background: #555; |
| } |
| |
| .bug-thread-bubble { |
| max-width: 80%; |
| padding: 10px 14px; |
| border-radius: 12px; |
| font-size: 0.83rem; |
| line-height: 1.5; |
| position: relative; |
| } |
| |
| .bug-thread-message.user .bug-thread-bubble { |
| background: rgba(255,255,255,0.06); |
| border: 1px solid rgba(255,255,255,0.06); |
| color: #ddd; |
| border-bottom-left-radius: 4px; |
| } |
| |
| .bug-thread-message.admin .bug-thread-bubble { |
| background: rgba(59,130,246,0.1); |
| border: 1px solid rgba(59,130,246,0.15); |
| color: #cbd5e1; |
| border-bottom-right-radius: 4px; |
| text-align: right; |
| } |
| |
| .bug-thread-bubble .bubble-meta { |
| font-size: 0.65rem; |
| color: #555; |
| margin-top: 4px; |
| display: flex; |
| align-items: center; |
| gap: 6px; |
| } |
| |
| .bug-thread-message.admin .bubble-meta { |
| justify-content: flex-end; |
| } |
| |
| .bug-thread-badge { |
| font-size: 0.6rem; |
| font-weight: 600; |
| padding: 1px 6px; |
| border-radius: 4px; |
| text-transform: uppercase; |
| background: rgba(59,130,246,0.15); |
| color: var(--bug-accent); |
| } |
| |
| .bug-thread-badge.broadcast { |
| background: rgba(245,158,11,0.15); |
| color: #f59e0b; |
| } |
| |
| |
| .bug-thread-reply { |
| display: flex; |
| gap: 8px; |
| align-items: flex-end; |
| } |
| |
| .bug-thread-reply-input { |
| flex: 1; |
| padding: 8px 12px; |
| background: rgba(0,0,0,0.3); |
| border: 1px solid var(--bug-border); |
| border-radius: 8px; |
| color: #fff; |
| font-size: 0.83rem; |
| font-family: inherit; |
| resize: vertical; |
| min-height: 36px; |
| } |
| |
| .bug-thread-reply-input:focus { |
| outline: none; |
| border-color: var(--bug-accent); |
| } |
| |
| .bug-thread-reply-btn { |
| padding: 8px 14px; |
| background: var(--bug-accent); |
| color: #fff; |
| border: none; |
| border-radius: 8px; |
| font-size: 0.82rem; |
| font-weight: 600; |
| cursor: pointer; |
| font-family: inherit; |
| white-space: nowrap; |
| transition: background 0.2s; |
| } |
| |
| .bug-thread-reply-btn:hover { |
| background: #2563eb; |
| } |
| |
| .bug-report-empty { |
| padding: 40px 20px; |
| text-align: center; |
| color: var(--bug-muted); |
| font-size: 0.85rem; |
| } |
| |
| .bug-report-empty-icon { |
| font-size: 2rem; |
| margin-bottom: 8px; |
| opacity: 0.5; |
| } |
| |
| .bug-loading { |
| padding: 40px; |
| text-align: center; |
| color: var(--bug-muted); |
| } |
| |
| .bug-spinner { |
| display: inline-block; |
| width: 24px; |
| height: 24px; |
| border: 2px solid rgba(255,255,255,0.1); |
| border-top-color: var(--bug-accent); |
| border-radius: 50%; |
| animation: bugSpin 0.6s linear infinite; |
| } |
| |
| @keyframes bugSpin { |
| to { transform: rotate(360deg); } |
| } |
| |
| @media (max-width: 600px) { |
| .bug-page { |
| padding: 20px 14px; |
| } |
| .bug-form { |
| padding: 18px; |
| } |
| } |
| </style> |
| {% endblock %} |
|
|
| {% block content %} |
| <div class="bug-page"> |
| <div class="bug-header"> |
| <div class="bug-header-icon"> |
| <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> |
| <path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"></path> |
| <line x1="4" y1="22" x2="4" y2="15"></line> |
| </svg> |
| </div> |
| <h1>Report an Issue</h1> |
| <p>Found a bug, something not working, or have a feature idea? Let us know and we'll look into it.</p> |
| </div> |
|
|
| |
| <div class="bug-success" id="bug-success"> |
| <div class="bug-success-icon"> |
| <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> |
| <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path> |
| <polyline points="22 4 12 14.01 9 11.01"></polyline> |
| </svg> |
| </div> |
| <div class="bug-success-text"> |
| <strong>Report submitted!</strong> Thank you for your feedback. We'll review it and get back to you if needed. |
| </div> |
| </div> |
|
|
| |
| <div class="bug-error" id="bug-error"></div> |
|
|
| {% if session.get('username') %} |
| |
| <form class="bug-form" id="bug-report-form"> |
| <div class="bug-form-group"> |
| <label class="bug-form-label"> |
| Category <span class="required">*</span> |
| </label> |
| <div class="bug-category-group" id="bug-category-group"> |
| <button type="button" class="bug-category-pill active" data-category="bug" onclick="selectCategory(this)"> |
| 🐛 Bug Report |
| </button> |
| <button type="button" class="bug-category-pill" data-category="feature" onclick="selectCategory(this)"> |
| 💡 Feature Request |
| </button> |
| <button type="button" class="bug-category-pill" data-category="other" onclick="selectCategory(this)"> |
| 📝 Other |
| </button> |
| </div> |
| </div> |
|
|
| <div class="bug-form-group"> |
| <label class="bug-form-label" for="bug-title"> |
| Title <span class="required">*</span> |
| </label> |
| <input type="text" id="bug-title" class="bug-form-input" placeholder="Brief summary of the issue..." maxlength="200" required> |
| <div class="bug-form-hint">Be specific — this helps us find and fix it faster.</div> |
| </div> |
|
|
| <div class="bug-form-group"> |
| <label class="bug-form-label" for="bug-body"> |
| Description <span class="required">*</span> |
| </label> |
| <textarea id="bug-body" class="bug-form-textarea" placeholder="Describe what happened, steps to reproduce, what you expected to happen..." maxlength="5000" required></textarea> |
| <div class="bug-form-hint">Include any relevant details like error messages, browser, or device info.</div> |
| </div> |
|
|
| <div class="bug-form-group"> |
| <label class="bug-form-label" for="bug-page-url"> |
| Page URL <span style="color:#555;">(optional)</span> |
| </label> |
| <input type="text" id="bug-page-url" class="bug-form-input" placeholder="e.g. /watch/example-ep-1" maxlength="500"> |
| <div class="bug-form-hint">Which page were you on when you encountered this issue?</div> |
| </div> |
|
|
| <button type="submit" class="bug-submit-btn" id="bug-submit-btn"> |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> |
| <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path> |
| <polyline points="22 4 12 14.01 9 11.01"></polyline> |
| </svg> |
| Submit Report |
| </button> |
| </form> |
|
|
| |
| <div class="bug-my-reports" id="bug-my-reports"> |
| <div class="bug-my-reports-header"> |
| <h2>My Reports</h2> |
| <span class="bug-my-reports-count" id="bug-reports-count">0</span> |
| </div> |
| <div id="bug-reports-list"> |
| <div class="bug-loading"> |
| <div class="bug-spinner"></div> |
| </div> |
| </div> |
| </div> |
| {% else %} |
| <div class="bug-form" style="text-align:center; padding: 40px 20px;"> |
| <p style="color: var(--bug-muted); margin-bottom: 16px;">Please sign in to submit a bug report.</p> |
| <button class="bug-submit-btn" onclick="openLoginModal()"> |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| <path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/> |
| <polyline points="10 17 15 12 10 7"/> |
| <line x1="15" y1="12" x2="3" y2="12"/> |
| </svg> |
| Sign In |
| </button> |
| </div> |
| {% endif %} |
| </div> |
| {% endblock %} |
|
|
| {% block extra_js %} |
| <script> |
| function selectCategory(btn) { |
| document.querySelectorAll('.bug-category-pill').forEach(p => p.classList.remove('active')); |
| btn.classList.add('active'); |
| } |
| |
| {% if session.get('username') %} |
| |
| async function loadMyReports() { |
| const listEl = document.getElementById('bug-reports-list'); |
| try { |
| const res = await fetch('/api/bug-reports/my'); |
| const data = await res.json(); |
| if (!data.success) { |
| listEl.innerHTML = '<div class="bug-report-empty">Could not load reports.</div>'; |
| return; |
| } |
| document.getElementById('bug-reports-count').textContent = data.total; |
| |
| if (data.reports.length === 0) { |
| listEl.innerHTML = '<div class="bug-report-empty"><div class="bug-report-empty-icon">📭</div>No reports yet. Submit one above!</div>'; |
| return; |
| } |
| |
| listEl.innerHTML = data.reports.map(r => { |
| const statusLabel = r.status === 'in_progress' ? 'In Progress' : r.status.charAt(0).toUpperCase() + r.status.slice(1); |
| const date = r.created_at ? new Date(r.created_at).toLocaleDateString() : ''; |
| |
| |
| let threadHtml = ''; |
| const allMessages = []; |
| |
| (r.admin_replies || []).forEach(rep => { |
| allMessages.push({ ...rep, type: 'admin' }); |
| }); |
| |
| (r.user_replies || []).forEach(rep => { |
| allMessages.push({ ...rep, type: 'user' }); |
| }); |
| |
| allMessages.sort((a, b) => (a.created_at || '').localeCompare(b.created_at || '')); |
| |
| if (allMessages.length > 0) { |
| threadHtml = '<div class="bug-thread">' + allMessages.map(msg => { |
| const msgDate = msg.created_at ? new Date(msg.created_at).toLocaleString() : ''; |
| if (msg.type === 'admin') { |
| return ` |
| <div class="bug-thread-message admin"> |
| <div class="bug-thread-avatar admin-avatar">A</div> |
| <div class="bug-thread-bubble"> |
| <div>${msg.body}</div> |
| <div class="bubble-meta"> |
| <span>${msgDate}</span> |
| ${msg.is_broadcast ? '<span class="bug-thread-badge broadcast">Broadcast</span>' : '<span class="bug-thread-badge">Admin</span>'} |
| </div> |
| </div> |
| </div> |
| `; |
| } else { |
| return ` |
| <div class="bug-thread-message user"> |
| <div class="bug-thread-avatar user-avatar">U</div> |
| <div class="bug-thread-bubble"> |
| <div>${msg.body}</div> |
| <div class="bubble-meta"> |
| <span>${msgDate}</span> |
| </div> |
| </div> |
| </div> |
| `; |
| } |
| }).join('') + '</div>'; |
| } |
| |
| return ` |
| <div class="bug-report-item" id="report-${r._id}"> |
| <div class="bug-report-summary" onclick="toggleReport('${r._id}')"> |
| <svg class="bug-report-expand-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| <polyline points="9 18 15 12 9 6"></polyline> |
| </svg> |
| <div class="bug-report-summary-content"> |
| <div class="bug-report-title"> |
| ${r.title} |
| <span class="bug-status-badge ${r.status}">${statusLabel}</span> |
| </div> |
| <div class="bug-report-body-preview">${r.body}</div> |
| <div class="bug-report-meta"> |
| <span class="bug-report-date">${date}</span> |
| ${r.reply_count > 0 ? ` |
| <span class="bug-report-reply-count"> |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> |
| <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/> |
| </svg> |
| ${r.reply_count} ${r.reply_count === 1 ? 'reply' : 'replies'} |
| </span> |
| ` : ''} |
| </div> |
| </div> |
| </div> |
| <div class="bug-report-detail"> |
| <div class="bug-report-full-body">${r.body}</div> |
| ${threadHtml} |
| <div class="bug-thread-reply"> |
| <textarea class="bug-thread-reply-input" id="user-reply-input-${r._id}" placeholder="Reply to admin..." rows="1"></textarea> |
| <button class="bug-thread-reply-btn" onclick="sendUserReply('${r._id}')">Send</button> |
| </div> |
| </div> |
| </div> |
| `; |
| }).join(''); |
| } catch (e) { |
| listEl.innerHTML = '<div class="bug-report-empty">Failed to load reports.</div>'; |
| } |
| } |
| |
| |
| document.getElementById('bug-report-form').addEventListener('submit', async function(e) { |
| e.preventDefault(); |
| const btn = document.getElementById('bug-submit-btn'); |
| const errorEl = document.getElementById('bug-error'); |
| const successEl = document.getElementById('bug-success'); |
| |
| errorEl.classList.remove('visible'); |
| successEl.classList.remove('visible'); |
| |
| const activeCategory = document.querySelector('.bug-category-pill.active'); |
| const category = activeCategory ? activeCategory.dataset.category : 'bug'; |
| const title = document.getElementById('bug-title').value.trim(); |
| const body = document.getElementById('bug-body').value.trim(); |
| const pageUrl = document.getElementById('bug-page-url').value.trim(); |
| |
| if (!title || title.length < 3) { |
| errorEl.textContent = 'Title must be at least 3 characters.'; |
| errorEl.classList.add('visible'); |
| return; |
| } |
| if (!body || body.length < 10) { |
| errorEl.textContent = 'Description must be at least 10 characters.'; |
| errorEl.classList.add('visible'); |
| return; |
| } |
| |
| btn.disabled = true; |
| btn.textContent = 'Submitting...'; |
| |
| try { |
| const res = await fetch('/api/bug-reports', { |
| method: 'POST', |
| headers: {'Content-Type': 'application/json'}, |
| body: JSON.stringify({ title, body, category, page_url: pageUrl }), |
| }); |
| const data = await res.json(); |
| if (data.success) { |
| document.getElementById('bug-report-form').reset(); |
| |
| document.querySelectorAll('.bug-category-pill').forEach(p => p.classList.remove('active')); |
| document.querySelector('.bug-category-pill[data-category="bug"]').classList.add('active'); |
| successEl.classList.add('visible'); |
| loadMyReports(); |
| } else { |
| errorEl.textContent = data.message || 'Failed to submit report.'; |
| errorEl.classList.add('visible'); |
| } |
| } catch (e) { |
| errorEl.textContent = 'Network error. Please try again.'; |
| errorEl.classList.add('visible'); |
| } finally { |
| btn.disabled = false; |
| btn.innerHTML = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg> Submit Report`; |
| } |
| }); |
| |
| function toggleReport(id) { |
| const item = document.getElementById('report-' + id); |
| if (item) { |
| item.classList.toggle('expanded'); |
| } |
| } |
| |
| async function sendUserReply(reportId) { |
| const input = document.getElementById('user-reply-input-' + reportId); |
| const body = input.value.trim(); |
| if (!body) return; |
| |
| const btn = input.nextElementSibling; |
| btn.disabled = true; |
| btn.textContent = 'Sending...'; |
| |
| try { |
| const res = await fetch('/api/bug-reports/' + reportId + '/user-reply', { |
| method: 'POST', |
| headers: {'Content-Type': 'application/json'}, |
| body: JSON.stringify({ body }), |
| }); |
| const data = await res.json(); |
| if (data.success) { |
| input.value = ''; |
| loadMyReports(); |
| } else { |
| alert(data.message || 'Failed to send reply.'); |
| } |
| } catch (e) { |
| alert('Network error. Please try again.'); |
| } finally { |
| btn.disabled = false; |
| btn.textContent = 'Send'; |
| } |
| } |
| |
| loadMyReports(); |
| {% endif %} |
| </script> |
| {% endblock %} |
|
|