session management
Some checks failed
PHPUnit / test (push) Has been cancelled

This commit is contained in:
tom.hempel
2026-06-30 08:35:18 +02:00
parent 7fefc264f4
commit 9db886a1ae
6 changed files with 7026 additions and 5 deletions

View File

@ -4,6 +4,7 @@ import { confirmAction } from '../confirm-modal.js';
import { navigate } from '../router.js';
const REVOKE_ALL_CONFIRM = 'REVOKE ALL SESSIONS';
const DELETE_ALL_TRANSLATIONS_CONFIRM = 'DELETE ALL TRANSLATIONS';
const ACTIVITY_LABELS = {
app_sync: 'App sync',
@ -49,6 +50,23 @@ export function devPage() {
</button>
</div>
</div>
<div class="card" style="max-width:720px;margin-bottom:16px">
<h2 style="margin:0 0 8px;font-size:1.1rem">Translations</h2>
<p class="field-hint callout-danger" style="margin:0 0 12px">
<strong>Removes all non-German translations</strong> from question, answer-option, and string tables,
and deletes all languages except German (<code>de</code>). Questionnaire structure and German source
text are kept. Re-import a bundle from the Translations page to restore other languages.
</p>
<label for="deleteAllTranslationsConfirm" style="font-size:.85rem;font-weight:500;display:block;margin-bottom:6px">
Type <code>${DELETE_ALL_TRANSLATIONS_CONFIRM}</code> to confirm
</label>
<input type="text" id="deleteAllTranslationsConfirm" class="revoke-confirm-input" autocomplete="off" spellcheck="false"
placeholder="${DELETE_ALL_TRANSLATIONS_CONFIRM}">
<button type="button" class="btn btn-danger" id="deleteAllTranslationsBtn" style="margin-top:10px" disabled>
Delete all translations
</button>
<pre id="deleteAllTranslationsResult" style="margin-top:12px;font-size:.8rem;max-height:160px;overflow:auto;display:none"></pre>
</div>
<div class="card" style="max-width:720px;margin-bottom:16px">
<h2 style="margin:0 0 8px;font-size:1.1rem">Export all response data (ZIP)</h2>
<p class="field-hint" style="margin:0 0 14px">
@ -248,6 +266,7 @@ export function devPage() {
wireAdminZipExport();
wireActivityLog();
wireSecuritySettings();
wireDeleteAllTranslations();
document.getElementById('devImportBtn').addEventListener('click', async () => {
if (!parsedFixture) return;
@ -635,6 +654,54 @@ async function saveSecuritySettings(formEl, metaEl) {
}
}
function wireDeleteAllTranslations() {
const input = document.getElementById('deleteAllTranslationsConfirm');
const btn = document.getElementById('deleteAllTranslationsBtn');
const result = document.getElementById('deleteAllTranslationsResult');
if (!input || !btn) return;
input.addEventListener('input', () => {
btn.disabled = input.value.trim() !== DELETE_ALL_TRANSLATIONS_CONFIRM;
});
btn.addEventListener('click', async () => {
if (input.value.trim() !== DELETE_ALL_TRANSLATIONS_CONFIRM) return;
if (!(await confirmAction({
title: 'Delete all translations',
message: 'Remove every non-German translation and language from the database?\n\nGerman source text is kept.',
confirmLabel: 'Delete all',
variant: 'danger',
}))) {
return;
}
btn.disabled = true;
if (result) result.style.display = 'none';
try {
const data = await apiPost('translations', {
action: 'deleteAll',
confirmPhrase: DELETE_ALL_TRANSLATIONS_CONFIRM,
});
if (!data.success) throw new Error(data.error || 'Failed');
const d = data.data || {};
const summary = [
`Question translations removed: ${d.question ?? 0}`,
`Answer-option translations removed: ${d.answer_option ?? 0}`,
`String translations removed: ${d.string ?? 0}`,
`Languages removed: ${d.languages ?? 0}`,
].join('\n');
if (result) {
result.textContent = summary;
result.style.display = 'block';
}
showToast('All non-German translations deleted', 'success');
input.value = '';
} catch (err) {
showToast(err.message, 'error');
btn.disabled = input.value.trim() !== DELETE_ALL_TRANSLATIONS_CONFIRM;
}
});
}
function escAttr(v) {
return String(v ?? '')
.replace(/&/g, '&amp;')