feat(helpdesk): align module with core UI standards and fix KPI accuracy

- Add server-side ticket summary endpoint for exact KPI counts instead
  of client-side calculation from first 200 tickets
- Extract summarizeTickets() as shared static method to avoid duplication
- Replace custom .helpdesk-spinner CSS with core [aria-busy] pattern
- Migrate settings connection-test feedback to showAsyncFlash()
- Replace helpdesk-clickable-rows.js with delegated event handling
- Remove unused helpdesk-search.js (dead code, never loaded)
- Harmonize German wording: Debitoren → Kunden (8 i18n keys)
- Add 6 PHPUnit tests for loadTicketSummary()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-02 21:05:54 +02:00
parent a0d7670dd7
commit a37486c0e3
13 changed files with 275 additions and 129 deletions

View File

@@ -1,6 +1,7 @@
/**
* Helpdesk settings page — auth mode toggle and connection test.
*/
import { showAsyncFlash } from '/js/components/app-async-flash.js';
// Toggle OAuth2 vs Basic Auth field visibility
const authModeSelect = document.getElementById('auth_mode');
@@ -23,13 +24,11 @@ if (authModeSelect) {
// Connection test button
const testButton = document.getElementById('helpdesk-test-connection-button');
const testResult = document.getElementById('helpdesk-test-connection-result');
if (testButton && testResult) {
if (testButton) {
testButton.addEventListener('click', async () => {
testButton.disabled = true;
testResult.textContent = testButton.dataset.testingLabel || 'Testing...';
testResult.className = '';
testButton.setAttribute('aria-busy', 'true');
try {
const csrfMeta = document.querySelector('meta[name="csrf-token"]');
@@ -53,25 +52,19 @@ if (testButton && testResult) {
const data = await response.json();
if (data.ok) {
testResult.textContent = data.message || 'Connection successful';
testResult.style.color = 'var(--color-success, green)';
showAsyncFlash('success', data.message || 'Connection successful');
} else {
let msg = data.error || 'Connection failed';
if (data.debug?.url) {
msg += '\nURL: ' + data.debug.url;
}
if (data.debug?.http_code) {
msg += ' (HTTP ' + data.debug.http_code + ')';
}
testResult.textContent = msg;
testResult.style.color = 'var(--color-error, red)';
testResult.style.whiteSpace = 'pre-wrap';
showAsyncFlash('error', msg);
}
} catch {
testResult.textContent = 'Connection failed';
testResult.style.color = 'var(--color-error, red)';
showAsyncFlash('error', 'Connection failed');
} finally {
testButton.disabled = false;
testButton.removeAttribute('aria-busy');
}
});
}