ordered questionnaire list with conditions * GET ?id= -> single questionnaire in app JSON format * GET ?translations=1 -> global app UI strings only; **no auth** (login screen). * * POST (coach interview upload) is handled by api/index.php -> handlers/app_questionnaires.php */ require_once __DIR__ . '/../common.php'; require_once __DIR__ . '/../db_init.php'; require_once __DIR__ . '/../lib/response.php'; header('Content-Type: application/json; charset=UTF-8'); $method = $_SERVER['REQUEST_METHOD']; if ($method === 'GET' && !empty($_GET['translations'])) { [$pdo, $tmpDb, $lockFp] = qdb_open(false); json_success(['translations' => qdb_build_app_translations_map($pdo)]); qdb_discard($tmpDb, $lockFp); exit; } $tokenRec = require_valid_token(); if ($method !== 'GET') { json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); } [$pdo, $tmpDb, $lockFp] = qdb_open(false); $qnID = $_GET['id'] ?? ''; if ($qnID) { // Single questionnaire in app JSON format $qn = $pdo->prepare("SELECT * FROM questionnaire WHERE questionnaireID = :id"); $qn->execute([':id' => $qnID]); $qnRow = $qn->fetch(PDO::FETCH_ASSOC); if (!$qnRow) { qdb_discard($tmpDb, $lockFp); http_response_code(404); echo json_encode(["error" => "Questionnaire not found"]); exit; } $stmt = $pdo->prepare(" SELECT questionID, defaultText, type, orderIndex, configJson FROM question WHERE questionnaireID = :id ORDER BY orderIndex "); $stmt->execute([':id' => $qnID]); $dbQuestions = $stmt->fetchAll(PDO::FETCH_ASSOC); $questions = []; foreach ($dbQuestions as $dbQ) { $localId = $dbQ['questionID']; $parts = explode('__', $localId); $shortId = end($parts); $layout = $dbQ['type']; $config = qdb_parse_config_json($dbQ['configJson']); $qKey = qdb_question_key($config, $dbQ['defaultText']); $q = [ 'id' => $shortId, 'layout' => $layout, 'question' => $qKey !== '' ? $qKey : $dbQ['defaultText'], ]; if ($qKey !== '' && !empty($config['noteBefore'])) { $q['noteBeforeKey'] = qdb_note_before_key($qKey); } if ($qKey !== '' && !empty($config['noteAfter'])) { $q['noteAfterKey'] = qdb_note_after_key($qKey); } if (isset($config['textKey'])) $q['textKey'] = $config['textKey']; if (isset($config['textKey1'])) $q['textKey1'] = $config['textKey1']; if (isset($config['textKey2'])) $q['textKey2'] = $config['textKey2']; if (isset($config['hint'])) $q['hint'] = $config['hint']; if (isset($config['hint1'])) $q['hint1'] = $config['hint1']; if (isset($config['hint2'])) $q['hint2'] = $config['hint2']; if (isset($config['symptoms'])) $q['symptoms'] = $config['symptoms']; if (isset($config['scaleType'])) $q['scaleType'] = $config['scaleType']; if (isset($config['range'])) $q['range'] = $config['range']; if (isset($config['step'])) $q['step'] = (int)$config['step']; if (isset($config['unitLabel'])) $q['unitLabel'] = $config['unitLabel']; if (isset($config['constraints'])) $q['constraints'] = $config['constraints']; if (isset($config['precision'])) $q['precision'] = $config['precision']; if (isset($config['minSelection'])) $q['minSelection'] = $config['minSelection']; if (isset($config['maxLength'])) $q['maxLength'] = (int)$config['maxLength']; if (isset($config['nextQuestionId'])) $q['nextQuestionId'] = $config['nextQuestionId']; if (isset($config['otherNextQuestionId'])) $q['otherNextQuestionId'] = $config['otherNextQuestionId']; if (isset($config['otherOptionKey'])) $q['otherOptionKey'] = $config['otherOptionKey']; if ($layout === 'string_spinner' && isset($config['options'])) { $q['options'] = $config['options']; } if (($layout === 'value_spinner' || $layout === 'slider_question') && isset($config['valueOptions'])) { $q['options'] = $config['valueOptions']; } // Answer options (for radio_question, multi_check_box_question, etc.) $ao = $pdo->prepare(" SELECT defaultText, points, nextQuestionId FROM answer_option WHERE questionID = :qid ORDER BY orderIndex "); $ao->execute([':qid' => $dbQ['questionID']]); $opts = $ao->fetchAll(PDO::FETCH_ASSOC); if ($opts) { $optionsArr = []; $pointsMap = []; foreach ($opts as $opt) { $o = ['key' => $opt['defaultText']]; if ($opt['nextQuestionId'] !== '') { $o['nextQuestionId'] = $opt['nextQuestionId']; } $optionsArr[] = $o; $pointsMap[$opt['defaultText']] = (int)$opt['points']; } $q['options'] = $optionsArr; $hasNonZero = false; foreach ($pointsMap as $v) { if ($v !== 0) { $hasNonZero = true; break; } } $q['pointsMap'] = $pointsMap; } $questions[] = $q; } qdb_discard($tmpDb, $lockFp); echo json_encode([ 'meta' => ['id' => $qnID], 'questions' => $questions, 'translations' => qdb_build_questionnaire_translations_map($pdo, $qnID), ]); exit; } // Default: ordered questionnaire list $rows = $pdo->query(" SELECT questionnaireID, name, showPoints, conditionJson FROM questionnaire WHERE state = 'active' ORDER BY orderIndex ")->fetchAll(PDO::FETCH_ASSOC); $list = []; foreach ($rows as $r) { $list[] = [ 'id' => $r['questionnaireID'], 'name' => $r['name'], 'showPoints' => (bool)(int)$r['showPoints'], 'condition' => json_decode($r['conditionJson'], true) ?: new stdClass(), ]; } qdb_discard($tmpDb, $lockFp); echo json_encode($list);