improved condition json to have UI integration
This commit is contained in:
@ -12,6 +12,10 @@ import {
|
||||
renderGroupedTranslationsHTML,
|
||||
escHtml,
|
||||
} from '../translations-helpers.js';
|
||||
import {
|
||||
mountConditionEditor,
|
||||
parseConditionForm,
|
||||
} from '../condition-editor.js';
|
||||
|
||||
const LAYOUT_TYPES = [
|
||||
{ value: 'radio_question', label: 'Radio (Single Choice)' },
|
||||
@ -33,6 +37,9 @@ let questionnaire = null;
|
||||
let questions = [];
|
||||
let isNew = false;
|
||||
let activeTab = 'questions';
|
||||
let allQuestionnaires = [];
|
||||
let questionsByQuestionnaire = {};
|
||||
let conditionEditorApi = null;
|
||||
|
||||
// ── Entry point ──────────────────────────────────────────────────────────
|
||||
|
||||
@ -52,6 +59,13 @@ export async function editorPage(params) {
|
||||
<div id="editorContent"><div class="spinner"></div></div>
|
||||
`;
|
||||
|
||||
try {
|
||||
const qnList = await apiGet('questionnaires.php');
|
||||
allQuestionnaires = qnList.questionnaires || [];
|
||||
} catch {
|
||||
allQuestionnaires = [];
|
||||
}
|
||||
|
||||
if (isNew) {
|
||||
questionnaire = { questionnaireID: null, name: '', version: '1.0', state: 'draft',
|
||||
orderIndex: 0, showPoints: 0, conditionJson: '{}' };
|
||||
@ -59,11 +73,8 @@ export async function editorPage(params) {
|
||||
renderEditor();
|
||||
} else {
|
||||
try {
|
||||
const [qnData, quData] = await Promise.all([
|
||||
apiGet('questionnaires.php'),
|
||||
apiGet(`questions.php?questionnaireID=${encodeURIComponent(params.id)}`)
|
||||
]);
|
||||
questionnaire = (qnData.questionnaires || []).find(q => q.questionnaireID === params.id);
|
||||
const quData = await apiGet(`questions.php?questionnaireID=${encodeURIComponent(params.id)}`);
|
||||
questionnaire = allQuestionnaires.find(q => q.questionnaireID === params.id);
|
||||
if (!questionnaire) {
|
||||
showToast('Questionnaire not found', 'error');
|
||||
navigate('#/');
|
||||
@ -547,16 +558,7 @@ function renderEditor() {
|
||||
<input type="text" id="metaCategoryKey" value="${esc(questionnaire.categoryKey || '')}" placeholder="e.g. health_interview" ${editable ? '' : 'disabled'}>
|
||||
</div>
|
||||
</div>
|
||||
${editable ? `
|
||||
<details class="condition-details" style="margin-bottom:16px">
|
||||
<summary style="cursor:pointer;font-size:.85rem;font-weight:600;color:var(--text-secondary)">
|
||||
Availability Condition (JSON)
|
||||
</summary>
|
||||
<textarea id="metaCondition" rows="6"
|
||||
style="width:100%;margin-top:8px;font-family:monospace;font-size:.85rem;padding:8px 12px;border:1px solid var(--border);border-radius:6px"
|
||||
>${formatJson(condJson)}</textarea>
|
||||
</details>
|
||||
` : ''}
|
||||
<div id="conditionEditorMount" style="margin:16px 0"></div>
|
||||
${editable ? `<button class="btn btn-primary" id="saveMetaBtn">${isNew ? 'Create Questionnaire' : 'Save Changes'}</button>` : ''}
|
||||
</div>
|
||||
|
||||
@ -586,6 +588,7 @@ function renderEditor() {
|
||||
if (editable) {
|
||||
document.getElementById('saveMetaBtn').addEventListener('click', saveMeta);
|
||||
}
|
||||
initConditionEditor(condJson);
|
||||
|
||||
if (!isNew) {
|
||||
initTabs();
|
||||
@ -613,14 +616,60 @@ function initTabs() {
|
||||
});
|
||||
}
|
||||
|
||||
function formatJson(str) {
|
||||
async function fetchAppStringGerman(messageKey) {
|
||||
if (!messageKey) return '';
|
||||
try {
|
||||
return JSON.stringify(JSON.parse(str), null, 2);
|
||||
const data = await apiGet(
|
||||
`translations.php?type=app_string&id=${encodeURIComponent(messageKey)}`
|
||||
);
|
||||
const row = (data.translations || []).find(
|
||||
tr => tr.languageCode === SOURCE_LANG
|
||||
);
|
||||
return row?.text?.trim() || '';
|
||||
} catch {
|
||||
return str;
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
async function loadQuestionsByQuestionnaire() {
|
||||
const ids = allQuestionnaires.map(q => q.questionnaireID).filter(Boolean);
|
||||
if (!ids.length) {
|
||||
questionsByQuestionnaire = {};
|
||||
return;
|
||||
}
|
||||
const pairs = await Promise.all(ids.map(async id => {
|
||||
try {
|
||||
const data = await apiGet(
|
||||
`questions.php?questionnaireID=${encodeURIComponent(id)}`
|
||||
);
|
||||
return [id, data.questions || []];
|
||||
} catch {
|
||||
return [id, []];
|
||||
}
|
||||
}));
|
||||
questionsByQuestionnaire = Object.fromEntries(pairs);
|
||||
}
|
||||
|
||||
async function initConditionEditor(condJson) {
|
||||
const mount = document.getElementById('conditionEditorMount');
|
||||
if (!mount) return;
|
||||
mount.innerHTML = '<div class="spinner"></div>';
|
||||
await loadQuestionsByQuestionnaire();
|
||||
const qid = questionnaire?.questionnaireID || 'new';
|
||||
const parsed = parseConditionForm(condJson, qid);
|
||||
const germanLabel = parsed.messageKey
|
||||
? await fetchAppStringGerman(parsed.messageKey)
|
||||
: '';
|
||||
conditionEditorApi = mountConditionEditor(mount, {
|
||||
questionnaireId: qid,
|
||||
allQuestionnaires,
|
||||
questionsByQuestionnaire,
|
||||
conditionJson: condJson,
|
||||
germanLabel,
|
||||
editable: canEdit(),
|
||||
});
|
||||
}
|
||||
|
||||
// ── Save questionnaire meta ──────────────────────────────────────────────
|
||||
|
||||
async function saveMeta() {
|
||||
@ -631,13 +680,13 @@ async function saveMeta() {
|
||||
const showPoints = document.getElementById('metaShowPoints').checked ? 1 : 0;
|
||||
const categoryKey = (document.getElementById('metaCategoryKey')?.value || '').trim();
|
||||
let conditionJson = '{}';
|
||||
const condEl = document.getElementById('metaCondition');
|
||||
if (condEl) {
|
||||
let conditionForm = null;
|
||||
if (conditionEditorApi) {
|
||||
try {
|
||||
JSON.parse(condEl.value);
|
||||
conditionJson = condEl.value.trim();
|
||||
} catch {
|
||||
showToast('Condition JSON is invalid', 'error');
|
||||
conditionJson = conditionEditorApi.buildJson();
|
||||
conditionForm = conditionEditorApi.readForm();
|
||||
} catch (e) {
|
||||
showToast(e.message || 'Invalid availability settings', 'error');
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -649,6 +698,7 @@ async function saveMeta() {
|
||||
if (data.success) {
|
||||
questionnaire = data.questionnaire;
|
||||
isNew = false;
|
||||
await saveConditionMessageLabel(conditionForm);
|
||||
showToast('Questionnaire created', 'success');
|
||||
navigate(`#/questionnaire/${questionnaire.questionnaireID}`);
|
||||
}
|
||||
@ -660,6 +710,7 @@ async function saveMeta() {
|
||||
if (data.success) {
|
||||
questionnaire = { ...questionnaire, name, version, state, orderIndex, showPoints, conditionJson, categoryKey };
|
||||
document.getElementById('editorTitle').textContent = name;
|
||||
await saveConditionMessageLabel(conditionForm);
|
||||
showToast('Saved', 'success');
|
||||
}
|
||||
}
|
||||
@ -668,6 +719,22 @@ async function saveMeta() {
|
||||
}
|
||||
}
|
||||
|
||||
async function saveConditionMessageLabel(form) {
|
||||
if (!form || form.mode === 'always') return;
|
||||
const messageKey = (form.messageKey || '').trim();
|
||||
const germanLabel = (form.germanLabel || '').trim();
|
||||
if (!messageKey || !germanLabel) return;
|
||||
try {
|
||||
await apiPost('questionnaires.php', {
|
||||
action: 'updateConditionMessageLabel',
|
||||
messageKey,
|
||||
germanLabel,
|
||||
});
|
||||
} catch (e) {
|
||||
showToast(`Saved questionnaire; message label failed: ${e.message}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// ── Add question form ────────────────────────────────────────────────────
|
||||
|
||||
function showAddQuestionForm() {
|
||||
|
||||
Reference in New Issue
Block a user