Polish /report-bug's visual design and refine bug report semantics
Design pass on the bug report page, staying inside the site's existing design system (tokens, IBM Plex Sans, card/badge/pill components, stroke icon set) rather than introducing a new one: - A slim top bar (brand mark + back-to-home pill button + language switcher) replaces the bare floating heading, so the page reads as part of the product instead of an orphaned form. - The "write in English" notice moves inside the form card, right above the field it applies to, and switches from the amber "needs attention" tone to an accent-tinted info tone, so it doesn't visually collide with the bug-status badges' own use of amber for "not read yet". - "Your reports" is promoted to a proper labeled section with a cleaner row layout (truncated description with a title tooltip, compact date). - A character counter on the description field. - The back-to-home control is now a bordered pill with an arrow icon instead of a bare text link with a hardcoded "←", which also meant dropping that hardcoded arrow from all 7 translations. Also, two content refinements based on feedback: - Max description length dropped from 5000 to 2000 characters, enforced on both the textarea and the API's Pydantic validator. - The "read" status is relabeled from a passive "read"/"letta" to an active "acknowledged"/"presa in carico" (and each other language's own equivalent helpdesk term) — it communicates a team is on it, not just that someone glanced at it. Only the label changed; the underlying "read" status value in the API/DB is untouched. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+61
-31
@@ -4,31 +4,53 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title data-i18n="bugReport.pageTitle">Report a bug</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/logo.svg">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-shell">
|
||||
<div class="lang-bar">
|
||||
<select id="lang-switcher" class="lang-switcher" onchange="setLanguage(this.value)" aria-label="Language">
|
||||
<option value="en">English</option>
|
||||
<option value="it">Italiano</option>
|
||||
<option value="es">Español</option>
|
||||
<option value="fr">Français</option>
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="ru">Русский</option>
|
||||
<option value="zh">中文</option>
|
||||
</select>
|
||||
<div class="app-shell app-shell-bugreport">
|
||||
|
||||
<div class="bugreport-topbar">
|
||||
<a class="brand" href="/">
|
||||
<img class="brand-mark" src="/logo.svg" alt="">
|
||||
PLM Lottery
|
||||
</a>
|
||||
<div class="bugreport-topbar-right">
|
||||
<a class="back-home-btn" href="/">
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
|
||||
<span data-i18n="bugReport.backLink">Back to home</span>
|
||||
</a>
|
||||
<select id="lang-switcher" class="lang-switcher" onchange="setLanguage(this.value)" aria-label="Language">
|
||||
<option value="en">English</option>
|
||||
<option value="it">Italiano</option>
|
||||
<option value="es">Español</option>
|
||||
<option value="fr">Français</option>
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="ru">Русский</option>
|
||||
<option value="zh">中文</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 data-i18n="bugReport.heading">Report a bug</h1>
|
||||
<p data-i18n="bugReport.intro">Found a problem? Describe it below — your report goes straight to the admin panel.</p>
|
||||
<p class="english-notice" data-i18n="bugReport.englishNotice">
|
||||
Please write your bug report in English, regardless of the language you're browsing in — this helps us handle it faster.
|
||||
</p>
|
||||
<div class="bugreport-hero">
|
||||
<div class="bugreport-hero-icon">
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 8v5"/><path d="M12 16h.01"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h1 data-i18n="bugReport.heading">Report a bug</h1>
|
||||
<p data-i18n="bugReport.intro">Found a problem? Describe it below — your report goes straight to the admin panel.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" id="report-form">
|
||||
<div class="field-note" id="english-notice">
|
||||
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>
|
||||
<span data-i18n="bugReport.englishNotice">Please write your bug report in English, regardless of the language you're browsing in — this helps us handle it faster.</span>
|
||||
</div>
|
||||
|
||||
<label for="bug-description" data-i18n="bugReport.descriptionLabel">What happened?</label>
|
||||
<textarea id="bug-description" rows="6" maxlength="5000" data-i18n-placeholder="bugReport.descriptionPlaceholder"></textarea>
|
||||
<textarea id="bug-description" rows="6" maxlength="2000" data-i18n-placeholder="bugReport.descriptionPlaceholder" oninput="updateCharCount()"></textarea>
|
||||
<div class="char-count" id="char-count">0 / 2000</div>
|
||||
|
||||
<label for="bug-contact" data-i18n="bugReport.contactLabel">Contact (optional)</label>
|
||||
<input id="bug-contact" type="text" maxlength="256" data-i18n-placeholder="bugReport.contactPlaceholder">
|
||||
@@ -36,13 +58,14 @@
|
||||
<button onclick="submitBugReport()" id="bug-submit-btn" data-i18n="bugReport.submitBtn">Send report</button>
|
||||
</div>
|
||||
|
||||
<div class="card hidden" id="my-reports-card">
|
||||
<h2 data-i18n="bugReport.myReportsTitle">Your reports</h2>
|
||||
<p class="hint" data-i18n="bugReport.myReportsHint">Only reports sent from this account, with the status set by the admin team.</p>
|
||||
<div id="my-reports-list"></div>
|
||||
<div class="hidden" id="my-reports-section">
|
||||
<p class="section-label" data-i18n="bugReport.myReportsTitle">Your reports</p>
|
||||
<div class="card">
|
||||
<p class="hint" data-i18n="bugReport.myReportsHint">Only reports sent from this account, with the status set by the admin team.</p>
|
||||
<div id="my-reports-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p><a class="link" href="/" data-i18n="bugReport.backLink">← Back to home</a></p>
|
||||
</div>
|
||||
|
||||
<div id="toast-container" aria-live="polite"></div>
|
||||
@@ -62,6 +85,11 @@
|
||||
return String(s).replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
||||
}
|
||||
|
||||
function updateCharCount() {
|
||||
const field = document.getElementById('bug-description');
|
||||
document.getElementById('char-count').textContent = field.value.length + ' / ' + field.maxLength;
|
||||
}
|
||||
|
||||
async function submitBugReport() {
|
||||
const btn = document.getElementById('bug-submit-btn');
|
||||
const description = document.getElementById('bug-description').value.trim();
|
||||
@@ -88,6 +116,7 @@
|
||||
if (!res.ok) throw new Error(data.detail?.message || data.detail || res.statusText);
|
||||
document.getElementById('bug-description').value = '';
|
||||
document.getElementById('bug-contact').value = '';
|
||||
updateCharCount();
|
||||
toast(t('bugReport.successToast'), 'success');
|
||||
loadMyBugReports();
|
||||
} catch (e) {
|
||||
@@ -101,29 +130,29 @@
|
||||
|
||||
async function loadMyBugReports() {
|
||||
const token = localStorage.getItem('plm_token');
|
||||
const card = document.getElementById('my-reports-card');
|
||||
const section = document.getElementById('my-reports-section');
|
||||
if (!token) {
|
||||
card.classList.add('hidden');
|
||||
section.classList.add('hidden');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await fetch('/bug-reports/mine', { headers: { Authorization: 'Bearer ' + token } });
|
||||
if (!res.ok) {
|
||||
card.classList.add('hidden');
|
||||
section.classList.add('hidden');
|
||||
return;
|
||||
}
|
||||
const reports = await res.json();
|
||||
card.classList.remove('hidden');
|
||||
section.classList.remove('hidden');
|
||||
const list = document.getElementById('my-reports-list');
|
||||
list.innerHTML = reports.map((r) => `
|
||||
<div class="my-report-row">
|
||||
<div class="report-row">
|
||||
<span class="badge bug-status-${escapeHtml(r.status)}">${escapeHtml(t('bugReport.status' + r.status.charAt(0).toUpperCase() + r.status.slice(1)))}</span>
|
||||
<span class="my-report-desc">${escapeHtml(r.description)}</span>
|
||||
<span class="my-report-date">${new Date(r.created_at).toLocaleString(currentDateLocale())}</span>
|
||||
<span class="report-row-desc" title="${escapeHtml(r.description)}">${escapeHtml(r.description)}</span>
|
||||
<span class="report-row-date">${new Date(r.created_at).toLocaleDateString(currentDateLocale(), { day: 'numeric', month: 'short', year: 'numeric' })}</span>
|
||||
</div>
|
||||
`).join('') || `<p class="hint">${escapeHtml(t('bugReport.myReportsEmpty'))}</p>`;
|
||||
`).join('') || `<p class="hint report-empty">${escapeHtml(t('bugReport.myReportsEmpty'))}</p>`;
|
||||
} catch (e) {
|
||||
card.classList.add('hidden');
|
||||
section.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +162,7 @@
|
||||
loadMyBugReports();
|
||||
}
|
||||
|
||||
updateCharCount();
|
||||
loadMyBugReports();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user