From f1caa9e681e39902f792d42f8257c7e561ae5ae0 Mon Sep 17 00:00:00 2001 From: "tom.hempel" Date: Mon, 29 Jun 2026 12:39:55 +0200 Subject: [PATCH] initial prototype based on nat-as-server --- .github/workflows/phpunit.yml | 27 + .gitignore | 64 + api/.htaccess | 6 + api/index.php | 91 + bw-schuetzt-schema.drawio.png | Bin 0 -> 334025 bytes cli/cleanup_sessions.php | 21 + cli/diagnose_db.php | 156 + cli/diagnose_login.php | 77 + common.php | 2398 ++++++++++ composer.json | 25 + composer.phar | Bin 0 -> 3561371 bytes data/app_ui_strings.json | 298 ++ data/countries_de_sorted.txt | 190 + db_init.php | 568 +++ handlers/activity_log.php | 21 + handlers/analytics.php | 51 + handlers/answer_options.php | 217 + handlers/app_questionnaires.php | 626 +++ handlers/assignments.php | 99 + handlers/auth.php | 286 ++ handlers/backup.php | 33 + handlers/clients.php | 239 + handlers/coaches.php | 29 + handlers/dev.php | 53 + handlers/export.php | 111 + handlers/logout.php | 18 + handlers/questionnaires.php | 244 + handlers/questions.php | 357 ++ handlers/results.php | 124 + handlers/scoring_profiles.php | 203 + handlers/session.php | 44 + handlers/settings.php | 70 + handlers/translations.php | 302 ++ handlers/users.php | 427 ++ lib/analytics.php | 401 ++ lib/api_log.php | 998 +++++ lib/app_answers.php | 306 ++ lib/app_submit_validate.php | 221 + lib/dev_fixture.php | 911 ++++ lib/encrypted_payload.php | 60 + lib/errors.php | 89 + lib/keycloak_auth.php | 221 + lib/login_rate_limit.php | 176 + lib/questionnaire_structure.php | 414 ++ lib/read_db_cache.php | 299 ++ lib/response.php | 122 + lib/scoring.php | 784 ++++ lib/settings.php | 114 + lib/submissions.php | 999 +++++ lib/text_sanitize.php | 61 + lib/validate.php | 71 + logs/api/.gitkeep | 0 phpunit.xml | 30 + schema.sql | 259 ++ scripts/generate_dev_fixture.py | 237 + seed_admin.php | 66 + tests/Integration/ApiRoutingTest.php | 22 + .../Integration/AppQuestionnairesBulkTest.php | 51 + tests/Integration/AppQuestionnairesTest.php | 61 + tests/Integration/AppSubmitExtendedTest.php | 271 ++ tests/Integration/AppSubmitLegacyTest.php | 78 + .../AssignmentsActivityLogTest.php | 38 + tests/Integration/AuthTest.php | 68 + tests/Integration/BackupDevOpsTest.php | 80 + tests/Integration/CatalogApiTest.php | 94 + tests/Integration/ChangePasswordTest.php | 39 + tests/Integration/ClientsLifecycleTest.php | 114 + .../Integration/ClientsQuestionnairesTest.php | 60 + tests/Integration/CoachesActivityTest.php | 27 + tests/Integration/DbInitTest.php | 52 + tests/Integration/DownloadsTest.php | 50 + tests/Integration/EditorCrudTest.php | 119 + tests/Integration/ExportExtendedTest.php | 116 + tests/Integration/ExportTest.php | 50 + .../Integration/QuestionnaireEditingTest.php | 57 + .../QuestionnaireImportExportTest.php | 76 + tests/Integration/RbacIntegrationTest.php | 85 + tests/Integration/ResultsAnalyticsTest.php | 86 + tests/Integration/SecurityPathsTest.php | 90 + tests/Integration/SessionLogoutTest.php | 40 + tests/Integration/SettingsApiTest.php | 66 + tests/Integration/TokenTest.php | 65 + .../TranslationsImportExportTest.php | 83 + tests/Integration/TranslationsWriteTest.php | 104 + tests/Integration/UsersAdminTest.php | 179 + tests/Integration/UsersTest.php | 55 + tests/Support/ApiClient.php | 181 + tests/Support/DatabaseSeeder.php | 233 + tests/Support/JsonErrorResponse.php | 20 + tests/Support/JsonSuccessResponse.php | 18 + tests/Support/QdbTestCase.php | 172 + tests/Support/RawHttpResponse.php | 22 + tests/Support/TestFilesystem.php | 48 + tests/Unit/ApiLogTest.php | 109 + tests/Unit/AppAnswersTest.php | 74 + tests/Unit/AppSubmitValidateTest.php | 131 + tests/Unit/CommonBundleTest.php | 62 + tests/Unit/CommonHelpersTest.php | 100 + tests/Unit/CryptoTest.php | 53 + tests/Unit/EncryptedPayloadTest.php | 51 + tests/Unit/ErrorsTest.php | 56 + tests/Unit/LoginRateLimitTest.php | 58 + tests/Unit/QuestionnaireStructureTest.php | 56 + tests/Unit/RbacTest.php | 48 + tests/Unit/ScoringTest.php | 211 + tests/Unit/SettingsTest.php | 54 + tests/Unit/TextSanitizeTest.php | 31 + tests/Unit/ValidateTest.php | 55 + tests/bootstrap.php | 29 + tests/check-coverage.php | 60 + uploads/logs/.gitkeep | 0 uploads/logs/api/.gitkeep | 0 website/css/style.css | 3975 +++++++++++++++++ website/index.html | 99 + website/js/api.js | 214 + website/js/app.js | 192 + website/js/client-archive.js | 48 + website/js/condition-editor.js | 662 +++ website/js/confirm-modal.js | 148 + website/js/pages/assignments.js | 461 ++ website/js/pages/clients.js | 938 ++++ website/js/pages/coaches.js | 423 ++ website/js/pages/dev.js | 643 +++ website/js/pages/editor.js | 2107 +++++++++ website/js/pages/export.js | 233 + website/js/pages/home.js | 108 + website/js/pages/insights.js | 376 ++ website/js/pages/login.js | 268 ++ website/js/pages/questionnaires.js | 832 ++++ website/js/pages/results.js | 468 ++ website/js/pages/translations.js | 707 +++ website/js/pages/users.js | 562 +++ website/js/password-modal.js | 208 + website/js/questionnaire-preview.js | 555 +++ website/js/router.js | 56 + website/js/table-filter.js | 52 + website/js/test-data.js | 125 + website/js/translations-helpers.js | 285 ++ .../media/UniKonstanz_Logo_Optimum_RGB.eps | Bin 0 -> 1607062 bytes website/media/uni-logo.png | Bin 0 -> 43991 bytes .../temp-assets/questionnaire_0_readme.json | 158 + ...estionnaire_1_demographic_information.json | 306 ++ website/temp-assets/questionnaire_2_rhs.json | 137 + .../questionnaire_3_integration_index.json | 255 ++ .../questionnaire_4_consultation_results.json | 182 + .../questionnaire_5_final_interview.json | 84 + .../questionnaire_6_follow_up_survey.json | 349 ++ website/temp-assets/questionnaire_order.json | 67 + 148 files changed, 34905 insertions(+) create mode 100644 .github/workflows/phpunit.yml create mode 100644 .gitignore create mode 100644 api/.htaccess create mode 100644 api/index.php create mode 100644 bw-schuetzt-schema.drawio.png create mode 100644 cli/cleanup_sessions.php create mode 100644 cli/diagnose_db.php create mode 100644 cli/diagnose_login.php create mode 100644 common.php create mode 100644 composer.json create mode 100644 composer.phar create mode 100644 data/app_ui_strings.json create mode 100644 data/countries_de_sorted.txt create mode 100644 db_init.php create mode 100644 handlers/activity_log.php create mode 100644 handlers/analytics.php create mode 100644 handlers/answer_options.php create mode 100644 handlers/app_questionnaires.php create mode 100644 handlers/assignments.php create mode 100644 handlers/auth.php create mode 100644 handlers/backup.php create mode 100644 handlers/clients.php create mode 100644 handlers/coaches.php create mode 100644 handlers/dev.php create mode 100644 handlers/export.php create mode 100644 handlers/logout.php create mode 100644 handlers/questionnaires.php create mode 100644 handlers/questions.php create mode 100644 handlers/results.php create mode 100644 handlers/scoring_profiles.php create mode 100644 handlers/session.php create mode 100644 handlers/settings.php create mode 100644 handlers/translations.php create mode 100644 handlers/users.php create mode 100644 lib/analytics.php create mode 100644 lib/api_log.php create mode 100644 lib/app_answers.php create mode 100644 lib/app_submit_validate.php create mode 100644 lib/dev_fixture.php create mode 100644 lib/encrypted_payload.php create mode 100644 lib/errors.php create mode 100644 lib/keycloak_auth.php create mode 100644 lib/login_rate_limit.php create mode 100644 lib/questionnaire_structure.php create mode 100644 lib/read_db_cache.php create mode 100644 lib/response.php create mode 100644 lib/scoring.php create mode 100644 lib/settings.php create mode 100644 lib/submissions.php create mode 100644 lib/text_sanitize.php create mode 100644 lib/validate.php create mode 100644 logs/api/.gitkeep create mode 100644 phpunit.xml create mode 100644 schema.sql create mode 100644 scripts/generate_dev_fixture.py create mode 100644 seed_admin.php create mode 100644 tests/Integration/ApiRoutingTest.php create mode 100644 tests/Integration/AppQuestionnairesBulkTest.php create mode 100644 tests/Integration/AppQuestionnairesTest.php create mode 100644 tests/Integration/AppSubmitExtendedTest.php create mode 100644 tests/Integration/AppSubmitLegacyTest.php create mode 100644 tests/Integration/AssignmentsActivityLogTest.php create mode 100644 tests/Integration/AuthTest.php create mode 100644 tests/Integration/BackupDevOpsTest.php create mode 100644 tests/Integration/CatalogApiTest.php create mode 100644 tests/Integration/ChangePasswordTest.php create mode 100644 tests/Integration/ClientsLifecycleTest.php create mode 100644 tests/Integration/ClientsQuestionnairesTest.php create mode 100644 tests/Integration/CoachesActivityTest.php create mode 100644 tests/Integration/DbInitTest.php create mode 100644 tests/Integration/DownloadsTest.php create mode 100644 tests/Integration/EditorCrudTest.php create mode 100644 tests/Integration/ExportExtendedTest.php create mode 100644 tests/Integration/ExportTest.php create mode 100644 tests/Integration/QuestionnaireEditingTest.php create mode 100644 tests/Integration/QuestionnaireImportExportTest.php create mode 100644 tests/Integration/RbacIntegrationTest.php create mode 100644 tests/Integration/ResultsAnalyticsTest.php create mode 100644 tests/Integration/SecurityPathsTest.php create mode 100644 tests/Integration/SessionLogoutTest.php create mode 100644 tests/Integration/SettingsApiTest.php create mode 100644 tests/Integration/TokenTest.php create mode 100644 tests/Integration/TranslationsImportExportTest.php create mode 100644 tests/Integration/TranslationsWriteTest.php create mode 100644 tests/Integration/UsersAdminTest.php create mode 100644 tests/Integration/UsersTest.php create mode 100644 tests/Support/ApiClient.php create mode 100644 tests/Support/DatabaseSeeder.php create mode 100644 tests/Support/JsonErrorResponse.php create mode 100644 tests/Support/JsonSuccessResponse.php create mode 100644 tests/Support/QdbTestCase.php create mode 100644 tests/Support/RawHttpResponse.php create mode 100644 tests/Support/TestFilesystem.php create mode 100644 tests/Unit/ApiLogTest.php create mode 100644 tests/Unit/AppAnswersTest.php create mode 100644 tests/Unit/AppSubmitValidateTest.php create mode 100644 tests/Unit/CommonBundleTest.php create mode 100644 tests/Unit/CommonHelpersTest.php create mode 100644 tests/Unit/CryptoTest.php create mode 100644 tests/Unit/EncryptedPayloadTest.php create mode 100644 tests/Unit/ErrorsTest.php create mode 100644 tests/Unit/LoginRateLimitTest.php create mode 100644 tests/Unit/QuestionnaireStructureTest.php create mode 100644 tests/Unit/RbacTest.php create mode 100644 tests/Unit/ScoringTest.php create mode 100644 tests/Unit/SettingsTest.php create mode 100644 tests/Unit/TextSanitizeTest.php create mode 100644 tests/Unit/ValidateTest.php create mode 100644 tests/bootstrap.php create mode 100644 tests/check-coverage.php create mode 100644 uploads/logs/.gitkeep create mode 100644 uploads/logs/api/.gitkeep create mode 100644 website/css/style.css create mode 100644 website/index.html create mode 100644 website/js/api.js create mode 100644 website/js/app.js create mode 100644 website/js/client-archive.js create mode 100644 website/js/condition-editor.js create mode 100644 website/js/confirm-modal.js create mode 100644 website/js/pages/assignments.js create mode 100644 website/js/pages/clients.js create mode 100644 website/js/pages/coaches.js create mode 100644 website/js/pages/dev.js create mode 100644 website/js/pages/editor.js create mode 100644 website/js/pages/export.js create mode 100644 website/js/pages/home.js create mode 100644 website/js/pages/insights.js create mode 100644 website/js/pages/login.js create mode 100644 website/js/pages/questionnaires.js create mode 100644 website/js/pages/results.js create mode 100644 website/js/pages/translations.js create mode 100644 website/js/pages/users.js create mode 100644 website/js/password-modal.js create mode 100644 website/js/questionnaire-preview.js create mode 100644 website/js/router.js create mode 100644 website/js/table-filter.js create mode 100644 website/js/test-data.js create mode 100644 website/js/translations-helpers.js create mode 100644 website/media/UniKonstanz_Logo_Optimum_RGB.eps create mode 100644 website/media/uni-logo.png create mode 100644 website/temp-assets/questionnaire_0_readme.json create mode 100644 website/temp-assets/questionnaire_1_demographic_information.json create mode 100644 website/temp-assets/questionnaire_2_rhs.json create mode 100644 website/temp-assets/questionnaire_3_integration_index.json create mode 100644 website/temp-assets/questionnaire_4_consultation_results.json create mode 100644 website/temp-assets/questionnaire_5_final_interview.json create mode 100644 website/temp-assets/questionnaire_6_follow_up_survey.json create mode 100644 website/temp-assets/questionnaire_order.json diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml new file mode 100644 index 0000000..bf175d4 --- /dev/null +++ b/.github/workflows/phpunit.yml @@ -0,0 +1,27 @@ +name: PHPUnit + +on: + push: + branches: [main, master] + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + extensions: json, openssl, pdo, pdo_sqlite, zip + coverage: pcov + tools: composer:v2 + + - name: Install dependencies + run: composer install --no-interaction --prefer-dist + + - name: Run tests with coverage floor + run: php tests/check-coverage.php 40 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..746aa56 --- /dev/null +++ b/.gitignore @@ -0,0 +1,64 @@ +# Secrets and session data +tokens.jsonl +valid_tokens.txt +.env +*.sh + +# Encrypted database, temp files, backups +uploads/* +!uploads/.gitkeep +!uploads/logs/ +!uploads/logs/** + +# Database lock files (just in case) +uploads/.qdb_lock +uploads/*.lock + +# Logs / dumps / exports +*.log +logs/api/* +!logs/api/.gitkeep +*.sqlite +*.db +*.dump +*.bak +*.backup +*.tmp +*.temp + +# Node/npm artifacts (if website/js uses npm) +node_modules/ +npm-debug.log +yarn.lock +package-lock.json + +# PHP composer artifacts (if using composer in future) +tests/runtime/ +.phpunit.cache/ +vendor/ +composer.lock + +# IDE / editor +.DS_Store +Thumbs.db +*.swp +*.swo +*~ +.vscode/ +.idea/ +*.code-workspace + +# Misc +*.orig +*.rej +*.md + +# Ignore browser build artifacts (if any) +website/js/dist/ +website/js/build/ + +# Ignore backups at project root +*.tar +*.tar.gz +*.zip +*.7z \ No newline at end of file diff --git a/api/.htaccess b/api/.htaccess new file mode 100644 index 0000000..1d6713f --- /dev/null +++ b/api/.htaccess @@ -0,0 +1,6 @@ +RewriteEngine On + +# Let the front controller handle everything except itself +RewriteCond %{REQUEST_FILENAME} !-d +RewriteCond %{REQUEST_URI} !=/api/index.php +RewriteRule ^(.*)$ index.php [QSA,L] diff --git a/api/index.php b/api/index.php new file mode 100644 index 0000000..9ebee27 --- /dev/null +++ b/api/index.php @@ -0,0 +1,91 @@ + handler file +$routes = [ + 'questionnaires' => __DIR__ . '/../handlers/questionnaires.php', + 'questions' => __DIR__ . '/../handlers/questions.php', + 'answer_options' => __DIR__ . '/../handlers/answer_options.php', + 'translations' => __DIR__ . '/../handlers/translations.php', + 'users' => __DIR__ . '/../handlers/users.php', + 'assignments' => __DIR__ . '/../handlers/assignments.php', + 'clients' => __DIR__ . '/../handlers/clients.php', + 'results' => __DIR__ . '/../handlers/results.php', + 'export' => __DIR__ . '/../handlers/export.php', + 'analytics' => __DIR__ . '/../handlers/analytics.php', + 'scoring_profiles' => __DIR__ . '/../handlers/scoring_profiles.php', + 'coaches' => __DIR__ . '/../handlers/coaches.php', + 'app_questionnaires' => __DIR__ . '/../handlers/app_questionnaires.php', + 'logout' => __DIR__ . '/../handlers/logout.php', + 'session' => __DIR__ . '/../handlers/session.php', + 'auth/login' => __DIR__ . '/../handlers/auth.php', + 'auth/change-password' => __DIR__ . '/../handlers/auth.php', + 'auth/keycloak-config' => __DIR__ . '/../handlers/auth.php', + 'auth/keycloak-login' => __DIR__ . '/../handlers/auth.php', + 'auth/keycloak-callback' => __DIR__ . '/../handlers/auth.php', + 'backup' => __DIR__ . '/../handlers/backup.php', + 'dev' => __DIR__ . '/../handlers/dev.php', + 'dev/import' => __DIR__ . '/../handlers/dev.php', + 'activity-log' => __DIR__ . '/../handlers/activity_log.php', + 'settings' => __DIR__ . '/../handlers/settings.php', +]; + +try { + if (!isset($routes[$route])) { + json_error('NOT_FOUND', "Unknown endpoint: $route", 404); + } + + $handlerFile = $routes[$route]; + if (!file_exists($handlerFile)) { + json_error('NOT_FOUND', "Handler not found for: $route", 404); + } + + require $handlerFile; + +} catch (Throwable $e) { + if (defined('QDB_TESTING') && ( + $e instanceof \Tests\Support\JsonSuccessResponse + || $e instanceof \Tests\Support\JsonErrorResponse + || $e instanceof \Tests\Support\RawHttpResponse + )) { + throw $e; + } + require_once __DIR__ . '/../lib/errors.php'; + qdb_emit_api_error($e, "API $method $route"); +} diff --git a/bw-schuetzt-schema.drawio.png b/bw-schuetzt-schema.drawio.png new file mode 100644 index 0000000000000000000000000000000000000000..09edc0914c57e1fb894cf6939b3dd9775c89d889 GIT binary patch literal 334025 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

ZXNl4Xh-{)bL_;JtD7n!UL#1PEpDRHaN;EQ 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

d+Si}*u*_Zc^l;_`FCIPz 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"DELETE FROM session WHERE expiresAt < :now"); +$stmt->execute([':now' => time()]); +$removed = $stmt->rowCount(); +qdb_save($tmpDb, $lockFp); + +echo "Removed $removed expired session(s).\n"; diff --git a/cli/diagnose_db.php b/cli/diagnose_db.php new file mode 100644 index 0000000..347d08a --- /dev/null +++ b/cli/diagnose_db.php @@ -0,0 +1,156 @@ + false, 'error' => 'file missing']; + } + $storedEnc = @file_get_contents($dbPath); + if ($storedEnc === false || strlen($storedEnc) < 17) { + return ['ok' => false, 'error' => 'unreadable or too short']; + } + try { + aes256_cbc_decrypt_bytes($storedEnc, $keyBytes); + return ['ok' => true]; + } catch (Throwable $e) { + return ['ok' => false, 'error' => $e->getMessage()]; + } +} + +$envPath = __DIR__ . '/../.env'; +echo "Project: " . realpath(__DIR__ . '/..') . "\n"; +echo ".env: " . ($envPath && is_readable($envPath) ? "readable" : "MISSING or not readable") . "\n"; + +$keyEnv = getenv('QDB_MASTER_KEY'); +if ($keyEnv === false || $keyEnv === '') { + echo "QDB_MASTER_KEY: NOT SET (set in .env or Apache/PHP-FPM env)\n"; + exit(1); +} +echo "QDB_MASTER_KEY: set (" . strlen($keyEnv) . " chars)\n"; +$b64 = base64_decode($keyEnv, true); +echo " parses as base64: " . ($b64 !== false && strlen($b64) > 0 ? 'yes (' . strlen($b64) . ' bytes)' : 'no (uses first 32 ASCII chars)') . "\n"; + +$dbPath = $argv[1] ?? QDB_PATH; +if ($dbPath !== QDB_PATH && !is_file($dbPath)) { + echo "File not found: $dbPath\n"; + exit(1); +} +if ($dbPath !== QDB_PATH) { + echo "Note: probing alternate file (app still uses " . QDB_PATH . " at runtime)\n"; +} +$uploads = dirname($dbPath); +echo "uploads dir: $uploads\n"; +echo " exists: " . (is_dir($uploads) ? 'yes' : 'no') . "\n"; +echo " writable: " . (is_writable($uploads) ? 'yes' : 'NO — migrations cannot save') . "\n"; +echo "DB file: $dbPath\n"; +if (is_file($dbPath)) { + $mtime = filemtime($dbPath); + echo " exists: yes (" . filesize($dbPath) . " bytes)\n"; + echo " modified: " . ($mtime ? date('Y-m-d H:i:s T', $mtime) : '?') . "\n"; +} else { + echo " exists: no (will create on first write)\n"; +} + +$backupDir = $uploads . '/backups'; +if (is_dir($backupDir)) { + $backups = glob($backupDir . '/questionnaire_database.*') ?: []; + rsort($backups); + echo "Backups in uploads/backups: " . count($backups) . "\n"; + foreach (array_slice($backups, 0, 5) as $bp) { + $mt = filemtime($bp); + echo " - " . basename($bp) . ' (' . filesize($bp) . " bytes, " . ($mt ? date('Y-m-d H:i:s', $mt) : '?') . ")\n"; + } +} else { + echo "Backups: none (uploads/backups missing)\n"; +} + +if (!is_file($dbPath)) { + exit(0); +} + +$candidates = [ + 'current .env (get_master_key_bytes)' => get_master_key_bytes(), + 'legacy fallback (pre-April 2026 default)' => str_pad('12345678901234567890123456789012', 32, "\0"), + 'raw first 32 chars of QDB_MASTER_KEY' => str_pad(substr($keyEnv, 0, 32), 32, "\0"), +]; +if ($b64 !== false && strlen($b64) > 0) { + $candidates['base64-decoded .env only'] = str_pad(substr($b64, 0, 32), 32, "\0"); +} + +echo "\nDecrypt probe (which key matches questionnaire_database?):\n"; +$matchedLabel = null; +$matchedKey = null; +$appKey = get_master_key_bytes(); +foreach ($candidates as $label => $keyBytes) { + $probe = qdb_probe_decrypt($dbPath, $keyBytes); + if ($probe['ok']) { + echo " MATCH: $label\n"; + if ($matchedLabel === null) { + $matchedLabel = $label; + $matchedKey = $keyBytes; + } + } else { + echo " no: $label\n"; + } +} + +if ($matchedLabel === null) { + echo "\nNo known key opened the file. Likely causes:\n"; + echo " - DB was encrypted with a different QDB_MASTER_KEY than in .env now\n"; + echo " - File was corrupted (e.g. interrupted save); try an older uploads/backups copy\n"; + echo " - Wrong DB path (another install under /var/www/...)\n"; + echo "\nProbe other copies, e.g.:\n"; + echo " php cli/diagnose_db.php /var/www/html/qdb/uploads/questionnaire_database\n"; + echo "\nWiping is NOT required for schema updates. Only wipe if you accept data loss\n"; + echo "and run: mv uploads/questionnaire_database uploads/questionnaire_database.bak\n"; + echo "then: php seed_admin.php \n"; + exit(1); +} + +$dbMatchesAppKey = $matchedKey !== null && hash_equals($matchedKey, $appKey); + +if ($dbPath !== QDB_PATH && $dbMatchesAppKey) { + echo "\nThis file decrypts with your .env. Restore it into the app uploads dir:\n"; + echo " cp " . escapeshellarg($dbPath) . ' ' . escapeshellarg(QDB_PATH) . "\n"; + echo " chown www-data:www-data " . escapeshellarg(QDB_PATH) . "\n"; + exit(0); +} + +if (!$dbMatchesAppKey) { + echo "\nThe DB does NOT match get_master_key_bytes() but matches: $matchedLabel\n"; + echo "Fix: align QDB_MASTER_KEY in .env and php-fpm pool env, then re-run diagnose.\n"; + exit(1); +} + +if ($dbPath !== QDB_PATH) { + echo "\nDecrypt OK for alternate path. Run without arguments after copying into uploads/.\n"; + exit(0); +} + +try { + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $version = (int)$pdo->query('PRAGMA user_version')->fetchColumn(); + $users = (int)$pdo->query('SELECT COUNT(*) FROM users')->fetchColumn(); + $hasSession = qdb_table_exists($pdo, 'session'); + qdb_discard($tmpDb, $lockFp); + echo "\nOK: full open + migrations succeeded.\n"; + echo " PRAGMA user_version: $version (expected " . QDB_VERSION . ")\n"; + echo " users: $users\n"; + echo " session table: " . ($hasSession ? 'yes' : 'MISSING') . "\n"; + exit(0); +} catch (Throwable $e) { + echo "\nDecrypt OK but qdb_open failed: " . $e->getMessage() . "\n"; + exit(1); +} diff --git a/cli/diagnose_login.php b/cli/diagnose_login.php new file mode 100644 index 0000000..fae7372 --- /dev/null +++ b/cli/diagnose_login.php @@ -0,0 +1,77 @@ + + */ +if (php_sapi_name() !== 'cli') { + exit(1); +} + +require_once __DIR__ . '/../common.php'; +require_once __DIR__ . '/../db_init.php'; + +$username = $argv[1] ?? ''; +$password = $argv[2] ?? ''; +if ($username === '' || $password === '') { + fwrite(STDERR, "Usage: php cli/diagnose_login.php \n"); + fwrite(STDERR, "Run as www-data to match php-fpm: sudo -u www-data php cli/diagnose_login.php ...\n"); + exit(1); +} + +echo 'User: ' . (function_exists('posix_getpwuid') ? (posix_getpwuid(posix_geteuid())['name'] ?? '?') : '?') . "\n"; +echo 'QDB_MASTER_KEY env length: ' . strlen(getenv('QDB_MASTER_KEY') ?: '') . "\n"; +echo 'Derived key sha256: ' . hash('sha256', get_master_key_bytes()) . "\n\n"; + +try { + echo "1) qdb_open(read-only)... "; + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + echo "OK\n"; + + echo "2) lookup user... "; + $stmt = $pdo->prepare( + "SELECT userID, passwordHash, role, entityID, mustChangePassword FROM users WHERE username = :u" + ); + $stmt->execute([':u' => $username]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + if (!$user) { + qdb_discard($tmpDb, $lockFp); + echo "FAIL — user not found\n"; + exit(1); + } + echo "OK (role={$user['role']}, mustChange={$user['mustChangePassword']})\n"; + + echo "3) password_verify... "; + if (!password_verify($password, $user['passwordHash'])) { + qdb_discard($tmpDb, $lockFp); + echo "FAIL — wrong password\n"; + exit(1); + } + echo "OK\n"; + + qdb_discard($tmpDb, $lockFp); + + echo "4) token_add (writable save, same as login)... "; + $token = bin2hex(random_bytes(32)); + token_add($token, 120, [ + 'role' => $user['role'], + 'entityID' => $user['entityID'], + 'userID' => $user['userID'], + 'temp' => (int)$user['mustChangePassword'] === 1, + ]); + echo "OK\n"; + + echo "5) token_get_record... "; + $rec = token_get_record($token); + if (!$rec) { + echo "FAIL — session not readable after save\n"; + exit(1); + } + echo "OK\n"; + + token_revoke($token); + echo "\nAll login steps succeeded. If the website still fails, check nginx routing (curl below).\n"; +} catch (Throwable $e) { + echo "FAIL\n " . $e->getMessage() . "\n"; + echo " " . $e->getFile() . ':' . $e->getLine() . "\n"; + exit(1); +} diff --git a/common.php b/common.php new file mode 100644 index 0000000..b019ae8 --- /dev/null +++ b/common.php @@ -0,0 +1,2398 @@ + + */ +$GLOBALS['qdb_dotenv'] = []; + +function qdb_env_file_path(): string { + return __DIR__ . '/.env'; +} + +/** + * Parse a dotenv file. Does not use getenv/putenv. + * Not cached when unreadable (FPM workers can start before .env exists). + * + * @return array + */ +function qdb_parse_env_file(string $path): array { + static $cache = []; + $mtime = @filemtime($path) ?: 0; + $cacheKey = $path . "\0" . $mtime; + if ($mtime > 0 && isset($cache[$cacheKey])) { + return $cache[$cacheKey]; + } + $out = []; + if (!is_readable($path)) { + return $out; + } + $lines = file($path, FILE_IGNORE_NEW_LINES); + if ($lines === false) { + return $out; + } + foreach ($lines as $line) { + $line = trim($line); + if ($line === '' || $line[0] === '#') { + continue; + } + $eq = strpos($line, '='); + if ($eq === false) { + continue; + } + $name = trim(substr($line, 0, $eq)); + $value = trim(substr($line, $eq + 1)); + if ($name === '') { + continue; + } + $len = strlen($value); + if ($len >= 2) { + $q = $value[0]; + if (($q === '"' && $value[$len - 1] === '"') || ($q === "'" && $value[$len - 1] === "'")) { + $value = substr($value, 1, -1); + } + } + $out[$name] = $value; + } + if ($mtime > 0) { + $cache[$cacheKey] = $out; + } + return $out; +} + +/** + * Read an environment variable (.env file first, then getenv / $_ENV / $_SERVER). + */ +function qdb_env_get(string $name): ?string { + $fileVars = qdb_parse_env_file(qdb_env_file_path()); + if (isset($fileVars[$name]) && $fileVars[$name] !== '') { + return $fileVars[$name]; + } + if (isset($GLOBALS['qdb_dotenv'][$name]) && $GLOBALS['qdb_dotenv'][$name] !== '') { + return $GLOBALS['qdb_dotenv'][$name]; + } + // Do not use getenv() for QDB_MASTER_KEY — FPM can retain a stale system value + // when .env was missing on the worker's first request. + if ($name === 'QDB_MASTER_KEY') { + return null; + } + $v = getenv($name); + if ($v !== false && $v !== '') { + return $v; + } + if (isset($_ENV[$name]) && $_ENV[$name] !== '') { + return (string)$_ENV[$name]; + } + if (isset($_SERVER[$name]) && $_SERVER[$name] !== '') { + return (string)$_SERVER[$name]; + } + return null; +} + +/** Set an environment variable for the current request (best-effort putenv). */ +function qdb_env_set(string $name, string $value): void { + $_ENV[$name] = $value; + $_SERVER[$name] = $value; + if (function_exists('putenv')) { + @putenv("$name=$value"); + } +} + +/** Load KEY=value pairs from .env into $GLOBALS['qdb_dotenv'] and $_ENV. */ +function qdb_load_dotenv(string $path): void { + foreach (qdb_parse_env_file($path) as $name => $value) { + $GLOBALS['qdb_dotenv'][$name] = $value; + qdb_env_set($name, $value); + } +} + +qdb_load_dotenv(qdb_env_file_path()); + +// --- MASTER-KEY (Server-seitig zum Speichern der DB) --- +// Per ENV 'QDB_MASTER_KEY' (Base64- oder ASCII-String). +function get_master_key_bytes(): string { + $env = qdb_env_get('QDB_MASTER_KEY'); + if ($env === null) { + $hint = is_readable(qdb_env_file_path()) ? 'missing QDB_MASTER_KEY in .env' + : '.env not readable by PHP (' . qdb_env_file_path() . ')'; + throw new RuntimeException('QDB_MASTER_KEY is not set. ' . $hint); + } + $b = base64_decode($env, true); + if ($b !== false && strlen($b) > 0) { + return str_pad(substr($b, 0, 32), 32, "\0"); + } + return str_pad(substr($env, 0, 32), 32, "\0"); +} + +// --- HKDF (SHA-256) über Token -> Session-Key (32 Byte) --- +// Kompatibel zu Android-Implementierung: salt = leer, info = "qdb-aes" +function hkdf_session_key_from_token(string $tokenHex, string $info = 'qdb-aes', int $len = 32): string { + $ikm = @hex2bin(trim($tokenHex)); + if ($ikm === false) $ikm = $tokenHex; // Falls bereits binär + if (function_exists('hash_hkdf')) { + return hash_hkdf('sha256', $ikm, $len, $info, ''); + } + // Fallback: eigene HKDF-Implementierung + $hashLen = 32; + $salt = str_repeat("\0", $hashLen); + $prk = hash_hmac('sha256', $ikm, $salt, true); + $okm = ''; + $t = ''; + $n = (int)ceil($len / $hashLen); + for ($i = 1; $i <= $n; $i++) { + $t = hash_hmac('sha256', $t . $info . chr($i), $prk, true); + $okm .= $t; + } + return substr($okm, 0, $len); +} + +// --- Token-Storage (DB-backed via session table) --- + +function token_storage_key(string $token): string { + return hash('sha256', $token); +} + +function token_add(string $token, int $ttlSeconds = 86400, array $extra = []): void { + require_once __DIR__ . '/db_init.php'; + [$pdo, $tmpDb, $lockFp] = qdb_open(true); + $now = time(); + $pdo->prepare( + "INSERT INTO session (token, userID, role, entityID, createdAt, expiresAt, temp) + VALUES (:t, :uid, :role, :eid, :ca, :ea, :tmp)" + )->execute([ + ':t' => token_storage_key($token), + ':uid' => $extra['userID'] ?? '', + ':role' => $extra['role'] ?? '', + ':eid' => $extra['entityID'] ?? '', + ':ca' => $now, + ':ea' => $now + $ttlSeconds, + ':tmp' => (int)(!empty($extra['temp'])), + ]); + // Opportunistic cleanup: remove expired sessions + $pdo->prepare("DELETE FROM session WHERE expiresAt < :now")->execute([':now' => $now]); + qdb_save($tmpDb, $lockFp); +} + +function token_get_record(string $token, bool $refreshExpiry = true): ?array { + require_once __DIR__ . '/db_init.php'; + require_once __DIR__ . '/lib/settings.php'; + [$pdo, $tmpDb, $lockFp] = qdb_open($refreshExpiry); + $now = time(); + $stmt = $pdo->prepare("SELECT * FROM session WHERE token = :t AND expiresAt >= :now"); + $storedToken = token_storage_key($token); + $stmt->execute([':t' => $storedToken, ':now' => $now]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + if (!$row) { + qdb_discard($tmpDb, $lockFp); + return null; + } + if ($refreshExpiry) { + $settings = qdb_settings_get_on_pdo($pdo); + $newExpiry = $now + qdb_session_ttl_seconds($settings, !empty($row['temp'])); + $pdo->prepare('UPDATE session SET expiresAt = :exp WHERE token = :t') + ->execute([':exp' => $newExpiry, ':t' => $storedToken]); + $row['expiresAt'] = $newExpiry; + qdb_save($tmpDb, $lockFp); + } else { + qdb_discard($tmpDb, $lockFp); + } + // Map DB columns to the keys the rest of the codebase expects + return [ + 'token' => $row['token'], + 'role' => $row['role'], + 'entityID' => $row['entityID'], + 'userID' => $row['userID'], + 'created' => (int)$row['createdAt'], + 'exp' => (int)$row['expiresAt'], + 'temp' => (int)$row['temp'], + ]; +} + +function token_revoke(string $token): void { + require_once __DIR__ . '/db_init.php'; + [$pdo, $tmpDb, $lockFp] = qdb_open(true); + $pdo->prepare("DELETE FROM session WHERE token = :t")->execute([':t' => token_storage_key($token)]); + qdb_save($tmpDb, $lockFp); +} + +/** Revoke all sessions for one user on an open writable connection. */ +function token_revoke_all_for_user_on_pdo(PDO $pdo, string $userID): int { + if ($userID === '') { + return 0; + } + $stmt = $pdo->prepare('DELETE FROM session WHERE userID = :uid'); + $stmt->execute([':uid' => $userID]); + return $stmt->rowCount(); +} + +/** Revoke every login session for a user (after password change or admin reset). */ +function token_revoke_all_for_user(string $userID): int { + if ($userID === '') { + return 0; + } + require_once __DIR__ . '/db_init.php'; + [$pdo, $tmpDb, $lockFp] = qdb_open(true); + $removed = token_revoke_all_for_user_on_pdo($pdo, $userID); + qdb_save($tmpDb, $lockFp); + return $removed; +} + +/** Revoke every stored session (all users and devices). */ +function token_revoke_all_on_pdo(PDO $pdo): int +{ + if (!qdb_table_exists($pdo, 'session')) { + return 0; + } + $count = (int)$pdo->query('SELECT COUNT(*) FROM session')->fetchColumn(); + $pdo->exec('DELETE FROM session'); + return $count; +} + +function token_revoke_all(): int +{ + require_once __DIR__ . '/db_init.php'; + [$pdo, $tmpDb, $lockFp] = qdb_open(true); + $removed = token_revoke_all_on_pdo($pdo); + qdb_save($tmpDb, $lockFp); + return $removed; +} + +// --- Auth helpers for endpoints --- +function get_bearer_token(): ?string { + $auth = $_SERVER['HTTP_AUTHORIZATION'] ?? ($_SERVER['Authorization'] ?? ''); + if (stripos($auth, 'Bearer ') === 0) { + return substr($auth, 7); + } + return null; +} + +function require_valid_token(): array { + $token = get_bearer_token(); + if (!$token) { + json_error('UNAUTHORIZED', 'Missing Bearer token', 401); + } + $rec = token_get_record($token); + if (!$rec) { + json_error('UNAUTHORIZED', 'Invalid or expired token', 401); + } + if (!empty($rec['temp'])) { + json_error('PASSWORD_CHANGE_REQUIRED', 'Password change required before access', 403); + } + $rec['_token'] = $token; + return $rec; +} + +function require_role(array $allowed, array $tokenRecord): void { + $role = $tokenRecord['role'] ?? ''; + if (!in_array($role, $allowed, true)) { + json_error('FORBIDDEN', 'Insufficient permissions', 403); + } +} + +/** Roles that may use the management website (not the mobile app). */ +function qdb_web_roles(): array { + return ['admin', 'supervisor']; +} + +/** True when the request originates from the management website (see website/js/api.js). */ +function qdb_is_web_client_request(): bool { + $h = $_SERVER['HTTP_X_QDB_CLIENT'] ?? ''; + return strtolower(trim($h)) === 'web'; +} + +/** Website or coordinator mobile app — not the counselor field app. */ +function qdb_is_management_client_request(): bool { + $h = strtolower(trim($_SERVER['HTTP_X_QDB_CLIENT'] ?? '')); + return in_array($h, ['web', 'coordinator'], true); +} + +/** Reject coach sign-in from management clients; coaches use the field app only. */ +function qdb_reject_coach_web_login(string $role): void { + if ($role === 'coach' && qdb_is_management_client_request()) { + json_error( + 'FORBIDDEN', + 'Counselor accounts can only sign in through the mobile app.', + 403 + ); + } +} + +/** Valid bearer token plus admin/supervisor role (website API). */ +function require_valid_token_web(): array { + $rec = require_valid_token(); + require_role(qdb_web_roles(), $rec); + return $rec; +} + +/** + * Build a SQL WHERE clause fragment that restricts client visibility by role. + * Returns [string $clause, array $params]. + * $clientAlias is the table alias or name that has a coachID column (e.g. 'client'). + */ +/** + * @param string $archiveMode active — non-archived only (default); archived — archived only; all — no archive filter + */ +function rbac_client_filter( + array $tokenRecord, + string $clientAlias = 'client', + string $archiveMode = 'active' +): array { + $role = $tokenRecord['role'] ?? ''; + $entityID = $tokenRecord['entityID'] ?? ''; + switch ($role) { + case 'admin': + $clause = '1=1'; + $params = []; + break; + case 'supervisor': + $clause = "$clientAlias.coachID IN (SELECT coachID FROM coach WHERE supervisorID = :rbac_eid)"; + $params = [':rbac_eid' => $entityID]; + break; + case 'coach': + $clause = "$clientAlias.coachID = :rbac_eid"; + $params = [':rbac_eid' => $entityID]; + break; + default: + return ['0=1', []]; + } + + $archiveClause = match ($archiveMode) { + 'archived' => "COALESCE($clientAlias.archived, 0) = 1", + 'all' => '1=1', + default => "COALESCE($clientAlias.archived, 0) = 0", + }; + + return ["($clause) AND ($archiveClause)", $params]; +} + +// --- Translations: German (de) is the canonical source language --- +define('QDB_SOURCE_LANGUAGE', 'de'); + +function qdb_ensure_source_language(PDO $pdo): void { + $pdo->prepare("INSERT INTO language (languageCode, name) VALUES (:lc, :n) + ON CONFLICT(languageCode) DO NOTHING") + ->execute([':lc' => QDB_SOURCE_LANGUAGE, ':n' => 'German']); +} + +function qdb_upsert_source_translation(PDO $pdo, string $type, string $id, string $text): void { + qdb_put_translation($pdo, $type, $id, QDB_SOURCE_LANGUAGE, $text); +} + +/** Catalog of global app UI string keys (LanguageManager). */ +function qdb_app_string_catalog(bool $reload = false): array { + static $catalog = null; + if (!$reload && $catalog !== null) { + return $catalog; + } + $path = qdb_app_string_catalog_path(); + if (!is_readable($path)) { + $catalog = ['keys' => [], 'germanDefaults' => []]; + return $catalog; + } + $data = json_decode(file_get_contents($path), true); + $catalog = is_array($data) ? $data : ['keys' => [], 'germanDefaults' => []]; + return $catalog; +} + +function qdb_app_string_catalog_path(): string { + return __DIR__ . '/data/app_ui_strings.json'; +} + +/** Remove questionnaire_group_* from catalog file (keys list + germanDefaults). */ +function qdb_remove_questionnaire_group_from_catalog(string $stringKey): void { + if (!qdb_is_questionnaire_group_string_key($stringKey)) { + return; + } + $path = qdb_app_string_catalog_path(); + if (!is_readable($path) || !is_writable($path)) { + return; + } + $data = json_decode(file_get_contents($path), true); + if (!is_array($data)) { + return; + } + $changed = false; + if (!empty($data['keys']) && is_array($data['keys'])) { + $next = array_values(array_filter( + $data['keys'], + fn($k) => (string)$k !== $stringKey + )); + if (count($next) !== count($data['keys'])) { + $data['keys'] = $next; + $changed = true; + } + } + if (!empty($data['germanDefaults']) && is_array($data['germanDefaults']) && array_key_exists($stringKey, $data['germanDefaults'])) { + unset($data['germanDefaults'][$stringKey]); + $changed = true; + } + if (!$changed) { + return; + } + file_put_contents( + $path, + json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n", + LOCK_EX + ); + qdb_app_string_catalog(true); +} + +function qdb_app_string_key_set(): array { + $keys = qdb_app_string_catalog()['keys'] ?? []; + return array_flip($keys); +} + +/** Keys for dev-only UI (database export, etc.) — excluded from website App UI and app sync catalog. */ +function qdb_dev_only_app_string_key_set(): array { + static $set = null; + if ($set !== null) { + return $set; + } + $set = array_flip([ + 'database', + 'database_clients_title', + 'download_header', + 'export_success_downloads', + 'export_failed', + 'headers', + 'view_missing', + 'no_header_template_found', + 'saved_pdf_csv', + 'no_pdf_viewer', + 'save_error', + 'no_clients_available', + 'questionnaire_id', + 'questionnaires', + 'status', + 'data', + 'data_final_warning', + 'open_client_via_load', + 'ask_before_upload', + 'You have completed questionnaires that have not been uploaded yet.', + 'You have completed questionnaires that have not been uploaded yet. Tap to open the app and upload.', + ]); + return $set; +} + +/** Prefix for app UI strings that label questionnaire category groups on the opening screen. */ +function qdb_questionnaire_group_prefix(): string { + return 'questionnaire_group_'; +} + +function qdb_questionnaire_group_string_key(string $categoryKey): string { + $categoryKey = trim($categoryKey); + return $categoryKey === '' ? '' : qdb_questionnaire_group_prefix() . $categoryKey; +} + +function qdb_is_questionnaire_group_string_key(string $stringKey): bool { + return str_starts_with($stringKey, qdb_questionnaire_group_prefix()); +} + +function qdb_category_key_from_group_string_key(string $stringKey): string { + $prefix = qdb_questionnaire_group_prefix(); + return str_starts_with($stringKey, $prefix) ? substr($stringKey, strlen($prefix)) : ''; +} + +/** @return array categoryKey => questionnaire count */ +function qdb_category_keys_in_use(PDO $pdo): array { + $map = []; + $rows = $pdo->query( + "SELECT categoryKey, COUNT(*) AS c FROM questionnaire + WHERE trim(categoryKey) != '' GROUP BY categoryKey" + )->fetchAll(PDO::FETCH_ASSOC); + foreach ($rows as $row) { + $k = trim((string)($row['categoryKey'] ?? '')); + if ($k !== '') { + $map[$k] = (int)$row['c']; + } + } + return $map; +} + +/** + * App string entries for category keys in use (not already in the static JSON catalog keys list). + * @return list + */ +function qdb_category_group_string_entries(PDO $pdo): array { + $catalog = qdb_app_string_catalog(); + $defaults = $catalog['germanDefaults'] ?? []; + $staticKeys = qdb_app_string_key_set(); + $entries = []; + $order = 0; + foreach (qdb_category_keys_in_use($pdo) as $catKey => $_count) { + $stringKey = qdb_questionnaire_group_string_key($catKey); + if ($stringKey === '' || isset($staticKeys[$stringKey])) { + continue; + } + $entries[] = [ + 'key' => $stringKey, + 'displayKey' => $stringKey, + 'type' => 'app_string', + 'entityId' => $stringKey, + 'sortOrder' => $order++, + 'germanDefault' => $defaults[$stringKey] ?? $catKey, + 'categoryKey' => $catKey, + ]; + } + usort($entries, fn($a, $b) => strcmp($a['categoryKey'], $b['categoryKey'])); + return $entries; +} + +/** + * Category keys with translations in DB but no questionnaire using them. + * @return list + */ +function qdb_orphaned_category_keys(PDO $pdo): array { + $inUse = qdb_category_keys_in_use($pdo); + $prefix = qdb_questionnaire_group_prefix(); + $orphans = []; + $stmt = $pdo->query( + "SELECT DISTINCT stringKey FROM string_translation WHERE stringKey LIKE " . $pdo->quote($prefix . '%') + ); + foreach ($stmt->fetchAll(PDO::FETCH_COLUMN) as $stringKey) { + $catKey = qdb_category_key_from_group_string_key((string)$stringKey); + if ($catKey !== '' && !isset($inUse[$catKey])) { + $orphans[$catKey] = true; + } + } + ksort($orphans); + return array_keys($orphans); +} + +/** + * @return list + */ +function qdb_list_category_keys_for_dashboard(PDO $pdo): array { + $defaults = qdb_app_string_catalog()['germanDefaults'] ?? []; + $inUse = qdb_category_keys_in_use($pdo); + $list = []; + + $loadGerman = function (string $stringKey) use ($pdo, $defaults): string { + $stmt = $pdo->prepare( + 'SELECT text FROM string_translation WHERE stringKey = :k AND languageCode = :lang LIMIT 1' + ); + $stmt->execute([':k' => $stringKey, ':lang' => QDB_SOURCE_LANGUAGE]); + $text = $stmt->fetchColumn(); + if (is_string($text) && $text !== '') { + return $text; + } + return $defaults[$stringKey] ?? ''; + }; + + foreach ($inUse as $catKey => $count) { + $stringKey = qdb_questionnaire_group_string_key($catKey); + $list[] = [ + 'categoryKey' => $catKey, + 'stringKey' => $stringKey, + 'questionnaireCount' => $count, + 'orphaned' => false, + 'germanLabel' => $loadGerman($stringKey) ?: $catKey, + ]; + } + + foreach (qdb_orphaned_category_keys($pdo) as $catKey) { + $stringKey = qdb_questionnaire_group_string_key($catKey); + $list[] = [ + 'categoryKey' => $catKey, + 'stringKey' => $stringKey, + 'questionnaireCount' => 0, + 'orphaned' => true, + 'germanLabel' => $loadGerman($stringKey) ?: $catKey, + ]; + } + + usort($list, fn($a, $b) => strcmp($a['categoryKey'], $b['categoryKey'])); + return $list; +} + +/** Persist default German for a questionnaire_group_* key in app_ui_strings.json. */ +function qdb_update_questionnaire_group_german_default(string $stringKey, string $text): void { + if (!qdb_is_questionnaire_group_string_key($stringKey)) { + return; + } + $path = qdb_app_string_catalog_path(); + if (!is_readable($path) || !is_writable($path)) { + return; + } + $data = json_decode(file_get_contents($path), true); + if (!is_array($data)) { + return; + } + if (!isset($data['germanDefaults']) || !is_array($data['germanDefaults'])) { + $data['germanDefaults'] = []; + } + if (($data['germanDefaults'][$stringKey] ?? '') === $text) { + return; + } + $data['germanDefaults'][$stringKey] = $text; + file_put_contents( + $path, + json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n", + LOCK_EX + ); + qdb_app_string_catalog(true); +} + +/** Set German label for a category (app opening screen group title). */ +function qdb_set_category_german_label(PDO $pdo, string $categoryKey, string $text): string { + $categoryKey = trim($categoryKey); + if ($categoryKey === '') { + json_error('INVALID_FIELD', 'categoryKey is required', 400); + } + $text = trim($text); + if ($text === '') { + json_error('INVALID_FIELD', 'German label is required', 400); + } + $stringKey = qdb_questionnaire_group_string_key($categoryKey); + qdb_set_app_string_german_label($pdo, $stringKey, $text); + qdb_update_questionnaire_group_german_default($stringKey, $text); + return $stringKey; +} + +/** + * Remove a category key completely: clear questionnaires, translations, and catalog defaults. + * @return int questionnaires updated + */ +function qdb_delete_category_key(PDO $pdo, string $categoryKey): int { + $categoryKey = trim($categoryKey); + if ($categoryKey === '') { + json_error('INVALID_FIELD', 'categoryKey is required', 400); + } + $stringKey = qdb_questionnaire_group_string_key($categoryKey); + + $stmt = $pdo->prepare('UPDATE questionnaire SET categoryKey = \'\' WHERE categoryKey = :ck'); + $stmt->execute([':ck' => $categoryKey]); + $cleared = $stmt->rowCount(); + + $pdo->prepare('DELETE FROM string_translation WHERE stringKey = :k') + ->execute([':k' => $stringKey]); + + qdb_remove_questionnaire_group_from_catalog($stringKey); + + return $cleared; +} + +/** + * Global app UI strings (screens, toasts, auth, etc.) for the mobile app. + * @return list + */ +function qdb_app_ui_string_entries(): array { + $catalog = qdb_app_string_catalog(); + $defaults = $catalog['germanDefaults'] ?? []; + $entries = []; + $order = 0; + foreach ($catalog['keys'] ?? [] as $key) { + if (qdb_is_questionnaire_group_string_key($key)) { + continue; + } + $entries[] = [ + 'key' => $key, + 'displayKey' => $key, + 'type' => 'app_string', + 'entityId' => $key, + 'sortOrder' => $order++, + 'germanDefault' => $defaults[$key] ?? $key, + ]; + } + return $entries; +} + +/** + * Static catalog plus questionnaire_group_* keys for categoryKey values in use. + * @return list + */ +/** messageKey field inside questionnaire conditionJson (app locked hint). */ +function qdb_message_key_from_condition_json(?string $json): string { + if ($json === null || trim($json) === '' || trim($json) === '{}') { + return ''; + } + $obj = json_decode($json, true); + if (!is_array($obj)) { + return ''; + } + $key = trim((string)($obj['messageKey'] ?? '')); + return $key !== '' && qdb_is_stable_key($key) ? $key : ''; +} + +/** @return array */ +function qdb_collect_condition_message_keys(PDO $pdo): array { + $keys = []; + foreach ($pdo->query('SELECT conditionJson FROM questionnaire')->fetchAll(PDO::FETCH_COLUMN) as $json) { + $mk = qdb_message_key_from_condition_json($json); + if ($mk !== '') { + $keys[$mk] = true; + } + } + return $keys; +} + +/** + * App string entries for condition messageKey values referenced in questionnaire conditions. + * @return list + */ +function qdb_condition_message_string_entries(PDO $pdo): array { + $staticKeys = qdb_app_string_key_set(); + $defaults = qdb_app_string_catalog()['germanDefaults'] ?? []; + $entries = []; + $order = 0; + foreach (array_keys(qdb_collect_condition_message_keys($pdo)) as $key) { + if (isset($staticKeys[$key])) { + continue; + } + $entries[] = [ + 'key' => $key, + 'displayKey' => $key, + 'type' => 'app_string', + 'entityId' => $key, + 'sortOrder' => $order++, + 'germanDefault' => $defaults[$key] ?? $key, + ]; + } + usort($entries, fn($a, $b) => strcmp($a['key'], $b['key'])); + return $entries; +} + +function qdb_update_app_string_german_default(string $stringKey, string $text): void { + $path = qdb_app_string_catalog_path(); + if (!is_readable($path) || !is_writable($path)) { + return; + } + $data = json_decode(file_get_contents($path), true); + if (!is_array($data)) { + return; + } + if (!isset($data['germanDefaults']) || !is_array($data['germanDefaults'])) { + $data['germanDefaults'] = []; + } + if (($data['germanDefaults'][$stringKey] ?? '') === $text) { + return; + } + $data['germanDefaults'][$stringKey] = $text; + file_put_contents( + $path, + json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n", + LOCK_EX + ); + qdb_app_string_catalog(true); +} + +function qdb_set_app_string_german_label(PDO $pdo, string $stringKey, string $text): void { + $stringKey = qdb_validate_stable_key($stringKey, 'messageKey'); + $text = trim($text); + if ($text === '') { + json_error('INVALID_FIELD', 'German label is required', 400); + } + qdb_put_translation($pdo, 'app_string', $stringKey, QDB_SOURCE_LANGUAGE, $text); + qdb_update_app_string_german_default($stringKey, $text); +} + +function qdb_app_ui_string_entries_merged(PDO $pdo): array { + $merged = qdb_app_ui_string_entries(); + $existing = []; + foreach ($merged as $e) { + $existing[$e['key']] = true; + } + $order = count($merged); + foreach (qdb_category_group_string_entries($pdo) as $e) { + if (!isset($existing[$e['key']])) { + $e['sortOrder'] = $order++; + $merged[] = $e; + $existing[$e['key']] = true; + } + } + foreach (qdb_condition_message_string_entries($pdo) as $e) { + if (!isset($existing[$e['key']])) { + $e['sortOrder'] = $order++; + $merged[] = $e; + $existing[$e['key']] = true; + } + } + return $merged; +} + +/** + * All string keys referenced by questionnaire content (questions, options, config). + * @return array + */ +function qdb_all_questionnaire_translation_key_set(PDO $pdo): array { + static $cache = null; + if ($cache !== null) { + return $cache; + } + + $keys = []; + $rows = $pdo->query("SELECT defaultText, configJson FROM question")->fetchAll(PDO::FETCH_ASSOC); + foreach ($rows as $dbQ) { + $config = json_decode($dbQ['configJson'] ?: '{}', true) ?: []; + $qk = qdb_question_key($config, $dbQ['defaultText']); + if ($qk !== '') { + $keys[$qk] = true; + if (!empty($config['noteBefore'])) { + $keys[qdb_note_before_key($qk)] = true; + } + if (!empty($config['noteAfter'])) { + $keys[qdb_note_after_key($qk)] = true; + } + } elseif ($dbQ['defaultText'] !== '') { + $keys[$dbQ['defaultText']] = true; + } + foreach (['textKey', 'textKey1', 'textKey2', 'hint', 'hint1', 'hint2'] as $field) { + if (!empty($config[$field])) { + $keys[$config[$field]] = true; + } + } + if (!empty($config['symptoms']) && is_array($config['symptoms'])) { + foreach ($config['symptoms'] as $s) { + if ($s !== '') { + $keys[$s] = true; + } + } + } + if (!empty($config['options']) && is_array($config['options'])) { + foreach ($config['options'] as $opt) { + if (is_string($opt) && $opt !== '') { + $keys[$opt] = true; + } elseif (is_array($opt) && !empty($opt['key'])) { + $keys[$opt['key']] = true; + } + } + } + } + + foreach ($pdo->query("SELECT defaultText FROM answer_option")->fetchAll(PDO::FETCH_COLUMN) as $text) { + if ($text !== '') { + $keys[$text] = true; + } + } + + $cache = $keys; + return $cache; +} + +/** + * App UI catalog for the website Translations page (coach-visible, no dev-only keys). + * Catalog keys always appear here even when also used inside a questionnaire (e.g. "save"). + */ +function qdb_app_ui_string_entries_for_website(PDO $pdo): array { + $dev = qdb_dev_only_app_string_key_set(); + $entries = []; + foreach (qdb_app_ui_string_entries_merged($pdo) as $e) { + if (!isset($dev[$e['key']])) { + $entries[] = $e; + } + } + return $entries; +} + +/** Upsert one translation row; sync German text into defaultText for questions/options. */ +function qdb_put_translation(PDO $pdo, string $type, string $id, string $lang, string $text): void { + if ($type === 'question') { + $pdo->prepare("INSERT INTO question_translation (questionID, languageCode, text) + VALUES (:id, :lang, :t) + ON CONFLICT(questionID, languageCode) DO UPDATE SET text = excluded.text") + ->execute([':id' => $id, ':lang' => $lang, ':t' => $text]); + if ($lang === QDB_SOURCE_LANGUAGE) { + $pdo->prepare("UPDATE question SET defaultText = :t WHERE questionID = :id") + ->execute([':t' => $text, ':id' => $id]); + } + } elseif ($type === 'answer_option') { + $pdo->prepare("INSERT INTO answer_option_translation (answerOptionID, languageCode, text) + VALUES (:id, :lang, :t) + ON CONFLICT(answerOptionID, languageCode) DO UPDATE SET text = excluded.text") + ->execute([':id' => $id, ':lang' => $lang, ':t' => $text]); + // defaultText holds optionKey; German label lives only in answer_option_translation. + } elseif ($type === 'string' || $type === 'app_string') { + $pdo->prepare("INSERT INTO string_translation (stringKey, languageCode, text) + VALUES (:id, :lang, :t) + ON CONFLICT(stringKey, languageCode) DO UPDATE SET text = excluded.text") + ->execute([':id' => $id, ':lang' => $lang, ':t' => $text]); + } +} + +/** Short label for translation UI (Key column); full text stays in `key` for API lookup. */ +function qdb_translation_display_key(array $e): string { + return qdb_translation_row_label($e); +} + +function qdb_short_entity_label(string $entityId): string { + $pos = strrpos($entityId, '__'); + return $pos !== false ? substr($entityId, $pos + 2) : $entityId; +} + +function qdb_require_non_empty_german(string $text, string $label = 'German text'): void { + if ($text === '') { + json_error('MISSING_FIELDS', "$label is required", 400); + } +} + +/** Stable identifier: snake_case starting with a letter (case preserved for LM keys). */ +function qdb_is_stable_key(string $s): bool { + return $s !== '' && (bool)preg_match('/^[a-zA-Z][a-zA-Z0-9_]*$/', $s); +} + +/** + * Coerce an arbitrary key string into a stable identifier (letters, digits, underscores; + * must start with a letter). Used on import and API writes so bundles are not limited to + * pre-validated keys (e.g. numeric option labels "1", "42"). + */ +function qdb_normalize_stable_key(string $s): string { + $s = trim($s); + if ($s === '') { + return ''; + } + if (qdb_is_stable_key($s)) { + return $s; + } + $out = preg_replace('/[^a-zA-Z0-9_]+/', '_', $s); + $out = preg_replace('/_+/', '_', $out ?? ''); + $out = trim($out, '_'); + if ($out === '') { + return 'k_' . substr(hash('crc32b', $s), 0, 8); + } + if (!preg_match('/^[a-zA-Z]/', $out)) { + $out = 'k_' . $out; + } + return qdb_is_stable_key($out) ? $out : 'k_' . substr(hash('crc32b', $s), 0, 8); +} + +function qdb_validate_stable_key(string $s, string $label = 'Key'): string { + $s = trim($s); + if ($s === '') { + json_error('INVALID_FIELD', "$label is required", 400); + } + $normalized = qdb_normalize_stable_key($s); + if (!qdb_is_stable_key($normalized)) { + json_error('INVALID_FIELD', "$label could not be normalized to a valid key", 400); + } + return $normalized; +} + +/** Resolve app lookup key for a question row. */ +function qdb_question_key(array $config, string $defaultText): string { + $key = trim((string)($config['questionKey'] ?? '')); + if ($key !== '' && qdb_is_stable_key($key)) { + return $key; + } + if (qdb_is_stable_key($defaultText)) { + return $defaultText; + } + return ''; +} + +/** Option key is stored in answer_option.defaultText. */ +function qdb_option_key(array $aoRow): string { + $dt = trim((string)($aoRow['defaultText'] ?? '')); + return qdb_is_stable_key($dt) ? $dt : ''; +} + +/** Human-readable text for glass-scale answers stored as JSON on the parent question row. */ +function qdb_format_glass_answer_json(?string $raw): string +{ + if ($raw === null || trim($raw) === '') { + return ''; + } + $decoded = json_decode($raw, true); + if (!is_array($decoded)) { + return $raw; + } + $parts = []; + foreach ($decoded as $key => $val) { + $val = trim((string)$val); + if ($val === '') { + continue; + } + $parts[] = $key . ': ' . $val; + } + return implode('; ', $parts); +} + +/** + * Map glass symptom string keys to parent questionID for one questionnaire. + * + * @return array symptomKey => parentQuestionID + */ +function qdb_glass_symptom_parent_map(PDO $pdo, string $qnID): array +{ + $stmt = $pdo->prepare( + "SELECT questionID, configJson FROM question WHERE questionnaireID = :qn AND type = 'glass_scale_question'" + ); + $stmt->execute([':qn' => $qnID]); + $map = []; + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) { + $cfg = json_decode($row['configJson'] ?? '{}', true) ?: []; + foreach ($cfg['symptoms'] ?? [] as $symptomKey) { + $sk = trim((string)$symptomKey); + if ($sk !== '') { + $map[$sk] = $row['questionID']; + } + } + } + return $map; +} + +/** Merge symptom => label selections into JSON for one glass-scale parent question. */ +function qdb_merge_glass_symptom_json(?string $existingJson, array $symptomLabels): string +{ + $data = []; + if ($existingJson !== null && trim($existingJson) !== '') { + $decoded = json_decode($existingJson, true); + if (is_array($decoded)) { + $data = $decoded; + } + } + foreach ($symptomLabels as $key => $label) { + $k = trim((string)$key); + $v = trim((string)$label); + if ($k !== '' && $v !== '') { + $data[$k] = $v; + } + } + return json_encode($data, JSON_UNESCAPED_UNICODE); +} + +/** Single symptom value from glass-scale JSON stored on the parent question row. */ +function qdb_glass_symptom_answer_value(?string $json, string $symptomKey): string +{ + $symptomKey = trim($symptomKey); + if ($symptomKey === '' || $json === null || trim($json) === '') { + return ''; + } + $decoded = json_decode($json, true); + if (!is_array($decoded)) { + return ''; + } + return trim((string)($decoded[$symptomKey] ?? '')); +} + +/** + * Flat columns for results / CSV: one column per glass-scale symptom. + * + * @return list + */ +function qdb_results_export_columns(array $questions, string $qnID): array +{ + $columns = []; + foreach ($questions as $q) { + $cfg = json_decode($q['configJson'] ?? '{}', true) ?: []; + $type = $q['type'] ?? ''; + if ($type === 'glass_scale_question') { + $symptoms = $cfg['symptoms'] ?? []; + if (is_array($symptoms) && $symptoms !== []) { + foreach ($symptoms as $symptomKey) { + $sk = trim((string)$symptomKey); + if ($sk === '') { + continue; + } + $columns[] = [ + 'header' => $sk, + 'questionID' => $q['questionID'], + 'symptomKey' => $sk, + 'kind' => 'glass_symptom', + 'question' => $q, + ]; + } + continue; + } + } + $columns[] = [ + 'header' => qdb_question_column_label($cfg, $q['defaultText'], $q['questionID'], $qnID), + 'questionID' => $q['questionID'], + 'symptomKey' => null, + 'kind' => 'question', + 'question' => $q, + ]; + } + return $columns; +} + +/** Cell value for one export/results column (including glass symptom columns). */ +function qdb_results_column_cell_value(array $column, ?array $answerRow, array $optionTextMap): string +{ + if (($column['kind'] ?? '') === 'glass_symptom') { + return qdb_glass_symptom_answer_value( + $answerRow['freeTextValue'] ?? null, + (string)($column['symptomKey'] ?? '') + ); + } + return qdb_format_client_answer_display($column['question'], $answerRow, $optionTextMap); +} + +/** Format one client_answer row for results table / CSV export. */ +function qdb_format_client_answer_display(array $question, ?array $answerRow, array $optionTextMap): string +{ + if (!$answerRow) { + return ''; + } + if (($question['type'] ?? '') === 'glass_scale_question') { + return qdb_format_glass_answer_json($answerRow['freeTextValue'] ?? null); + } + $aoid = $answerRow['answerOptionID'] ?? null; + if ($aoid && isset($optionTextMap[$aoid])) { + return (string)$optionTextMap[$aoid]; + } + if (!empty($answerRow['freeTextValue'])) { + return (string)$answerRow['freeTextValue']; + } + if (isset($answerRow['numericValue']) && $answerRow['numericValue'] !== null && $answerRow['numericValue'] !== '') { + return (string)$answerRow['numericValue']; + } + return ''; +} + +function qdb_note_before_key(string $questionKey): string { + return $questionKey . '_note_before'; +} + +function qdb_note_after_key(string $questionKey): string { + return $questionKey . '_note_after'; +} + +/** Strip deprecated fields and persist questionKey + notes in config. */ +function qdb_normalize_question_config(array $config, string $questionKey, ?string $noteBefore = null, ?string $noteAfter = null): array { + unset($config['multiline']); + $config['questionKey'] = $questionKey; + if ($noteBefore !== null) { + $nb = trim($noteBefore); + if ($nb !== '') { + $config['noteBefore'] = $nb; + } else { + unset($config['noteBefore']); + } + } + if ($noteAfter !== null) { + $na = trim($noteAfter); + if ($na !== '') { + $config['noteAfter'] = $na; + } else { + unset($config['noteAfter']); + } + } + return $config; +} + +function qdb_parse_config_json($configJson): array { + if (is_array($configJson)) { + $config = $configJson; + } else { + $config = json_decode($configJson ?: '{}', true) ?: []; + } + unset($config['multiline']); + return $config; +} + +/** + * @return list + */ +function qdb_parse_glass_symptoms_body(array $body, array $config): array { + if (isset($body['glassSymptoms']) && is_array($body['glassSymptoms'])) { + $rows = []; + foreach ($body['glassSymptoms'] as $row) { + if (!is_array($row)) { + continue; + } + $key = trim((string)($row['key'] ?? '')); + if ($key === '') { + continue; + } + $key = qdb_validate_stable_key($key, 'Symptom key'); + $rows[] = [ + 'key' => $key, + 'labelDe' => trim((string)($row['labelDe'] ?? '')), + ]; + } + return $rows; + } + $out = []; + foreach ($config['symptoms'] ?? [] as $symptomKey) { + $key = trim((string)$symptomKey); + if ($key !== '') { + $out[] = ['key' => $key, 'labelDe' => '']; + } + } + return $out; +} + +/** Apply ordered symptom keys from editor rows into question config. */ +function qdb_apply_glass_symptoms_config(array $config, array $glassSymptoms): array { + $keys = []; + foreach ($glassSymptoms as $row) { + $keys[] = $row['key']; + } + if ($keys) { + $config['symptoms'] = $keys; + } else { + unset($config['symptoms']); + } + return $config; +} + +/** Upsert German labels for glass-scale symptom string keys. */ +function qdb_sync_glass_symptom_strings(PDO $pdo, array $glassSymptoms): void { + foreach ($glassSymptoms as $row) { + $key = $row['key'] ?? ''; + $label = trim((string)($row['labelDe'] ?? '')); + if ($key === '' || $label === '') { + continue; + } + qdb_put_translation($pdo, 'string', $key, QDB_SOURCE_LANGUAGE, $label); + } +} + +/** + * Symptom keys with German labels for editor / API consumers. + * + * @return list + */ +function qdb_glass_symptoms_with_labels(PDO $pdo, array $config): array { + $rows = []; + foreach ($config['symptoms'] ?? [] as $symptomKey) { + $key = trim((string)$symptomKey); + if ($key === '') { + continue; + } + $label = qdb_string_german_label($pdo, $key); + if ($label === $key) { + $label = ''; + } + $rows[] = ['key' => $key, 'labelDe' => $label]; + } + return $rows; +} + +/** Sync note German text into string_translation rows for the app. */ +function qdb_sync_question_note_strings(PDO $pdo, string $questionKey, array $config): void { + if ($questionKey === '') { + return; + } + foreach ([ + 'noteBefore' => qdb_note_before_key($questionKey), + 'noteAfter' => qdb_note_after_key($questionKey), + ] as $field => $stringKey) { + $text = trim((string)($config[$field] ?? '')); + if ($text !== '') { + qdb_put_translation($pdo, 'string', $stringKey, QDB_SOURCE_LANGUAGE, $text); + } + } +} + +function qdb_assert_unique_question_key(PDO $pdo, string $qnID, string $questionKey, ?string $excludeQuestionID = null): void { + $stmt = $pdo->prepare('SELECT questionID, configJson, defaultText FROM question WHERE questionnaireID = :qn'); + $stmt->execute([':qn' => $qnID]); + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) { + if ($excludeQuestionID !== null && $row['questionID'] === $excludeQuestionID) { + continue; + } + $cfg = json_decode($row['configJson'] ?: '{}', true) ?: []; + $existing = qdb_question_key($cfg, $row['defaultText']); + if ($existing !== '' && $existing === $questionKey) { + json_error('CONFLICT', "Question key \"$questionKey\" already exists in this questionnaire", 409); + } + } +} + +function qdb_assert_unique_option_key(PDO $pdo, string $questionID, string $optionKey, ?string $excludeOptionID = null): void { + $stmt = $pdo->prepare('SELECT answerOptionID, defaultText FROM answer_option WHERE questionID = :qid'); + $stmt->execute([':qid' => $questionID]); + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) { + if ($excludeOptionID !== null && $row['answerOptionID'] === $excludeOptionID) { + continue; + } + if ($row['defaultText'] === $optionKey) { + json_error('CONFLICT', "Option key \"$optionKey\" already exists on this question", 409); + } + } +} + +/** German label for an option (translation de, else legacy defaultText when not a stable key). */ +function qdb_option_german_label(PDO $pdo, string $answerOptionID, string $storedDefaultText): string { + $tr = qdb_fetch_translation_map($pdo, 'answer_option', $answerOptionID); + $de = trim($tr[QDB_SOURCE_LANGUAGE] ?? ''); + if ($de !== '') { + return $de; + } + if (!qdb_is_stable_key($storedDefaultText)) { + return $storedDefaultText; + } + return ''; +} + +/** German label for a string catalog key (symptoms, glass levels, etc.). */ +function qdb_string_german_label(PDO $pdo, string $stringKey, array &$cache = []): string { + $stringKey = trim($stringKey); + if ($stringKey === '') { + return ''; + } + if (isset($cache[$stringKey])) { + return $cache[$stringKey]; + } + $tr = qdb_fetch_translation_map($pdo, 'string', $stringKey); + $de = trim($tr[QDB_SOURCE_LANGUAGE] ?? ''); + $label = $de !== '' ? $de : $stringKey; + $cache[$stringKey] = $label; + return $label; +} + +/** German question text for UI display. */ +function qdb_question_german_label(PDO $pdo, array $questionRow): string { + $tr = qdb_fetch_translation_map($pdo, 'question', $questionRow['questionID']); + $de = trim($tr[QDB_SOURCE_LANGUAGE] ?? ''); + if ($de !== '') { + return $de; + } + $defaultText = trim($questionRow['defaultText'] ?? ''); + if ($defaultText !== '' && !qdb_is_stable_key($defaultText)) { + return $defaultText; + } + return qdb_question_local_id($questionRow['questionID']); +} + +/** + * Result columns + option map using German labels (client detail, not CSV export). + * + * @return array{questions: array, resultColumns: array, optionTextMap: array, stringLabelCache: array} + */ +function qdb_display_questionnaire_context(PDO $pdo, string $questionnaireID): array { + $qStmt = $pdo->prepare( + 'SELECT questionID, defaultText, type, orderIndex, configJson FROM question WHERE questionnaireID = :id ORDER BY orderIndex' + ); + $qStmt->execute([':id' => $questionnaireID]); + $questions = $qStmt->fetchAll(PDO::FETCH_ASSOC); + $stringLabelCache = []; + $resultColumns = []; + + foreach ($questions as $q) { + $cfg = json_decode($q['configJson'] ?? '{}', true) ?: []; + $type = $q['type'] ?? ''; + if ($type === 'glass_scale_question') { + $symptoms = $cfg['symptoms'] ?? []; + if (is_array($symptoms) && $symptoms !== []) { + foreach ($symptoms as $symptomKey) { + $sk = trim((string)$symptomKey); + if ($sk === '') { + continue; + } + $resultColumns[] = [ + 'header' => qdb_string_german_label($pdo, $sk, $stringLabelCache), + 'questionID' => $q['questionID'], + 'symptomKey' => $sk, + 'kind' => 'glass_symptom', + 'question' => $q, + ]; + } + continue; + } + } + $resultColumns[] = [ + 'header' => qdb_question_german_label($pdo, $q), + 'questionID' => $q['questionID'], + 'symptomKey' => null, + 'kind' => 'question', + 'question' => $q, + ]; + } + + $optionTextMap = []; + foreach ($questions as $q) { + $aoStmt = $pdo->prepare('SELECT answerOptionID, defaultText FROM answer_option WHERE questionID = :qid'); + $aoStmt->execute([':qid' => $q['questionID']]); + foreach ($aoStmt->fetchAll(PDO::FETCH_ASSOC) as $ao) { + $optionTextMap[$ao['answerOptionID']] = qdb_option_german_label( + $pdo, + $ao['answerOptionID'], + $ao['defaultText'] + ); + } + } + + return [ + 'questions' => $questions, + 'resultColumns' => $resultColumns, + 'optionTextMap' => $optionTextMap, + 'stringLabelCache' => $stringLabelCache, + ]; +} + +/** Format one answer cell for UI (German labels). */ +function qdb_display_column_cell_value( + PDO $pdo, + array $column, + ?array $answerRow, + array $optionTextMap, + array &$stringLabelCache +): string { + if (($column['kind'] ?? '') === 'glass_symptom') { + $raw = qdb_glass_symptom_answer_value( + $answerRow['freeTextValue'] ?? null, + (string)($column['symptomKey'] ?? '') + ); + if ($raw === '') { + return ''; + } + return qdb_string_german_label($pdo, $raw, $stringLabelCache); + } + if (($column['question']['type'] ?? '') === 'glass_scale_question') { + return qdb_format_glass_answer_json_display($pdo, $answerRow['freeTextValue'] ?? null, $stringLabelCache); + } + return qdb_format_client_answer_display($column['question'], $answerRow, $optionTextMap); +} + +/** Glass-scale JSON with German symptom keys and level labels. */ +function qdb_format_glass_answer_json_display(PDO $pdo, ?string $raw, array &$stringLabelCache): string +{ + if ($raw === null || trim($raw) === '') { + return ''; + } + $decoded = json_decode($raw, true); + if (!is_array($decoded)) { + return $raw; + } + $parts = []; + foreach ($decoded as $key => $val) { + $val = trim((string)$val); + if ($val === '') { + continue; + } + $symLabel = qdb_string_german_label($pdo, (string)$key, $stringLabelCache); + $levelLabel = qdb_string_german_label($pdo, $val, $stringLabelCache); + $parts[] = $symLabel . ': ' . $levelLabel; + } + return implode('; ', $parts); +} + +/** + * Build translation entry lists for one questionnaire. + * Returns ['stringEntries' => [...], 'contentEntries' => [...]] (questions + options interleaved). + */ +function qdb_translation_entry_lists(PDO $pdo, string $qnID): array { + $qStmt = $pdo->prepare(" + SELECT q.questionID, q.defaultText, q.type, q.configJson, q.orderIndex + FROM question q WHERE q.questionnaireID = :id ORDER BY q.orderIndex + "); + $qStmt->execute([':id' => $qnID]); + $dbQuestions = $qStmt->fetchAll(PDO::FETCH_ASSOC); + + $stringKeys = []; + $stringEntries = []; + $contentEntries = []; + $addedSymptomKeys = []; + $appKeySet = qdb_app_string_key_set(); + + foreach ($dbQuestions as $dbQ) { + $qOrder = (int)($dbQ['orderIndex'] ?? 0); + $qLocal = qdb_question_local_id($dbQ['questionID']); + $config = json_decode($dbQ['configJson'] ?: '{}', true) ?: []; + $qKey = qdb_question_key($config, $dbQ['defaultText']); + $germanQ = trim($dbQ['defaultText']); + if ($qKey !== '' && qdb_is_stable_key($germanQ) && $germanQ === $qKey) { + $germanQ = ''; + } + $contentEntries[] = [ + 'key' => $qKey !== '' ? $qKey : $dbQ['defaultText'], + 'displayKey' => $qKey !== '' ? $qKey : (preg_match('/^[a-f0-9]{32}$/i', $qLocal) ? ('#' . ($qOrder + 1)) : $qLocal), + 'germanText' => $germanQ, + 'type' => 'question', + 'entityId' => $dbQ['questionID'], + 'sortOrder' => $qOrder * 1000, + ]; + + $aoStmt = $pdo->prepare(" + SELECT answerOptionID, defaultText, orderIndex + FROM answer_option WHERE questionID = :qid ORDER BY orderIndex + "); + $aoStmt->execute([':qid' => $dbQ['questionID']]); + foreach ($aoStmt->fetchAll(PDO::FETCH_ASSOC) as $ao) { + $optKey = qdb_option_key($ao); + $optLabel = $optKey !== '' + ? ($qLocal . ' · ' . $optKey) + : (preg_match('/^[a-f0-9]{32}$/i', $qLocal) + ? ('#' . ($qOrder + 1) . ' · option ' . ((int)$ao['orderIndex'] + 1)) + : ($qLocal . ' · option ' . ((int)$ao['orderIndex'] + 1))); + $contentEntries[] = [ + 'key' => $optKey !== '' ? $optKey : $ao['defaultText'], + 'displayKey' => $optLabel, + 'germanText' => qdb_option_german_label($pdo, $ao['answerOptionID'], $ao['defaultText']), + 'type' => 'answer_option', + 'entityId' => $ao['answerOptionID'], + 'sortOrder' => $qOrder * 1000 + 10 + (int)($ao['orderIndex'] ?? 0), + ]; + } + + foreach (['textKey', 'textKey1', 'textKey2', 'hint', 'hint1', 'hint2', 'otherOptionKey'] as $field) { + if (!empty($config[$field])) { + $stringKeys[$config[$field]] = true; + } + } + if (($dbQ['type'] ?? '') === 'glass_scale_question' + && !empty($config['symptoms']) && is_array($config['symptoms'])) { + $symIdx = 0; + foreach ($config['symptoms'] as $s) { + $sk = trim((string)$s); + if ($sk === '' || isset($appKeySet[$sk]) || isset($addedSymptomKeys[$sk])) { + continue; + } + $addedSymptomKeys[$sk] = true; + $parentLabel = $qKey !== '' ? $qKey : $qLocal; + $stringEntries[] = [ + 'key' => $sk, + 'displayKey' => $qLocal . ' · ' . $parentLabel . ' · ' . $sk, + 'type' => 'string', + 'entityId' => $sk, + 'sortOrder' => $qOrder * 1000 + 5 + $symIdx, + ]; + $symIdx++; + } + } + if ($qKey !== '') { + if (!empty($config['noteBefore'])) { + $stringKeys[qdb_note_before_key($qKey)] = true; + } + if (!empty($config['noteAfter'])) { + $stringKeys[qdb_note_after_key($qKey)] = true; + } + } + } + + $stringOrder = 0; + $skList = array_keys($stringKeys); + sort($skList, SORT_STRING); + foreach ($skList as $sk) { + if (isset($appKeySet[$sk])) { + continue; + } + $stringEntries[] = [ + 'key' => $sk, + 'displayKey' => $sk, + 'type' => 'string', + 'entityId' => $sk, + 'sortOrder' => $stringOrder++, + ]; + } + + return ['stringEntries' => $stringEntries, 'contentEntries' => $contentEntries]; +} + +/** Attach translation texts and germanText to entry rows. */ +function qdb_attach_translation_texts(array &$entries, array $translations): void { + foreach ($entries as &$e) { + $tr = $translations[$e['entityId']] ?? []; + $e['translations'] = (object)$tr; + $de = trim($tr[QDB_SOURCE_LANGUAGE] ?? ''); + if ($de !== '') { + $e['germanText'] = $de; + } elseif (!empty($e['germanDefault'])) { + $e['germanText'] = $e['germanDefault']; + } else { + $e['germanText'] = $e['key']; + } + unset($e['germanDefault']); + } + unset($e); +} + +/** Load all translation rows for a flat entry list. */ +function qdb_load_translations_for_entries(PDO $pdo, array $entries): array { + $translations = []; + + $qIds = array_values(array_filter(array_map( + fn($e) => $e['type'] === 'question' ? $e['entityId'] : null, + $entries + ))); + if ($qIds) { + $ph = implode(',', array_fill(0, count($qIds), '?')); + $stmt = $pdo->prepare("SELECT questionID AS entityId, languageCode, text FROM question_translation WHERE questionID IN ($ph)"); + $stmt->execute($qIds); + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) { + $translations[$r['entityId']][$r['languageCode']] = $r['text']; + } + } + + $aoIds = array_values(array_filter(array_map( + fn($e) => $e['type'] === 'answer_option' ? $e['entityId'] : null, + $entries + ))); + if ($aoIds) { + $ph = implode(',', array_fill(0, count($aoIds), '?')); + $stmt = $pdo->prepare("SELECT answerOptionID AS entityId, languageCode, text FROM answer_option_translation WHERE answerOptionID IN ($ph)"); + $stmt->execute($aoIds); + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) { + $translations[$r['entityId']][$r['languageCode']] = $r['text']; + } + } + + $sKeys = array_values(array_filter(array_map( + fn($e) => ($e['type'] === 'string' || $e['type'] === 'app_string') ? $e['entityId'] : null, + $entries + ))); + if ($sKeys) { + $ph = implode(',', array_fill(0, count($sKeys), '?')); + $stmt = $pdo->prepare("SELECT stringKey AS entityId, languageCode, text FROM string_translation WHERE stringKey IN ($ph)"); + $stmt->execute($sKeys); + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) { + $translations[$r['entityId']][$r['languageCode']] = $r['text']; + } + } + + return $translations; +} + +/** + * Flat language -> stringKey -> text map for global app UI strings only. + * @return array> + */ +function qdb_build_app_translations_map(PDO $pdo): array { + $entries = qdb_app_ui_string_entries_merged($pdo); + $translations = qdb_load_translations_for_entries($pdo, $entries); + $defaults = qdb_app_string_catalog()['germanDefaults'] ?? []; + $result = []; + foreach ($entries as $e) { + $key = $e['key']; + foreach ($translations[$e['entityId']] ?? [] as $lang => $text) { + $result[$lang][$key] = $text; + } + if (!isset($result[QDB_SOURCE_LANGUAGE][$key]) && !empty($defaults[$key])) { + $result[QDB_SOURCE_LANGUAGE][$key] = $defaults[$key]; + } + } + return $result; +} + +/** + * Flat language -> stringKey -> text map for one questionnaire (questions, options, config strings). + * Excludes keys from the global app UI catalog. + * @return array> + */ +function qdb_build_questionnaire_translations_map(PDO $pdo, string $qnID): array { + $lists = qdb_translation_entry_lists($pdo, $qnID); + $entries = array_merge($lists['stringEntries'], $lists['contentEntries']); + if (!$entries) { + return []; + } + $translations = qdb_load_translations_for_entries($pdo, $entries); + $result = []; + foreach ($entries as $e) { + $key = $e['key']; + foreach ($translations[$e['entityId']] ?? [] as $lang => $text) { + $result[$lang][$key] = $text; + } + } + return $result; +} + +/** @return array */ +function qdb_translation_rows_to_map(array $rows): array { + $map = []; + foreach ($rows as $row) { + if (is_array($row) && isset($row['languageCode'])) { + $map[$row['languageCode']] = $row['text'] ?? ''; + } + } + return $map; +} + +/** @return array */ +function qdb_fetch_translation_map(PDO $pdo, string $type, string $entityId): array { + if ($type === 'question') { + $stmt = $pdo->prepare('SELECT languageCode, text FROM question_translation WHERE questionID = :id'); + } elseif ($type === 'answer_option') { + $stmt = $pdo->prepare('SELECT languageCode, text FROM answer_option_translation WHERE answerOptionID = :id'); + } else { + $stmt = $pdo->prepare('SELECT languageCode, text FROM string_translation WHERE stringKey = :id'); + } + $stmt->execute([':id' => $entityId]); + return qdb_translation_rows_to_map($stmt->fetchAll(PDO::FETCH_ASSOC)); +} + +function qdb_apply_translation_map(PDO $pdo, string $type, string $entityId, array $map): void { + foreach ($map as $lang => $text) { + if (!is_string($lang) || $lang === '') { + continue; + } + qdb_put_translation($pdo, $type, $entityId, $lang, (string)$text); + } +} + +function qdb_question_local_id(string $questionID, ?string $questionnaireID = null): string { + $pos = strrpos($questionID, '__'); + return $pos !== false ? substr($questionID, $pos + 2) : $questionID; +} + +/** Column / export header: stable question key, else local id (q1, q2, …). */ +function qdb_question_column_label(array $config, string $defaultText, string $questionID, ?string $questionnaireID = null): string { + $key = qdb_question_key($config, $defaultText); + return $key !== '' ? $key : qdb_question_local_id($questionID, $questionnaireID); +} + +function qdb_make_question_id(string $questionnaireID, string $localId): string { + return $questionnaireID . '__' . $localId; +} + +/** Next short id for a new question (q1, q2, …) within one questionnaire. */ +function qdb_allocate_question_local_id(PDO $pdo, string $questionnaireID): string { + $stmt = $pdo->prepare('SELECT questionID FROM question WHERE questionnaireID = :id'); + $stmt->execute([':id' => $questionnaireID]); + $max = 0; + foreach ($stmt->fetchAll(PDO::FETCH_COLUMN) as $questionID) { + $local = qdb_question_local_id($questionID); + if (preg_match('/^q(\d+)$/i', $local, $m)) { + $max = max($max, (int)$m[1]); + } + } + return 'q' . ($max + 1); +} + +/** Human-readable Key column label (not the app translation lookup key). */ +function qdb_translation_row_label(array $e): string { + if (!empty($e['displayKey']) && !preg_match('/^[a-f0-9]{32}$/i', $e['displayKey'])) { + return $e['displayKey']; + } + $type = $e['type'] ?? ''; + $key = $e['key'] ?? ''; + if ($type === 'question' || $type === 'answer_option') { + if ($key !== '') { + $short = mb_strlen($key) > 56 ? mb_substr($key, 0, 56) . '…' : $key; + return $short; + } + } + $id = $e['entityId'] ?? ''; + $pos = strrpos($id, '__'); + if ($pos !== false) { + return substr($id, $pos + 2); + } + return $key !== '' ? $key : $id; +} + +/** Export one questionnaire with structure + all translations (portable JSON). */ +function qdb_export_questionnaire_bundle(PDO $pdo, string $qnID): ?array { + require_once __DIR__ . '/lib/scoring.php'; + require_once __DIR__ . '/lib/questionnaire_structure.php'; + $qn = $pdo->prepare('SELECT * FROM questionnaire WHERE questionnaireID = :id'); + $qn->execute([':id' => $qnID]); + $qnRow = $qn->fetch(PDO::FETCH_ASSOC); + if (!$qnRow) { + return null; + } + + $qStmt = $pdo->prepare( + 'SELECT questionID, defaultText, type, orderIndex, isRequired, configJson + FROM question WHERE questionnaireID = :id AND ' . qdb_active_questions_clause('question') . ' + ORDER BY orderIndex' + ); + $qStmt->execute([':id' => $qnID]); + $questionsOut = []; + + foreach ($qStmt->fetchAll(PDO::FETCH_ASSOC) as $dbQ) { + $localId = qdb_question_local_id($dbQ['questionID'], $qnID); + $config = json_decode($dbQ['configJson'] ?: '{}', true) ?: []; + + $optionsOut = []; + $aoStmt = $pdo->prepare( + 'SELECT answerOptionID, defaultText, points, orderIndex, nextQuestionId + FROM answer_option WHERE questionID = :qid AND ' . qdb_active_options_clause('answer_option') . ' + ORDER BY orderIndex' + ); + $aoStmt->execute([':qid' => $dbQ['questionID']]); + $qKey = qdb_question_key($config, $dbQ['defaultText']); + foreach ($aoStmt->fetchAll(PDO::FETCH_ASSOC) as $ao) { + $optKey = qdb_option_key($ao); + $optTr = qdb_fetch_translation_map($pdo, 'answer_option', $ao['answerOptionID']); + $optGerman = trim($optTr[QDB_SOURCE_LANGUAGE] ?? ''); + if ($optGerman === '' && !qdb_is_stable_key($ao['defaultText'])) { + $optGerman = $ao['defaultText']; + } + $optionsOut[] = [ + 'optionKey' => $optKey !== '' ? $optKey : $ao['defaultText'], + 'defaultText' => $optGerman, + 'points' => (int)$ao['points'], + 'orderIndex' => (int)$ao['orderIndex'], + 'nextQuestionId' => $ao['nextQuestionId'] ?? '', + 'translations' => $optTr, + ]; + } + + $questionsOut[] = [ + 'localId' => $localId, + 'questionKey' => $qKey, + 'defaultText' => $dbQ['defaultText'], + 'type' => $dbQ['type'], + 'orderIndex' => (int)$dbQ['orderIndex'], + 'isRequired' => (int)$dbQ['isRequired'], + 'config' => $config, + 'answerOptions' => $optionsOut, + 'scoreRules' => qdb_get_score_rules_for_question($pdo, $dbQ['questionID']), + 'translations' => qdb_fetch_translation_map($pdo, 'question', $dbQ['questionID']), + ]; + } + + $lists = qdb_translation_entry_lists($pdo, $qnID); + $stringTranslations = []; + if (!empty($lists['stringEntries'])) { + $tr = qdb_load_translations_for_entries($pdo, $lists['stringEntries']); + foreach ($lists['stringEntries'] as $e) { + $stringTranslations[] = [ + 'stringKey' => $e['key'], + 'translations' => $tr[$e['entityId']] ?? [], + ]; + } + } + + return [ + 'questionnaire' => [ + 'questionnaireID' => $qnRow['questionnaireID'], + 'name' => $qnRow['name'], + 'version' => $qnRow['version'], + 'state' => $qnRow['state'], + 'orderIndex' => (int)$qnRow['orderIndex'], + 'showPoints' => (int)$qnRow['showPoints'], + 'conditionJson' => $qnRow['conditionJson'] ?: '{}', + 'structureRevision' => qdb_questionnaire_structure_revision($pdo, $qnID), + ], + 'questions' => $questionsOut, + 'stringTranslations' => $stringTranslations, + 'translationsFlat' => qdb_build_questionnaire_translations_map($pdo, $qnID), + ]; +} + +/** Export every questionnaire as a portable bundle. */ +function qdb_export_all_questionnaires_bundle(PDO $pdo): array { + $ids = $pdo->query('SELECT questionnaireID FROM questionnaire ORDER BY orderIndex, name') + ->fetchAll(PDO::FETCH_COLUMN); + $items = []; + foreach ($ids as $id) { + $item = qdb_export_questionnaire_bundle($pdo, $id); + if ($item !== null) { + $items[] = $item; + } + } + return [ + 'exportVersion' => 2, + 'exportedAt' => time(), + 'sourceLanguage' => QDB_SOURCE_LANGUAGE, + 'questionnaireCount' => count($items), + 'questionnaires' => $items, + 'scoringProfiles' => qdb_export_scoring_profiles_bundle($pdo), + ]; +} + +/** Export all scoring profiles for bundle portability. */ +function qdb_export_scoring_profiles_bundle(PDO $pdo): array { + require_once __DIR__ . '/lib/scoring.php'; + return qdb_list_scoring_profiles($pdo); +} + +/** Remove questionnaire and related rows (full replace import; not per-question retire). */ +function qdb_delete_questionnaire_cascade(PDO $pdo, string $qnID): void { + $qIds = $pdo->prepare('SELECT questionID FROM question WHERE questionnaireID = :id'); + $qIds->execute([':id' => $qnID]); + $questionIDs = $qIds->fetchAll(PDO::FETCH_COLUMN); + + foreach ($questionIDs as $qid) { + $aoIds = $pdo->prepare('SELECT answerOptionID FROM answer_option WHERE questionID = :qid'); + $aoIds->execute([':qid' => $qid]); + foreach ($aoIds->fetchAll(PDO::FETCH_COLUMN) as $aoid) { + $pdo->prepare('DELETE FROM answer_option_translation WHERE answerOptionID = :id') + ->execute([':id' => $aoid]); + } + $pdo->prepare('DELETE FROM answer_option WHERE questionID = :qid')->execute([':qid' => $qid]); + $pdo->prepare('DELETE FROM question_translation WHERE questionID = :qid')->execute([':qid' => $qid]); + if (qdb_table_exists($pdo, 'client_answer_submission')) { + $pdo->prepare('DELETE FROM client_answer_submission WHERE questionID = :qid') + ->execute([':qid' => $qid]); + } + $pdo->prepare('DELETE FROM client_answer WHERE questionID = :qid')->execute([':qid' => $qid]); + } + if (qdb_table_exists($pdo, 'questionnaire_structure_snapshot')) { + $pdo->prepare('DELETE FROM questionnaire_structure_snapshot WHERE questionnaireID = :id') + ->execute([':id' => $qnID]); + } + if (qdb_table_exists($pdo, 'questionnaire_submission')) { + $pdo->prepare('DELETE FROM questionnaire_submission WHERE questionnaireID = :id') + ->execute([':id' => $qnID]); + } + $pdo->prepare('DELETE FROM question WHERE questionnaireID = :id')->execute([':id' => $qnID]); + $pdo->prepare('DELETE FROM completed_questionnaire WHERE questionnaireID = :id')->execute([':id' => $qnID]); + $pdo->prepare('DELETE FROM questionnaire WHERE questionnaireID = :id')->execute([':id' => $qnID]); +} + +/** + * Import one questionnaire from a bundle item. + * @return array{questionnaireID: string, created: bool, replaced: bool} + */ +function qdb_import_questionnaire_bundle(PDO $pdo, array $item, bool $replaceIfExists = false): array { + $meta = $item['questionnaire'] ?? []; + if (empty($meta['name'])) { + json_error('MISSING_FIELDS', 'questionnaire.name is required', 400); + } + + $wantedId = trim($meta['questionnaireID'] ?? ''); + $chk = $wantedId !== '' ? $pdo->prepare('SELECT 1 FROM questionnaire WHERE questionnaireID = :id') : null; + if ($chk) { + $chk->execute([':id' => $wantedId]); + } + $exists = $chk && (bool)$chk->fetch(); + + $replaced = false; + if ($exists && $replaceIfExists) { + qdb_delete_questionnaire_cascade($pdo, $wantedId); + $replaced = true; + $qnID = $wantedId; + } elseif ($exists) { + $qnID = bin2hex(random_bytes(16)); + } elseif ($wantedId !== '') { + $qnID = $wantedId; + } else { + $qnID = bin2hex(random_bytes(16)); + } + + $order = (int)($meta['orderIndex'] ?? 0); + if ($order === 0) { + $order = (int)$pdo->query('SELECT COALESCE(MAX(orderIndex),0)+1 FROM questionnaire')->fetchColumn(); + } + + $condJson = $meta['conditionJson'] ?? '{}'; + if (is_array($condJson)) { + $condJson = json_encode($condJson, JSON_UNESCAPED_UNICODE); + } + + $catKey = trim($meta['categoryKey'] ?? ''); + $pdo->prepare("INSERT INTO questionnaire (questionnaireID, name, version, state, orderIndex, showPoints, conditionJson, categoryKey) + VALUES (:id, :n, :v, :s, :o, :sp, :cj, :ck)") + ->execute([ + ':id' => $qnID, + ':n' => trim($meta['name']), + ':v' => trim($meta['version'] ?? ''), + ':s' => trim($meta['state'] ?? 'draft'), + ':o' => $order, + ':sp' => (int)($meta['showPoints'] ?? 0), + ':cj' => $condJson ?: '{}', + ':ck' => $catKey, + ]); + + foreach ($item['questions'] ?? [] as $q) { + $localId = trim($q['localId'] ?? ''); + if ($localId === '') { + $localId = 'q_' . bin2hex(random_bytes(4)); + } + $questionID = $qnID . '__' . $localId; + + $config = qdb_parse_config_json($q['config'] ?? $q['configJson'] ?? []); + $questionKey = trim((string)($q['questionKey'] ?? $config['questionKey'] ?? '')); + $text = trim($q['defaultText'] ?? ''); + $qTr = $q['translations'] ?? []; + if (is_array($qTr) && isset($qTr[0]['languageCode'])) { + $qTr = qdb_translation_rows_to_map($qTr); + } + if ($questionKey === '' && qdb_is_stable_key($text) && trim($qTr[QDB_SOURCE_LANGUAGE] ?? '') !== '' && $qTr[QDB_SOURCE_LANGUAGE] !== $text) { + $questionKey = $text; + $text = trim($qTr[QDB_SOURCE_LANGUAGE]); + } + if ($questionKey === '') { + json_error( + 'MISSING_FIELDS', + "Question key is required for {$qnID} / {$localId} (stable key [a-z][a-z0-9_]*)", + 400 + ); + } + $questionKey = qdb_validate_stable_key($questionKey, 'Question key'); + // Duplicate question keys in one questionnaire are allowed (same prompt in different branches). + + qdb_require_non_empty_german($text, 'Question German text'); + $noteBefore = $config['noteBefore'] ?? null; + $noteAfter = $config['noteAfter'] ?? null; + $config = qdb_normalize_question_config($config, $questionKey, $noteBefore, $noteAfter); + $configJson = json_encode($config, JSON_UNESCAPED_UNICODE); + + $pdo->prepare("INSERT INTO question (questionID, questionnaireID, defaultText, type, orderIndex, isRequired, configJson) + VALUES (:id, :qid, :t, :ty, :o, :r, :cj)") + ->execute([ + ':id' => $questionID, + ':qid' => $qnID, + ':t' => $text, + ':ty' => trim($q['type'] ?? ''), + ':o' => (int)($q['orderIndex'] ?? 0), + ':r' => (int)($q['isRequired'] ?? 0), + ':cj' => $configJson, + ]); + + qdb_upsert_source_translation($pdo, 'question', $questionID, $text); + qdb_apply_translation_map($pdo, 'question', $questionID, is_array($qTr) ? $qTr : []); + qdb_sync_question_note_strings($pdo, $questionKey, $config); + + if (!empty($q['scoreRules']) && is_array($q['scoreRules'])) { + require_once __DIR__ . '/lib/scoring.php'; + qdb_sync_question_score_rules($pdo, $questionID, $q['scoreRules']); + } + + foreach ($q['answerOptions'] ?? [] as $opt) { + $optKey = trim((string)($opt['optionKey'] ?? '')); + $optGerman = trim($opt['defaultText'] ?? ''); + $oTr = $opt['translations'] ?? []; + if (is_array($oTr) && isset($oTr[0]['languageCode'])) { + $oTr = qdb_translation_rows_to_map($oTr); + } + if ($optKey === '' && qdb_is_stable_key($optGerman) && trim($oTr[QDB_SOURCE_LANGUAGE] ?? '') !== '' && $oTr[QDB_SOURCE_LANGUAGE] !== $optGerman) { + $optKey = $optGerman; + $optGerman = trim($oTr[QDB_SOURCE_LANGUAGE]); + } + if ($optKey === '' && qdb_is_stable_key($optGerman)) { + $optKey = $optGerman; + $optGerman = trim($oTr[QDB_SOURCE_LANGUAGE] ?? $optGerman); + } + if ($optKey === '') { + json_error( + 'MISSING_FIELDS', + "Option key is required for {$qnID} / {$localId} option #" . ((int)($opt['orderIndex'] ?? 0) + 1), + 400 + ); + } + $optKey = qdb_validate_stable_key($optKey, 'Option key'); + if ($optGerman === '') { + $optGerman = trim($oTr[QDB_SOURCE_LANGUAGE] ?? ''); + } + qdb_require_non_empty_german($optGerman, 'Answer option German text'); + + $aoId = bin2hex(random_bytes(16)); + $pdo->prepare("INSERT INTO answer_option (answerOptionID, questionID, defaultText, points, orderIndex, nextQuestionId) + VALUES (:id, :qid, :t, :p, :o, :nq)") + ->execute([ + ':id' => $aoId, + ':qid' => $questionID, + ':t' => $optKey, + ':p' => (int)($opt['points'] ?? 0), + ':o' => (int)($opt['orderIndex'] ?? 0), + ':nq' => trim($opt['nextQuestionId'] ?? ''), + ]); + qdb_upsert_source_translation($pdo, 'answer_option', $aoId, $optGerman); + qdb_apply_translation_map($pdo, 'answer_option', $aoId, is_array($oTr) ? $oTr : []); + } + } + + foreach ($item['stringTranslations'] ?? [] as $st) { + $sk = trim($st['stringKey'] ?? ''); + if ($sk === '') { + continue; + } + $map = $st['translations'] ?? []; + if (is_array($map) && isset($map[0]['languageCode'])) { + $map = qdb_translation_rows_to_map($map); + } + qdb_apply_translation_map($pdo, 'string', $sk, is_array($map) ? $map : []); + } + + return [ + 'questionnaireID' => $qnID, + 'created' => true, + 'replaced' => $replaced, + ]; +} + +/** One translation row for a portable translations JSON bundle. */ +function qdb_translation_entry_to_bundle_row(array $e, ?string $questionnaireID = null, ?string $questionnaireName = null): array { + $tr = $e['translations'] ?? []; + if ($tr instanceof stdClass) { + $tr = (array)$tr; + } + if (is_array($tr) && isset($tr[0]) && is_array($tr[0]) && isset($tr[0]['languageCode'])) { + $tr = qdb_translation_rows_to_map($tr); + } + $row = [ + 'type' => $e['type'], + 'entityId' => $e['entityId'], + 'key' => $e['key'] ?? '', + 'translations' => is_array($tr) ? $tr : [], + ]; + if (!empty($e['displayKey'])) { + $row['displayKey'] = $e['displayKey']; + } + if ($questionnaireID !== null && $questionnaireID !== '') { + $row['questionnaireID'] = $questionnaireID; + } + if ($questionnaireName !== null && $questionnaireName !== '') { + $row['questionnaireName'] = $questionnaireName; + } + return $row; +} + +/** @return array */ +function qdb_normalize_bundle_translations($raw): array { + if (!is_array($raw)) { + return []; + } + if (isset($raw[0]) && is_array($raw[0]) && isset($raw[0]['languageCode'])) { + return qdb_translation_rows_to_map($raw); + } + $map = []; + foreach ($raw as $lang => $text) { + if (is_string($lang) && $lang !== '') { + $map[$lang] = (string)$text; + } + } + return $map; +} + +function qdb_translation_entity_exists(PDO $pdo, string $type, string $entityId): bool { + if ($type === 'string' || $type === 'app_string') { + return true; + } + if ($type === 'question') { + $stmt = $pdo->prepare('SELECT 1 FROM question WHERE questionID = :id LIMIT 1'); + } elseif ($type === 'answer_option') { + $stmt = $pdo->prepare('SELECT 1 FROM answer_option WHERE answerOptionID = :id LIMIT 1'); + } else { + return false; + } + $stmt->execute([':id' => $entityId]); + return (bool)$stmt->fetchColumn(); +} + +/** Export all translations (languages + flat entries) as portable JSON. */ +function qdb_export_translations_bundle(PDO $pdo): array { + $languages = $pdo->query('SELECT languageCode, name FROM language ORDER BY languageCode') + ->fetchAll(PDO::FETCH_ASSOC); + if (!array_filter($languages, fn($l) => $l['languageCode'] === QDB_SOURCE_LANGUAGE)) { + array_unshift($languages, ['languageCode' => QDB_SOURCE_LANGUAGE, 'name' => 'German']); + } + qdb_ensure_source_language($pdo); + + $exportEntries = []; + $appEntries = qdb_app_ui_string_entries_for_website($pdo); + $appTranslations = qdb_load_translations_for_entries($pdo, $appEntries); + qdb_attach_translation_texts($appEntries, $appTranslations); + foreach ($appEntries as $e) { + $exportEntries[] = qdb_translation_entry_to_bundle_row($e); + } + + $qnRows = $pdo->query('SELECT questionnaireID, name FROM questionnaire ORDER BY orderIndex, name') + ->fetchAll(PDO::FETCH_ASSOC); + foreach ($qnRows as $qn) { + $lists = qdb_translation_entry_lists($pdo, $qn['questionnaireID']); + $entries = array_merge($lists['stringEntries'], $lists['contentEntries']); + if (!$entries) { + continue; + } + $translations = qdb_load_translations_for_entries($pdo, $entries); + qdb_attach_translation_texts($entries, $translations); + foreach ($entries as $e) { + $exportEntries[] = qdb_translation_entry_to_bundle_row( + $e, + $qn['questionnaireID'], + $qn['name'] + ); + } + } + + return [ + 'exportVersion' => 1, + 'exportedAt' => time(), + 'sourceLanguage' => QDB_SOURCE_LANGUAGE, + 'languageCount' => count($languages), + 'languages' => $languages, + 'entryCount' => count($exportEntries), + 'entries' => $exportEntries, + ]; +} + +/** Import translations bundle (upsert languages and translation rows). */ +function qdb_import_translations_bundle(PDO $pdo, array $bundle): array { + $entries = $bundle['entries'] ?? null; + if (!is_array($entries)) { + json_error('MISSING_FIELDS', 'bundle.entries array is required', 400); + } + + qdb_ensure_source_language($pdo); + foreach ($bundle['languages'] ?? [] as $langRow) { + if (!is_array($langRow)) { + continue; + } + $lc = trim($langRow['languageCode'] ?? ''); + $name = trim($langRow['name'] ?? ''); + if ($lc === '' || $lc === QDB_SOURCE_LANGUAGE) { + continue; + } + $pdo->prepare('INSERT INTO language (languageCode, name) VALUES (:lc, :n) + ON CONFLICT(languageCode) DO UPDATE SET name = excluded.name') + ->execute([':lc' => $lc, ':n' => $name]); + } + + $validTypes = ['question', 'answer_option', 'string', 'app_string']; + $imported = 0; + $skipped = 0; + + foreach ($entries as $entry) { + if (!is_array($entry)) { + $skipped++; + continue; + } + $type = $entry['type'] ?? ''; + $entityId = trim((string)($entry['entityId'] ?? '')); + if (!in_array($type, $validTypes, true) || $entityId === '') { + $skipped++; + continue; + } + if (!qdb_translation_entity_exists($pdo, $type, $entityId)) { + $skipped++; + continue; + } + $map = qdb_normalize_bundle_translations($entry['translations'] ?? []); + if ($map) { + qdb_apply_translation_map($pdo, $type, $entityId, $map); + $imported++; + } else { + $skipped++; + } + } + + return [ + 'imported' => $imported, + 'skipped' => $skipped, + 'total' => count($entries), + ]; +} + +/** Import a full export bundle. */ +function qdb_import_questionnaires_bundle(PDO $pdo, array $bundle, bool $replaceIfExists = false): array { + $items = $bundle['questionnaires'] ?? []; + if (!is_array($items) || !$items) { + json_error('MISSING_FIELDS', 'bundle.questionnaires array is required', 400); + } + foreach ($bundle['appStringTranslations'] ?? [] as $st) { + if (!is_array($st)) { + continue; + } + $sk = trim($st['stringKey'] ?? ''); + if ($sk === '') { + continue; + } + $map = qdb_normalize_bundle_translations($st['translations'] ?? []); + if (!$map) { + continue; + } + if (isset($map[QDB_SOURCE_LANGUAGE])) { + qdb_set_app_string_german_label($pdo, $sk, $map[QDB_SOURCE_LANGUAGE]); + unset($map[QDB_SOURCE_LANGUAGE]); + } + if ($map) { + qdb_apply_translation_map($pdo, 'app_string', $sk, $map); + } + } + $results = []; + foreach ($items as $item) { + $results[] = qdb_import_questionnaire_bundle($pdo, $item, $replaceIfExists); + } + if (!empty($bundle['scoringProfiles']) && is_array($bundle['scoringProfiles'])) { + qdb_import_scoring_profiles_bundle($pdo, $bundle['scoringProfiles'], $replaceIfExists); + } + return [ + 'imported' => count($results), + 'results' => $results, + ]; +} + +/** Import scoring profiles from a questionnaire bundle. */ +function qdb_import_scoring_profiles_bundle(PDO $pdo, array $profiles, bool $replaceIfExists = false): void { + require_once __DIR__ . '/lib/scoring.php'; + foreach ($profiles as $profile) { + if (!is_array($profile)) { + continue; + } + $wantedId = trim($profile['profileID'] ?? ''); + $name = trim($profile['name'] ?? ''); + if ($name === '') { + continue; + } + $exists = false; + if ($wantedId !== '') { + $chk = $pdo->prepare('SELECT 1 FROM scoring_profile WHERE profileID = :id'); + $chk->execute([':id' => $wantedId]); + $exists = (bool)$chk->fetch(); + } + if ($exists && $replaceIfExists) { + $pdo->prepare('DELETE FROM scoring_profile WHERE profileID = :id')->execute([':id' => $wantedId]); + $profileID = $wantedId; + } elseif ($exists) { + $profileID = bin2hex(random_bytes(16)); + } else { + $profileID = $wantedId !== '' ? $wantedId : bin2hex(random_bytes(16)); + } + $now = time(); + $pdo->prepare( + 'INSERT INTO scoring_profile (profileID, name, description, isActive, + greenMin, greenMax, yellowMin, yellowMax, redMin, createdAt, updatedAt) + VALUES (:id, :name, :desc, :act, :gmin, :gmax, :ymin, :ymax, :rmin, :ca, :ua)' + )->execute([ + ':id' => $profileID, + ':name' => $name, + ':desc' => trim($profile['description'] ?? ''), + ':act' => !empty($profile['isActive']) ? 1 : 0, + ':gmin' => (int)($profile['greenMin'] ?? 0), + ':gmax' => (int)($profile['greenMax'] ?? 12), + ':ymin' => (int)($profile['yellowMin'] ?? ((int)($profile['greenMax'] ?? 12) + 1)), + ':ymax' => (int)($profile['yellowMax'] ?? 36), + ':rmin' => (int)($profile['redMin'] ?? ((int)($profile['yellowMax'] ?? 36) + 1)), + ':ca' => (int)($profile['createdAt'] ?? $now), + ':ua' => (int)($profile['updatedAt'] ?? $now), + ]); + $members = []; + foreach ($profile['questionnaires'] ?? [] as $idx => $row) { + if (!is_array($row)) { + continue; + } + $qnID = trim((string)($row['questionnaireID'] ?? '')); + if ($qnID === '') { + continue; + } + $chk = $pdo->prepare('SELECT 1 FROM questionnaire WHERE questionnaireID = :id'); + $chk->execute([':id' => $qnID]); + if (!$chk->fetch()) { + continue; + } + $members[] = [ + 'questionnaireID' => $qnID, + 'weight' => (float)($row['weight'] ?? 1.0), + 'orderIndex' => (int)($row['orderIndex'] ?? $idx), + ]; + } + if ($members !== []) { + qdb_sync_profile_questionnaire_members($pdo, $profileID, $members); + } + } +} + +function qdb_sync_profile_questionnaire_members(PDO $pdo, string $profileID, array $members): void { + $pdo->prepare('DELETE FROM scoring_profile_questionnaire WHERE profileID = :pid') + ->execute([':pid' => $profileID]); + $ins = $pdo->prepare( + 'INSERT INTO scoring_profile_questionnaire (profileID, questionnaireID, weight, orderIndex) + VALUES (:pid, :qn, :w, :o)' + ); + foreach ($members as $m) { + $ins->execute([ + ':pid' => $profileID, + ':qn' => $m['questionnaireID'], + ':w' => $m['weight'], + ':o' => $m['orderIndex'], + ]); + } +} + +// --- AES-256-CBC: IV(16) + CIPHERTEXT --- +function aes256_cbc_encrypt_bytes(string $plain, string $key): string { + $key = str_pad(substr($key, 0, 32), 32, "\0"); + $iv = random_bytes(16); + $cipher = openssl_encrypt($plain, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv); + if ($cipher === false) throw new Exception('openssl_encrypt failed'); + return $iv . $cipher; +} +function aes256_cbc_decrypt_bytes(string $data, string $key): string { + if (strlen($data) < 16) throw new Exception('cipher too short'); + $key = str_pad(substr($key, 0, 32), 32, "\0"); + $iv = substr($data, 0, 16); + $ct = substr($data, 16); + $plain = openssl_decrypt($ct, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv); + if ($plain === false) throw new Exception('openssl_decrypt failed'); + return $plain; +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..ed7c69c --- /dev/null +++ b/composer.json @@ -0,0 +1,25 @@ +{ + "name": "nat-as/ressourcenbarometer-server", + "description": "Encrypted SQLite questionnaire API", + "type": "project", + "require": { + "php": ">=8.2", + "ext-json": "*", + "ext-openssl": "*", + "ext-pdo": "*", + "ext-pdo_sqlite": "*" + }, + "require-dev": { + "phpunit/phpunit": "^11.5" + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "scripts": { + "test": "phpunit", + "test:coverage": "phpunit --coverage-text", + "test:ci": "php tests/check-coverage.php 40" + } +} diff --git a/composer.phar b/composer.phar new file mode 100644 index 0000000000000000000000000000000000000000..fe03d9b9c28a52eb2d0d33f06f4ffd341e97a6c9 GIT binary patch literal 3561371 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 z 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%0A8!1G2@S%5EWjizqixoN@WXo9S#xMoER6 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 wAwd_>Be-h-)%Xs;`a5Z?y#m!Zc@C*ZG z*XJ=jT?OorpnSEQw^t*SBPI4X7zWW8+tgO2yZ6e7@SiP;pDtqm)}okB6~KZYY1%;t z>#Ekbr(|@R>mb#&&p_v(c?c|6b|F+$@5~;vi>mrow;Bev-cmAY+b6qSQLjNj}$b) z)AIHuhL5}YY{19?el3d_uGaB_Ug`(QAl&9jT{6GkrBhQX)>(7*TY2obTif7|se6F~$W#hGM_3t{dXnds+I_rHkVYUd{j2qHwdi zjK9wq#-GP>?{>xCEe`=e3NT;zE^s#?cp{hSWShOr+fyavU<$H7Y5)R4<$LJEH?Z_s z*6emVeNRCX-ktjnI{2qJ*!Lg))g!%+t6!)PFn5T9A8o1ZTj2g}p76GwfE`P7Jxq5$ z<_YL~mhj_ezdi|b`}sjOJg8^?-6sK3(@*Zw&0mXTRLh()inxzfyuTALaO(Ed0Rp%T zs%UmsD4lB&o$gPGU|!cVxjC%t17upj^}BBS2O@$@W6dhsTH0x6b9RMRNykg!&cYMl zg)Vy92N(NdVUFf zNK{V_fqD#7eLqL5tGfn&$Aha^Uap2IoXu!&5h}Ka^rF5V_u0;pp;asGQD@42vOZl5Qx5S z>p-EZD%Z&hA?~#v5RhLmsSNr_6)`l%2VY_ zC5`v+n|p3-Rzy4S^|?p0PWf0HDm~0P?^Qf_G-SCoip(8T1Sj=A4T(PSz$Us=DCHqW{zsUF4bVhu&EihDA_@IucNx~F<8>%* zFcbX!h43x-{*x4mUq*Gm(PvwJICoHkn_wtF@5E@tK~N*D{mkb(g&*Y&^;G2R>2iFM z_mKpH)cxcpTE&j%{iDz8Er`I5ySx<-d&$nw$Fw6(Lpg_-%D3`>-p?hPAHuCnO*v$# zZU#Zdiz~L<(c0yaLzz@?G$bcSd6pmMwXE(U-`e*Z%$D}(4iAZ>hN}Qo0>R~mo!r%3 zd>dLXpy6oC)gvTyUU4sceDIm3MH1|3CxO@E=h}z&T_)+9R5!bKVCkWBWlXp@& zO$d+8d3MdtC9wP%b)I~$-3yvZl)o;tq3oiwni0&tbNzJ{KGfbIXc3b@mNZA2q;J--<6IwA5M4srsRwzj&Q~Ay!;imkCfT)cx{SGwXTi`QU!xu}+-eD#DfbR|4 zH&y=qv>?}VA=sURFxXEu4M@`^{+mgZ|1^ni{?sL^B`QQ@+grlLnitIp9DlWT!|M{` zlmadUOpIOm@-|Nibv%=e&OOM@#f+TwbshGH@n*tI)MiRJ#?JFXZXHO0zG3TZ`GR6y zfrh0O`3!afFaE>MeD+96tji1Px;E zzVNW$>98VX_$edeCwiPSw;D^71E;Xh$4n1@c%ftgD>J{xOE#Lb(rF9sBU0lJg{d%y zYjtfeqCFWU1)d?fR~u^p5*e0)`(~3gKC13?nBl(#hM(+J|48jC5XaD^tQa?mfH0YcmL-E@?1&sH} zs-+q>^N&BtkN-!H@c+#G{_hVC2sfJ>)BFBfyub6ui>e+q+CF*+3=S3`jeO%GL)bug z9^pwp;3>%o;L5+;1OScj+x@tfnc)aBiwJc8(Y73};T4Dh3vpYE?dc}QU{`UiqEb|X zgpQvOObW!ZLljvL0j%cOHkLgZsxd43 z-`q?5;2-QI$B)_0@AeWGKotMwy+rW;t-ZuNs(?Y}-;9Nt2wUm)NY?!h=xUZA_aHVu zBu+&IStj;#88={#NU05MVH~0({M{obZ848%rDCGW=h}R^%sDrgBS4oE(CwAAG8Nk{ z$Nk!I+Ubly#55fN-tEax-37$#px1iF|J%KU0wr&R8xO193p~-8c@H7zz_M%9{~LRW zzxzMBm)HQb{(rZZEY*5a?jaZ6{nBtFCK4X%bT)vkdQU93C$IZkXwaeNQf)~eIA%c_ zw6}+V4pj$X@Ak)Da?8`{wo;Z|4s)k7q%Dt(Vo5@O6L2rr_d$0s1O-wk2RSKn4UG%) zE>%}6*rI0_)q=m=OG*f{12X;xIqR*0FyD0ikLmDf|L3*IKj}Acr+1I~{C!?*aT(`A zlsCRh%e{G>RF-RB)cQLqu<=WM@-Z(CA@lV^0SNVpVV~-I{EmRVTm74j6)nX4Tb{h; zik{t_cl8G+{~HeLlYRkdIU60wRG&hM6#I>b_0}xDCBN|Zr+W=Fjqn*z)bwwA3z%^} zQtaVu{;>P|VE5m^$lL!?VDPu!epU62ylONc_kYidho25Rf|{fI0D*I#n5$2!1@i&p z|I#yxy8Aa&3-I^1DP=>(ag3J+r(3y>YJCKJsyPSkn!ApdO)bg}nM&l=SP$85h$Le< zKl?ist2*|&fM7~1T-qZ~kYU__hL04{R54FS%PMQCvkHUY)DB(!cI}30b0-|E@vx|s zNX$1ffB7at`w2y)3AXLC=f_IkRpEI6!GgmQoRqV|nG}=tY1q z{IaCpggF$-{oC&A+_qQmQ;Gp92#^s$ssFj8-cpQrWE*^j0=oh;KpqUzqMx4XOBnKB zd8+qAeHSjjpXy7K@ed>!D{8s`Sv_5ld~S*?*Raf?AjjyqjjO-_>hv9lZW@c?rUtDz z&U&54U=DZJtMU@d-l&?kyOq6qSL-nw=Tnd0p(ogx^9cn-hb)XQZ^O7dQbh{roI?_d zM6?8GVhhARpY{Qy91^^p1>5PSZpl%iAg9f3c;zBIif$_U)X}c(Vf6bXr9exMm$Bg`2i8XvxZD6a|BUy$ux#hNKY1gYi#WM_8~Xjf^1c5lLaJ zAqLbN6qJid1({0zhQEYm&a&kL5OijktmeUNdrhRtLl?U7a_Z(1RnjB0GHI{^G}?}l zhSi@&@E46jZfQb1DZMFzqlkWN+%OB7n4izj3}0V}0nzpKC4Kb7R;XyQys}ye(pvhW z+EMAhfCZi(?kWyOjq~deFDP<%$D^=?+4nduoAr=T1d#FTuDDjJV;}4q{s4e^Go4pY z4v*3pc&(mDrvfM{X&0(>zUm;~@y&I(PNg^+Oagr^I@j~J+((l@Ii9wx;cfM{ zKYEb;o1O;`V%d^_8hDsyp9AA&szcG*MgmBI-EEF$@q|DoVezR4Y^N0VDLPE7{ID@M?}!m zak|PU5XAGQ9Fg*fM(8dD;OBLoHm6X^TB;F|2b|J!bXWxK$UT9pK-utiHTj7x$i4s( zF);OLdN)cZ4&aGEOn6-`dz}T9jx?RCzNzmsh{r~By7XQb_vffcBTLwZx66{w zhZp8qI2c29f8}c$qFZf0R0V^0$y?qUIySF`ZF~lVpd`7uz5$9g{W2BmAqIs-PutRa zSj}bRWb-1@dsGH2 zPA^v#Nrn!g4{+CgX=Zt2E{b44(phtc4Wu5Xlb39-4YPeljKy8hz3k9YPL9sS%b}?+0akdkKIgt6Cy?&|4Jo zNeDM}ByFZ72_{cuyzLIdNbZr`)hPCLk{{BF-#HIZA9z1?nt5Sy3LPI-(~oENz}zmx z!ohs*Vj{3Jg1&OnycD?r7jPqt?G;90z_e_<%R(-JQ>wYZIb3rl3`vLPoF6|}@w87* zmvbG0H^3P&JP;n9atdjd+7^OPr}^7o0JCETEkov0~_tI1rh!q z_|eTDxKba`hIgfTtlnv-y8mv7aUZ3xeSMGGGWQrmCdEf-49zh6Gi62qn8UpPI+ni_ zk}zrmx+@Rg?U!@;r_U;e{;^v4RxCJg%-Xy3h5B25|02-L`TSLW7z9FzD|{5)QBVUm z>+Q|^;LkdL2X2L=BKUe0E%&jZ=*$iSuNfYXz+_LF?V>Wct?BU{B1!;AZJw^xxl&Sw z4D?Pq@wJq- zU#}$z>}T4csnu*OCmlS_|+B!+JwaEjr{h*}fdHP2&;Lg+^vC4i9@aQlPSw zyG4eS;nTk9WDqoB=#zUkNP=PRYAsHd7x)*eC1Ye7rO+&cc4bDm+rIKMkhftle zL+w2~?N*;{J!Y95cTYA?&@&keI$82dL60(+`!b1%9nrvy02XynA$s$;W;5`CipkRi z-Z&(|jvd*05rZY)!_Y3f^CmlNJq)rfeMF{+P?Dw;F-3A$9Xlt(F^+KVE(M2tFSU0t zT@VT-Zdc9DN&tv1(15-%hsPovSb0%_p>ayPr?cq};c*ZH`{DGzkXwJR=mApFA6w0T zW=XllqoV5SOha{kJRG>g>jEzeU<)(|miyCb*$Zql09A;6!Z2f|U%_puyI@xHQ^R_i z39~;S4>=lf9J>}rb%E(D(7!1?2m-JW7mCSN>G?rpHU7N%w-o~jXn1J=BIL#se%Vm| zt)E<@)+ENat&4DiRM(-Cn8&_`sMy^yje}@03E-WSx2s+ZY(ao~P#P7E;-|psbp6cE(TZlNJ+#mmg+=84x6qchOy$amZwVuvXu4 zK`gwVdo6d*P`b~9f?lDVJbQ!(t_G|>Sss7tz#RP&a}O@?uX}S*mxY_w+a3^rGkVa@ zu4aKUla}E=i$*wSH0|>+8>($eR|;+80l(zfb|-bB3eMZDe^2)JDP(Lj8Bs7S;lvV$ z1_)g?A-%4IHsQ zQ<{D(48VZ?+2(>g>%Z}p;|)S=@_Eh4ycVxSJcIFG*-8qW6%TVccA0YDNwJc$(ba+E zs4l%fG{D_HPLgXy9@|^^z~Ii3h^GLeT3mY*j z}gmC#`Rg+e^rWpq@IaXg`+9-xaHz@h-lS?~Nsy z(a)EL)FHZqeEy1<)lR>nMLTead6V>^@OT3e<{2j4P)C5{@*}zspa>b{7M%kOyga(8 zmb2y`E$EE)lk(Y-;*X2`BRGLib{Hi32K#r)_*dM}cLN*@n!n{3K+GZk?(+fs)b*Cn z7z2%X_iX`|`t{lLj%tEA#;1S$jU999_ayPFwbilNx@~UH+&=-h7IY zom&^suvMA-=!z&hXQ@Ki*wYAU)4#m1oq_BNM}{dPdpFF^-jT|URiP!t-S7FOzOZzE zRMmC^>4swxc}wFtb2#GK1yt<;xhv-pTs@%CxC~I$RYB(v1$6{Kdk8W~ckUFx>V4=g zf7JPGAj0G%0@DoY3Z0LO9B4>E@fcvF4ai8*5Hr<=7)2>Lj%@(s2sI+nY@^eE zolVNhkSp?O75y5w{_(2&yLI-f{?hUTx+b7Eb33&OcM*ECXM5?7$~>O+e!P;s8@O*a zSOW;X&pTN$U96igJ2yD40R;|0Y98obWD6BM+hJMXwicJt|4qa30V?}K`~5bNffRdB z796jWUEr`-sPFhkptV9+iGTD8%ZHHbBkh0(>8#o4bb4?1mc@gZ)8?ET2sw`5tgYWC zH^3eIfoS%(Kr>TlHk^u|XWPFngmtt))T86+WlC6b+v~U^gCwYN?U%S%`WcO~sH*De zNZEwA@DC;8&jg}xnRDOq3-CytP49Nsb$mc13&CH9bWL73Zb$P@r}rsFhogQW)bSpG+m4 zZRw$27mVf2xZ?{u9>~+lJc~vtv)4tXX6e@YdZ~3Tqiop}?5~drN5bZQvoC}pH$!0T z7Q6@csb!=+QA0|vM)rSj~+#USs7G+4Ou~);EuRh@Yxf@+T*x(+%=&kMEYH_!e9#gzLr(E|(kdhsE zVVj5#q^q{c#&A&%#!6AY%X+wO^u;pVjYvpe;laZK7vwhd>j?<8fONz7c9AgruwvZK z@b711-QDNpX7?E9J}Sq#Zcgf!_K`!^xB<4CS^ULg(hF>id*oP|L!MtDionzXp=`C< zn1}bu342B+AyNl0T zViVcip{-6);N>j}uyXMVxmE`A@Gr2X_am{_E?qlgdTPm8xClaYyrx{VmyZC##T8Jp z1KZf5uEyQH%A1RJTT;OuCNHO|r?tBePC8!D0;^5=q%@fStas%e{_I`{x-kzgk_7wT zj^82v{iIxbL_!}PgbCdjsi8Ip;KMe3Pmedm&w7}QQ$smj?wMG?H5fHcx&R`4m!d*) zJio7&qnxcs+iAy=cv5t35V4Bj;V^AM-L-LJyJAL^sP`VihFlTTbXjOfT1C&uB1QxZ+yq68kOF}Vky3NUYN)SP6B#sdQ zN0)e>yMPb2I^_cENIQmi4L$2sxb=ObAb7xw0X>GO)DB+_lD$EKnq;r*J$L5EJZ}hp zsYGibxq;4!hYlNAiw6%7qav=CXTy%n_4t4Z$MS3Kemy)w0jRiyh;G7)%aeFSq_EnJxGmnTh`ZI^k7;PxGt}ggO?|n zTlX-%J~!JlB&@kJ;cOf;hj?-$Qncry6)(5qYSTP1%W4Hdl&#AXqM1@|dUd1pmp95k zIK_YOp!i3s2KjyR#J`1qsB9|b0pM-SgK!dVrFxuIGE*r zcY-h9*t|cS;D8wOe?7rJKKp)xzgxWDNx}c=3I0C7fm`<{CwTQYDhC-?3;CpbATNLU z958aKdgDO$l2kO4=gSdR_Gm?Uj(v5762O92lj@U4X~GU0i7+skhGpnWYV;a`vA|H-A5815fv#sT^2HFb?P(7(24Z-?|Ih>}E@cSV|gN zuHEf?tYc^ZX(-|PhI2xXJO<<|o*DZ1abea9iaTj$v=JOgrV2z#Kt+Ux1rcCLbm0Y$ zhYnayvd||cy5X#j=a*PbNR8D-@K{&L(z`2-85KSKwaNi5rbAh@|1w_*@Nxd-K-@_7 z{1>EpT(kS|bxr+H5rOCNAKXztdpiH{y86~Zq(2Y_M^)QhhfYWBT*&vB!g2(3OO>Cl z9FPJ3V+jolP~2G6G1A!RYph{@Pz6C+lq{?k2UvGG`8TL~C;dzMya2cyLT^FDGfD!B zMGaV<7GVB`@(vPmrNRm!pAYi~Im{Kv)y~pCLO#HEO_KMp|JjR^e zl!97G?C}>;`VItu@FF{I5A)ry4#rPc)^A}9t}P9n7UY5&E)ntQ@PK^v`h>i?=#RW9 zKfnrp>@IDPUpMeczr&UF23OWQ`LN>$cOnm77S>Yp#y$?2he+dD&jPk0Rl8;4z9GyD zcXLr2V4I8(v}3W#V57squdA{6gK!R#3TU=&Mmg>FP79y?i($N;9FY)por^I9(q06g%;UhZEe1G#iB9 zU2;EENfPVG^UgnAp>X;5kT3o8xc~0cZvJdT`6o~NtEve! z8h=WfamT^idG~kk(I~{@g>xWCVtA%kBz1huD}co@dEN{i{2YA_%kH+pxb*;fx{CwR zb)!W~W;n!+;EeYgoi9g9L8YMnb3G7H)WuEd4 zv<>G6+$5j*^>yGO1pck08)Es&8jmEsni@4*b(J|94X%14%Y6tUr{pV-HQ+CrQvw2D zSkD?mV$sZvAV8#^0aBg;M;8du&}pDY$H%MZa(sWn-=ni2*o#xjInNOd*z-}-N9TA@ z*R{JBxGJeWCsS*j?16cG<~g_<@7yz7QMAOXJYK`47XUi)0vN5c07KzD>Z42MDI&gm zYW}r`Zo%R4LK^IHYe3786Sp|v_g1`aqUpfk2IKe<*BHAPU~1{Gn7oDBZntMJorgPz zkAwVVo<+Zdu#o)4M$x;s!dbfT!XaD|7!t+Nkr8GyIi)lhw{&@AGv&v4hSpk;d4&*{ zP~+bZhHQ7u?RKgFt(VzWcq|q^8;HC==dbPg0;wpjWXmcHm?Htgg04%0-`pK58C^wtVdX z4v^n&^~1a*K3AfWy8>POiIDj4yg=7bi~%MZkB2SyVy#y}z}7}SMwBK@7~!FZuwL#1 zjJ|@+0la-r73Xhqxe#VkV#-;x@OfDOMAr0HHTicvI?Lhyb_Jtf*&d^NbG6t&+DP8* zeUl)0{QM8QZ{bXZH{mh#2p5zx;1K$Bdp*5}p>NI?JW1Fo* zrWs&7YfOb>+!^s}}@(IgavH+n`yM@9WcEzop1cy^ABG zrolX${H? zC6ytcUC3XnQSF}i5$eTwdlR}?gzsHaE>Ah)Eo{9WXe_gI&ma|8b61{*=t`k5iY20( zO((oyFcY(~m2{Vf#c*)Cu5OSJ#*tWdta~l5oMq2Pe_OWCco2vM_6WV-o;{H~9paGR zzV;XKz89a<1!n)4_@uP)PFzmiwr_%h4kFSoBZw~AZXra>QSV6Cn7SG44xX%jqK?TdS^;lHu=oX(45Aqw9!udMBLXwegIn+1KCN3 zhUWw7!}t}gW!3j^h|!6SuGso)Ci+IxY-Eg+vxON=WQ(-XpxFBgQSd=Fd#$q@$W?mu ztCBncGkzauE3I zn6y*)NKOQ;X9Z2bbqnQWB_bixW2s+Q;>l(ifw~Y>vU?*M4#=cKK-Px5mLQcGoY_my zkTI~HPl+FlxRjGqik*++Dom(NKZam-h1b;C)I@7BZ*z&-I=r?%5l>g z%qbnmiQT6%v}VyKl0s8i2)SNPCI@^ep$2;97-Cda+=SeNkkE7r5iio|7C^#ki3!1u zkl5wY?6r+eUNGNpP!D&Z$~d!6zf`m!GR23KC?0F5G)?ZWQLp9;LYf8&h~$Xw1{pI5 zAsKEcpqyutIjN|_pEmq~!$$UEIO1SWLSF}Xc@u>4i%%vUW>Ef7cX zG(NcF$f0J|0{mb|?hRI%Gg)cJVB;DGCMU@WLPl6`meyz864ql3`E}1$VFWJc;92Hi z`JtQHx_ebJn!5E=DL?n!>ztAkuSb-xI@^f5Z78Ff45BAi#RaD!W!}x5ipHk7YXQoV z)|Ni_DSMF=D|C}+KH^gx6!opoDmHR1hl8h!5MxNm4P!WuO|g9_Hw8EJ8}O}t5y*hZ z8W$lA{fu0;YsL%6M^S?YXaCxBTnhj&uM$}^I9$dA?BoZS8{8XYEHE#+ZAhC{BlK0X z(H2x)=(7WX9RZxcFzByZAp&warf&6A| z{T%RDKxmqT7O>U}fs-!-($CV@3RJD$g*O9?o!0aAb2A1a4>$Ujtgd2vS9@r9CdV~5 zAr|te?0@u}{9rlx)0TG?)x+j`^didJuB|~sOPIMld5!`$+tlL?ID@WxdSFG&#JOR^1UKV= z3UgE4hwsW^TXpPeaO{}dM7ca&vXF^P1@BP)@GeodPnJ!|qS5nd+FkMRw!`S|UBLF^ zH%CJTC2-TgVh$ue1~P97J&IiH7$Y$HGBWb3byD;IjU%oRD7{zL%GkdwcF80uK<&+BYlgLJ=8u`g7kLEpur7KeCx7Uv2Qc)V`wHD@J>9Wz#V zojew|xJ)*hQ9lyZs&0TNe$3*^UolsP1P3ubBH-b1jzEHgFDiz?R2yn;;%*bUf)H15 z^ti+G*q+VZ#1y=kNL;xYZAiaj=%w%xtv0u0lf{RdHa@m#q)iV}IHkne21EM7_mnQA z;WKBDXOBOg`+Khn_O{Z9*ReiJB&^eoi2{9JSJMk0gP>TimWfW&%JIl;_biJ7nbJ;PvVl7A zpPl%e@zH_%4Ll$)CiXyjyqqsgu$B4#5!&+}%(9M7U!uQvS- z@@R#AN0x2xQF_6-!LpH0Q4^baaRw;KZ*z_>o*JW9^8tq5QOQ}Mkih3vX_ti>6P z+f9ywAg60Cwa(HEAzFg&WH#yUM+}hT6~Pb{-hqVAnQO0z^$H|De-=BJzY8GI(T?vg zPr*8JwQQ>c_bLRzCN~ro6LL-84d#`CSNtbBav4!$yDgqAYkL`@2QTiNpVvaY>%S+_ zzilX>*!dF|pb%D4opjhZM1ysnZIfjR2M06aVHsmGC zEDSbXn==&H_p5&p4?x^us`L4-!RJ` zDiII-upc${M@bD_W_02{#Tp6^B=Yx<|7x;t-cCbU?VxOU z6M-&3Otz5poJd9)pAPjdA;3iHv#XB-p6bCzgsDx?M1nK_(iza(u9KqSYW9Xn7Uoz4 zQ^|nPv7Ak^MZ)<)yb!qbeN#mKrD&Wltbk?hox+7dp0Q1HZ{@ye$*KY>us-N!Tm6Ad z3wqwa!|%S4{iMfvR`TX4yLL~@&^wC_ zjUQed_vAY4DZSAbO{Qu4g9)xaT~==~C+*Ic#bUf8%+YD%6m4QW?Xsc2tvjC?X*C6S zM+>POh$K0k^|IQ(y2k$L!UC)9e|cdE@Vf)U?RQ?D<%GF z=5SMR1=}}9pQfG%teWwA%88@j8sY!X+j(rejU{b%&Zqe2SiN8gf~E6T;i;&OUqV9(G<#4cr6#n2zNs{8UAEJ9{tvJOZC#ZcY&wLGphRlByylxfa^9X z$pPly%HZtGg~ln@vybnILqR+|q$LU&SlW*p7w)*8R7ddoJU!6C#|z*K#vaL&Wx-hh zb@n7k6CoXzEY6+<=a{Sd;A7Y(){)sO!(mi5WY_oUHkNyW@_ikcvgSJ8 z!k(dsN?Fqf%*j&g{Lap##J`dn?x$Qz-&}u~YI0Av|hz%t7 z^kNs`uOvjIarn`DtWO$O7P;8e&Nzq%#=7=5t_PYwruJ9Z{`7+#3y2#D{k&%aTK9AE zb~M|~G|us%HM7F;=iHCgX`Pq5f53tko2$)S67O~_w#tVlFmX7O=k|6xq_879l&cKs`+MjxC&?AI94Z z+RSkm-_wP&oE3@Rdts9)Hb~EwtGv`&ru{$3}*sON3y6ssVvZD0~ z22JG6#bS-Uyg&Qv;&8>Rz=TbkX%oeCK6>tLUMB$QN*TpJ4|5vF#uB+PjsQ}+pRzYx z`PF1%1<~hAbFTxW?*O0srZs_q12ioRIipB=IzKH^f8Zq39 zR|g=NwIQ^r(7c|+E8$^NYEzVeL5KB{SRwq&U>99{5dKaMjr(;tC2#i}?HSwGA?daL zgNNr!oy-nExBWF{{Xfz#O!OZW@wfl-Pld7AehOpi#|DrK#L{yVWO>{ThwZYcY5z{7V;%e6_MKG&Udta2BIv7+zx+eL$(KE@ z8+35tuJuP|@W*ws`&h$xco@Pj(Aef<5aU@RRP$o8U1@H*C_5osSHT*mVAD0n^^<;p zOr-WoDnQMy*0`9qgI_|*UbbBs$$&o@yNzC6kYjQf>%KtZA9VHSd11}WqwD%=9E2t` z%VWtRLqPcxB)3^`ZpqMLKVNQ7b{(|wBEd}=l2dOBhCc7*a<8J3 zysdg$m;1b6@p|Ro7BcDA1+iX3;J*5BfEklO9j|=mpx(}+nRJu)Qb2F_hyxoRmdu64 zTKe5qP6wgwIz;S>GVF91Z1K#7eH%(c{U_OZ)2%Dx1kMl)+| zd#<_f1T2{DB$WU+BwCsnsqP~%Plyql>vvGs;SoGZTh)S5ka1PpW!a}|-8fI=Rqc*2 zp4(Jo$YpdvrJey;Oh(XzV%<$5bF9Fo9p#DE_yy0cm3i=5j*Gz^+~!PkKEs&W7lUEt%V9S}XD-5a5xq=H1UMvIt)Clgv7C^3*)y#I$DL zg-60z*JDph6|FYtm;DOZFUiqrad{y)q4$wfTt6R&)2q>BM&kDNVQ%}fZNsO#uLkFS z6bowuZmE!zP2(MW#$*DLlA0(L7tmD(N;BOkx=fphG*_br@UJYyfHN zqPH{M;5#0bT!G9;GonVX6a~PWNc!^uZ-9QdGDL9Y?j!|%6e?I4qi(BJ1XUJnNqKlH z;;bL(EA{dqJ`$U#vy;eYEwr$RBD-eVIG|Nt;&=>hI$p~Sve`M2BqMHvVdafo$X8;| z4mg%caCwK|RZ#U<7M?lHQ_$oGLgZwad7uhP>7uf{wWtpM4N<4N7yVKI#FZ&k6%WcD zsUex)ZT0>b`RIDK^^(=Q#FY*7LInU|ZilhlrE=1MTavZ6@Mxen&y(Gf=r*_ms3q;` zcwj-W`=WNS!;3Q81{_U2TZkYpu0&4f_3mH~7P){JFI|Tn0&6gf?0a0tp+GXJNnlmC zvL>TtrB`)}iLg7-`brC`ARqE0bu88nP2R5O`zu%>wI1M2%?3COv&-l1(*Zf^*?K+f zlae{CU}cI5nsC>>1$x%X8)1NSKC-Bg4zu{&?;q!D{jl-u5O5Uqp8W>^1gt>$sv z#p1h@lLrTh_QRg)HVU^NXSRM==KjPC`lz?>P2POM9AB@8Ch1#L7CfVM-9grlKK!s# zgy5;wmJF=DivUa`Sb}6aCf}*^H076N%Q;k5lGh8aK!|)FD*Cbfn5e0 zl7Cka0HNa}-TidY{#6h_#|%Ehel7?;U9{bQt04H)1F)a?bsOv7D+qoSOngs`eh-bp zK8v#q3`Xr?z`M_==#)&{Empi>@m_3pICV#2YxA+TL2?a47`^B22_SO8z@&Oq-shH= zfD9~Dklbf>)nSw&C^2p4$R6ab#BvhEA@s>Sh@A&>dQ0_oR0V=5qg5T-Xe-@NKJ?$SuroZP}|2VS$RKTAu zU*=5Jk1VPA>im~crbGEMF;>i$n-~11v;F$sg#O-x{s69Pz-WD6mIS(+hi9@0Ww85A zhCP0A8b2=~e*<6tYe|_usUNjOxr;x3YVoJ_-!EOeuZ`%hjp;A#@t+{rpTr4EnvgvC z2A2df-sdxA+kK}?hTE@nNuU@n;BPL!ce3T;T4up@3yJ)rNh}k|>k|SrpoI6z0QQgp zZ3mV>zRBfcj$_#;ODHU7 z&?s8on)lJ~j>{?DlI<$Q+A+d(qc{(qr1DWSV21V#daFP|1E^U%YT!GfIO}Y4LK0jk zppVi6U+X?s$BQw!q@srp6t3SqfveMTkXq$-DOy`8gb{r1{vggx+aL#>lh1NX(+~~V511G@5 zBqJJ+TzrgqR*;W6-jl9=tucFT(k$Zv`&_H4#M>-sZTHOGJz{jYBi9bVU4zdW3$3rudTu}aA0{Y9D{3N|# zF!bL!laQcZ%D*_1U;5WSoyqN63Jj#T#(L&KR_}!1;Qo>V18{(`?#%69#Ml4yOzu91 z@_&0Kzs>=^x=p{F${Ia2;% zdt6o66y?#J^iDesvQH0B8OTNFq|pAP*hI|?Jeybqyx3s(HPXDG*USdOcipo@Pj|w; z%U;^jSH!k}RX!b#(VIJ{mbH^^ZFz6(XF!?k4^$rkfhs_jmEgso>cS$z>kpZi4czG(wpPO$IbSlLCp_^TdfKEG6IFNQPPHN#=52n+4?sTY_ zVm^y6$QSeg+0}!C8Wr&GeJ;C)#~_t#;15!8<9lgB_Z zBxhF{f`!O>V9y9xxp$iQDz$7z@7fcLtsp|BljfR-Rylcxz0}gSgcB9(m!yQ||0*k~8m3VZl9 zBKF%#=}VLVs8*El34f2>Er6XNERWu#r6Nw%di%`NAiSUcyTJuw*Y0m8Xo83pnlknN z9G5|fyUhYSyMIio!fpx+NcD%ncE*yo{T{D#@6t*TXO|&NueZo<@Os8yE&5d7sS1{L zd5L@kMcftW<<=NhUK~fgPqZqCrx}-YJ?O{kXp>V3rr&THZ+Pzbue0VqF|pkhTTo3= z*@wNIi$sXE)%aTa^2|K-)@O15lF2m8cHl;j9VP%xr$6JZe`(oihbyeJR4La_f;(S0qy9wE$tAL?}mWJYMyCRj%jx5O&rWA30c9 zX4RHMLLBgpMOi%dI*^&Xaofc@J}#Q#@KqWCuIF^GW}~X;hk7DGs`^eBR&mkBOQPtm zIUw&l$?eI!*WFwtF2YKa+}p@Zzussa$GfHr@7H%M9PUt(442bZXNs<@>j6YP4Kfej z6*lVfdZKJ}?Zb0YcSA%nF*qC+a-XE)frn?APD=w7GrQTkr-0&1`d$Y|jW5|p{%IYO z5Me3yP?E1Bg@-7EK%2jO1;l|o1POwse4H6Lzv!LZs%Pz;AIH z*Eklq>w>oD_1QmJhNS@6e{NiMCLd3}5>OsMZtjIwAnSu1#An6_^Z_}@pw9N;zm&ne`DA6A;* z>}vi~nJ&IQS8CG#QKny5Fc<(W?#F+eZC(BZ8~C#Fb@{ne|C9x;_XpaRC&1kg6VE!( zLX7LTu+ZhA15)Q3-|F-EFiYQALJRyJn91$n#T5Y*3K>|cKJhW#iitOcaE0=-Xh>0#N;Dg{1R^9>`PNh9DRXuWcF)5U zj5G-kU(xxxr28#l4wJw!F*l&CO7Nny)CJ8@PqhHumRe_9!H|@U)*(&IlUPw08#SiS zJ3F)6FPGU_U|>PrEd%7sO;h8|qXf;-mfWWo`7Ey2SF1h|vs#M=$bjUzBK9~pQ)%Ia zgLmke(=!<<9TvY+)Hi12j_hx@;ku^v)ZT+}>PXK4@|n;iA&^=~-+rAE0Br=seBkd_ zvE+d?$ZJ-p7+PZ*i2Sa#Lf$Ep8Tr@=GTKivzC4?8HKr?DMkfB2spY-`N@O~Fx=6wd ztujhoQG@i_l=qR-_qko2Y35OjHqY8e2Q2UGGC(4(s@8C6Uu%6F>x)gR1-ebqCD1tt z&IXKaA2jBQDLwEHS7lXAlhOvBZESRLAFv>R9EoTG#WKNS#~$_w7pkaM7Ma@Fm|$h- zjy!pwIS_LMR}F(W&3KFkJhz<+nGfvz$=n~(Jiu*EE-um*UoD#5uDf|pr?- zTnoExYV6A)M)xTx#nhDDD}SGg(Q$tbG}|}N1>~5cmcvA*f?ZIay|7&KwT*rT8Wh9` zFU-el1PhDbI@@16FMp)G{V{Twfu`5J8{#wBXU$$<$lc{EjTF2W%|8s?>(I+ZdE>$g z3F7nBGBG~yp+@sw@NE5yHu{d0YzuxSo}gD$fV3><4?4H=#7x~-Yb3dj~R3;*O|Fv7=s5&El%gu%`+y6l1@t7*1zz~V+b#6_T%7tKkv z(t(XIGCHA;*zh>`49{<>2ns_0;h(f}f* zE4>gjrqTLt8%{lp*ORl)1ooh8=l%iL3ANCO#Itb`zK@A6JrF(T8WA2XnObsA-|f&A z&)-TC!LFGzy*hAE(6*@@R8~5ql}@Gyg%$gYawLghJk1sfc^QG@CA!!2F`Bk1C#o z2hv=8?6NU!yV4-U+gTOey5)OFB?8YWh74Y-Q5%PX0Zd$k!IzhSL^Ow&IcK4oL` zZ!Ip^cro|DNLs(S#~yYi&Y@4ikQW$yC%{EYKn?tf4<;;E=d(VIgImmjjqm9fdf8X2 zEWG7Ee9gyfItF3#(bw4NA3g&P+Lr@smml>I@i)gTju*q~GW$xUWNV z;WISnfCLYzTc+X$Inb~(To%_2Q;d?O8xdAx6TAPRJr|hHFyiknA}LoVXL>)6EkA26u!%$lJSIkMm%t z?+K_=KK7>698R(y@2$R}u9Xak1YGMv5Zfvla#kI4UoMgQy1NMO-tN%K5mi{_7M;sV zc;U8q6ruq~e_XBt{Mk<9_j~8%1+Sn#%meC{;53J}z zTFm;rfP&#fV=<$R~`UEbUVL3D9 zs}*-DYoCe6!`r%5o=0r=-cs7tXw%!SdKMrcAS<{CkH^4`_dVB4-t;_F;1k;XJZ~$O z*-Rbc6@@xj?@TO*Tc;Ldz87YkfACvz>>O)p@0PuL`*DSA zVz@Z1fbHFe0Ocqh=o-c%NBLlQL|ngGAyw;%*G=?&d`EZhoj>AT94q@rR>a46gn_U1 zjOHmjvRmhws6BMZqP&F%c~}JbA0`*Sc_99Np!iP@MDfc55&!9dXpS*|0vCjtwi$d| zRa%YJBQ`R_@Vk2B|MLU!#{}{F!*64WACaO@RQU6d{Px-Z?+4=LFJnZ%x&DY)-Q_#H z=}-}vWtAz=rM1M9L_KF;C)>HH4>(Wgy>tUk_?&=R`+S#Ya)F*qpV^A(**>O4_51vI zm&!e0mU@@O>EvoUS5gFlr~y9ceOlj6$a+M{v!%+%IF5_f?ZUEz2eP)9x2zm0X{B3r ziEo#d5fBb$oXpU9$qqOC#`m*>cU{zu>@xT{-5gkXIiEeUXslEbI!^xdzmJlVDYYoxHhC!8yutOu~4f z4Y`b@?c;kC;2U@o`UBW0gUSNA>4Mmg=5;A~gneZ(bfNTw7W(i4d5Ot8mBn(+;*>tl&XD> z^Ip&di0?gTt>_D*)u5TV&y|^VM?7BkRUH)XHVEIcd%}{mJl-y^0rMf8Vs~rn!?S@3 zuw5o5{PC9I?(;FU_hWUX0~dYMH%~v7z-IRi2s)W9&?UKTI>SuMHWE3r9H}=<*1kr0 zc3R&riNQfi5bju5zp~Wj!l#{WEc~Jtnrk5suWndp8`N(;-;j z{GAdXsBSYt^#Uc=w-gcFh#@4z|3!@G_cP*OmZ-QEHt$NRiJe;q1V$k5qu?rEt%9Nd zq!RfT$wH0U^ifFnb~B3k;!iY7zasTKF7URkMYzy|&%6(i^6IgdsMu?lSmBAQXx(17 zy^R1FcvGv)4i0cnN7=QgUdh9k@8R7b_R4;GQQkoTVFq)^=l&V#4ZB;b?R_8F7fn7b zSOiW?{&ol1z_Vr9qwqZYzDQ~zb(fm?ra&IG&6?KP?xJ-7H$j1%qn1yZ9Cp!xCqL}u zEk#Wnv^S!ZjrUV9_ct-FfQ!f-O7})PTQN@gAlv-u8ku=_*Hy`)ASGC;u6Br|j&JL< zEUg#l-KCw_H?L{jvU?7X2TUUfuMtVLz2g}|5k+<+UazF*YfrAmLCbGOm=a48DBR2# z4^~nec;a|j*TE~6?soN%z}hJ2x@_UJH{3=ni|dq?E$~|GoBp)D{h$EOvefrJJejgy z;jW{2X0*3_2)*p75Kokr;^6Q^ICOWud#gqTg05|E$@OhVH*=5#n=)~*A6GN2-i8gH zDXW6)UA-rYDSrAa*^7rKe>0BFyUtrW;|}>@fK2=O;bgF2aPlc?&jjc}?s1!&+2g!x*9R;a=#QI~4^y?y zNngpqR7Iw9Uo8L9cQK0<%<-ik!E;3KUn=e1oQ6!bAde8M$`xg;)q2&8yM>3f93U)gcL~4w0{8AiHXOT_aH5A1)9MT81+IWgz=*R9f@D5JA2ObsP}JO z-#z3)j@aNrCxuHtLpHi?TIl)bo+iwxAc$U?%ZK4of4d_7-6Z{!m7e&lrv6P@;GYha z*nOq}`+wnJ(ruVIAx{Vwr(oSzUt>9q{xl81M8NC)Vw3(J67US}D^%q_G1Ko1$a@0@ z=BEIF3u~t=Wja9L`Vs*6m;-+LO#Evz{kJ~Be`2OrzS3Xd`Lp}C@h{o|J$cpj!M0j_ z4h!q>toO!)hSapbrNt|ZPtGpALo*U;)<(+8&{W-+Fs}(#Y@nHx1rnpSOKfv%X~)=} zOjyS$?|lX_q$o(m{O&?STQ`MAFkbs3w(SN#XYgWgpwF?H(iXPPogpYQ^f;4p&4tVR;pXwmoZ2G`xY zI9i;z5@+L`*NJ$uH?E6-F4gAsN|{|Fvafb~6Az~bE!S!PCW3`9MtX-J$Bktu_9&A9 zC1QuDf2FU!<5wypSQK1wxAc|F$uUALh!)w*^?<;dTrR~|$KdYw1jDK!X4ZbMyZY|3 zUT-!rnxN;h>J03mIUR!ptB-bk z2;Jd)Qr^4e_<(MRXrca+(w2W#sSLu_O@7_8*)7CUAAMP{P*@xiGkGNk0hS4~OM0Dp zK93=aie*}1&uC&#SHasEGpJGc0R|by%#E_Byuv1YLfTzOjFR4b4aW=B3?`X9+>kks zGrNZg40%3b8iFP5EURQs`Lul9TVtY%SEBH};z4z1)8zlaHh(E|qdh<4BaKTYfJcIT+M&9 zT*j{*$Ll?;)qDC;*1=Oa^q6%QY@eNSWBUBUKlbZ**mC(?Phqfu9S87rIzsu;tX{AQ<%Le2Wcq3EN6yFJ>CcMEE^avz%=#7h)b2q$^QOrk6Xh5)Y3I>;>p4rgrsNN%68l1M}=)$p7C%sscEhvD*Wkd)>G6&9Hr{nw#0QEPSZiW@6h+i z=ND6YL)>J23Ke=xL3wi3Nb3PTdx`V5M321fDmx+!9RhwnD3FN{mWr*5#6PnqZIFsa zcIn_`?b;1eE@5muss>*LHGYhuEkU40j9r#duZPIag0pK`Ah508j=$YdA#zNLE@4=i zO%%UpRNj3pAJ-(e^WpFd9^u63Ti#a}bRvqB&am=HAFqB0QDGz0Cr;z3tH5ej0rKsfui}WoCQ9I>)V^<1LtOl#=!gR@5i{p$$}5 zmJR7bx>G%^%+cl7XQDxiRi>1q^LoRNBmEk8HS~HjgzZBNxy=YgPUJ()ReGPihqj9N z=3$=i>OXYJ{*}J}CD?mnf0GaRH`9ZsFUk&B+WKB?!>B5Ef~6FgYJk6)vf=mk{dv5^ z#-FQg|C?asN3{()m_JB-e%Y$~%T+uDOnqMm4u&Inzb5bhl!*oC#ka`71qKKopS26T z3YYjA8TjQh9FWc7?&mZEgc&}PmLE(wC|}ZVtM&kmEb~u_7t*{_0_1iuYnSOdKWu*w zQzw`WoLK40oCGEb(I5UH!(D&Q8Uyp@pLFqh4v}^_rU3?!9oqYo%!}L8J)kW%+B6JpguZKdU(cZidul? zR)x1BOPfg@A05OG44j8uBC;!Cp{v-2Z@Tz@>*=p6o&#r}-yFTGyE7<)wXoA-hyJUl z|4*Qp&zjV~eDUH{#u;M_!$>*GB{&!`T*n#nSY<8P~V%nZxz!T5=Er?KW%yNw?cZ0r(9 zmN%gdYj9@?UetT`fN*S-3cwjQv@UJXL#X=p>J_4T8}QWzrL!oQQ3_p zl?m*%y^rK%lp`q0yvmXyz1m)@>+C^SUY`V# zxQ~0`mQ3r5+hPnGVGS+eZ+d3nu~QJ^s=XculxNOWcnX`{w#6t?rWyH z=Cx>e{;m!dm^KomzIQz;I6qSQpIgS`p&bHgpGF?eS|^!M^Jui|CmnPtc^`NYVtH%&_FF7uIf0FI#8%dd9cM!ygo|sTbI9FwL(TOyO|Ri}+QtRKQ;#4y^N-YU zK$>1h$NmW0_t8+G}v_7?uWJXNv)KM?J-uKFO4Bq<7FsbY^bh?j%asUwU(^SiY7*K0gn zAn83cF~<-Rc^umvakwfH%Z_hN7oO0tYA=WUD!IsW0A&_OK0{Cqbxo>**PbiDto~G= zDih2&OUKXK8muzeDOfmo@hIs-wBn_+h|buge~G zVmlBY7ZiV+6Z0;<7>IP*NunzA%^pq)x%rpv#hi}PH7AF=vg7u_&47H?i&jsUlT+d^ zGrMbt_uxVaBi_Kc)TjN!4(zbN%9WbA^DR6c2OW@koIE!uV(1WE=##28H<%2};w0Ch zA2ZT^1BM-6Eq85lsZ-&$&(}&4u2SEx5FlS zKZR@n2KWEaVKblShN{@pL51D_wFRy;IR(d;xLEHNAAq1#;=2Lja3Uk=V5@8>V+Osu zz5?j29nnYD6TMIZH1Nt)9_Z2pmnXQBg%(zFUhu(R(eR-X`&BrcTzGM0=+2OzNnF0v zu{IiucgX6Vp%|S2W5{EA8~D^b8b%A}+@HGXJiRM(g304mya`4!UbVlh?XL(!$nwVz3Lv&D~ExnRA}}ag^-bj-U0-- z<>TQnJ=$c8j9uwV5&|s#en3Thei~h5M6vQlUJu3pmQL-NCBUm;Oj!)dOaE{N|dQ6@|y`{L0!70xLVqs6I%W>*_Ul^NdA&jih%k0aXdaVVE1 z>D4k2pA|NPAg{hh4j6UMWJMOhNZ%sGtZy&qjADwnFW*V{W*8XgLXyZwUtp#-g9@!j&u0g zYq_JPAbT>!I8o1N1v<_EUB5<{x1(ItfbDtATXe!o_U1q+S=RHh}_pMO1h#0ZVIZ(LzqATKo zL&K@%M0lj#Sd4*vzNgNxXMfOU50k|>t|#aroE+jUwv?_ucTPlGC{<+@NKCG`%4MIL zN%7#;v;^T6h=ZqC6vS^uBOgN#A_mymt*@j!9N-M#l~&mpX2r3tzR&X$x(q;lRxiPQ z(#{%B6U)@&rnKHcOf8?yNlNd%S-Lr&bf)!a?t!W1lf8!wDVSAwl#OxEqLs^bvXBDZ z$;;CyN}OuRsFvVfLWAldWlP`d&NI&rqgftAI(57UGfCn;9TLub=Xm$F<1izDSiqh3 zpm}L<;L__mL{kb*Aw$YHZ)^)$%r?&hZh8B1R1D~Rnk+v%{R1J^o}a_;HJAI1T=^Xl z3-dSPfBw(^{5yEqe;mdYp9welV`CS}m&OP%3TPp@a5zesv$^-HWwQqWjr<`K|NGGH z1$!)_!#dwXyS!+UabRxWQm)(;n#4cW&lltAl3AOQ=J$Lq%uG2KSZKyixT~*z2IYi3 zpj7xe<$ggn4Jgg(o#lTG{Sv+q;xpF+E)VqD!>_>34nBJz20GNRm4+-xHP zV#IFxL*Wf=qL0D*=db+BEr0igb@>zW_R9wDlN}Xa_}^sIbPaMpyAxvuZ{B;o!&5`gR(k6S1<4FICDp2|tzrEqOY$DBt7kp&8;iPwVun%PB2@f7coYG8G z$3A!Koex3yQ`(k%jp|3pkAacZ$k4DCAGX&6Myhp~jwr;_ep>~>a&jwNdWh*?uW+U& zF-AZx3>ORhteGXKom@eB&3luZxEXfkxp;DPVLxuYfVsA+jA%vFh?4cUAjseOY3Z7r z^*txp80S)9_I8%a;-$z}g@jNIa3l}>{;1txPbVf&O?goZrt=pKw9C&14W($2_dI1x zsSdmk!9cVtFk9Z7xUtl^s?#ksTLmRze%8-BLF&)z`?=R*gLsDVbYeIwJQR}u2^9=t z3$u(3)OAxGuA91l*;R;@?Se=H9l0t0V(uQO+aLLdQr3++2Ip9J%K zNR7<5n0ITH#VmF8>p)iEeFCMEHTcGx&s@GA!Tk`tS8>AAcTSO83d4`vHQlRQ&*iWH zLn)8=wzd5UBtDn%jPT$ij$%!g2&m`|A>2*1*}`Q;qjdkoiz^2!&+W~06{Av5HCa1@ z#r?>r{T=G+_ZhW6tms_duS(MkAd$kGiYHpeIx-i8;un_!P4zF6zJ%I8pL>%Z&LVs4 z?mPfmt%+qIG4B59akTrk@XoQ?JZP?XM9K}9S~q;JYy0eq!jl7h5>EX}KML0#LW?-J z@Rs)BWd&iLGl^sIZ9X-&71I6=5J-2PKkM-Zh^E#*ob7U)SryjCFFH4?rozeO&`ckAN;{h%BQ3D<3QOzeX%e9tQ=rUU!-0$0mwJLE-ibH&$k zeq;|aFy|R=^$BM6wD-Qt{QZ7<&15N`@YNQ5YjkU0+hb9?TXE4ltu44q)xx366eZI2 zRl1|mDCy&L?27wKht0CEdB%<2nU1F1M-TCU$3n62hd8@T`2?cs=~cd=875KnC?eGh7?`tyJFY#q zZxyEbIO@pv%Wif<92PuY)vSt_{WT^NupQbuGL_W3unR~43^`ft3y4pfuPp=BLf+A+ zld%nqJZEj0_^c^QzMn+Y-Dyf!DvwpmEiTeWm&AOzynrH1YoOj4-%qN*%m9ta_ru5f zf-=cGJi^cNrj~%!6b5#K@0fUKhwy~eyByLK`?`JThroHvcISL70fWR3&s(S+g!4o1 z+1%U1wF_B7Rd(nlM^gDYWz88$z5Y_1Ep5JrWrX$fZmcJoini$Q0&Qo-c7*Q^)h|`R zhomF@bph~Cul@qUjd}t$F2w{<3m1U^M^1Ma-pdB-fapdu^xpRlw`$1S+&f zK4kmO0|KHHT+c8v>(0|__wFuh`aTnETJ&ge6O+<{mmf^upT0d#MJ~ioa52t0vyUFt z>s`}16wm$TxVK+7Svk>xM47}=mDGl$JFU-QGnA|x`hO#4xR6emmm4}CpQ8_sG_3xEn8CydB}!4o3VW|XMRgV^Ly(TAQH9~qhyC?S*h2cj$oawt`Z{ha0Fo1) zjLWG7jcW$W6*5$XjEm-n689_%?Q#?L78v@DNZw28cW z0~PA!WIuuY8%W@2d^S!OyxScB4}iits1WW)kZ z;pyMDXd0| zp|*BsPP!8mb<5=#pSD+o@o>z~%sX+Tn;W+-v*mf56mJP-Zkt;(v4xT?^(EFObnrrkkIaXsc}D z4ymvP(IqDlZOCz`W76P}bHvcw6!=pZBn^ zVRsE@W|4frh{c|etuJ&&EU80D!`Au?#QE%?sUVE?7%GL>5)r^nDqDpf3)7}<28$A^ z%XD`M!y9rb1aV!~d))dOv&RJr<@QLV%9G2|NzL}V4QYC9g8df68)>`hqKjZYa%)f_ zJEjZVD#8%0IBldZv=n} zyaTvg*_EF~B^^;#NXtLl3-S zFq1u8=UN(&v@-jCUO_N;*`~k_gzMY33Gm#l9-DyY;k5@4R;!EJEditm#O5t0q#-5+ zNb~fn`>GSXYBYb>07Q!OX{2-BDf4LMooJrlIb*_5o`%aVHKgHmo7T5%A2eGO7 zE1dE(rThBfx6kJ6+vff!BLnEvlP$W7@QsGeu_+K!R6LTzIRc2SaSi94`1LCRDHLwT zzPTnO&@$KW8~o`ke%Y(P<~+OiFWy!0Y#`fiE@X}`d-!U;#NWOIa%RA_M>g)v{$Mc?L~NsrbC8u zcB%X4vB^MI6r&&RL%TEbdYf88APZEt|Eqri!atw6x)()a5`EaoA#_hixXo4(V> zY9x=x<0)gN0xlchSSi2vKmEQB^P{!0`%u#S@Nx>vR=QbUM*d&+-nOreBWV|Z&!?Dh zxK|=ULeCE*u|qJ9*|mWiaI(3L&2Ka_0wqGy(MY!0IOk)XSO0hCi~X;vewgl_(E|uO z_Ac72k!Jd_y1MG>>gwu))<5?5E`O^Y{_~e_N-y6%6K>f0!p(VU*Y+-dJ@^N1z53U0 zC9LoM8;gQ?_r<|a&vt)5`0;rNbaq-tP-=v|X#UT;ZsmVU@UlI^M%?c1Kd=3N^$+&y zxb@@7v(e;)y@$1>Q#BAN!LlBu~G1 z+UrL@`~6->ad^M?eB3L3li!pji}L6EJDPU-s6f`!oeqLYZ#eKSv72>++v~3Pe6mQ0 zUQ-kDkDbnX8-4CQsvb=H{Y|vK*Y)yo)bH->$aZ^QeJ|g7f2@t8$#gvMtOVb#{kev; zNj0(46Z_?>*LW=oKtuNjZ-4g3J-^eB@;Tgkd9jD!q8CpOp6}-b8E&8X$-xQd&B)ms z^e#|oJ~tdigE;ON1HX$ApybP?9)XIY@Zsbt8b604J&`==@mg*>ngrX}CEpJG!h~)S z+>YK)#(sd?1h+@g_4cG67x1dXyjaAO+?HqL!t3oI8c*=AP85tMNg^~mNkA!k-5y#I z#l>_oU4pJ=8jrWF5@b7c-XXjp`sh}FJHGPA(Kcd4qdxp&G+HO7(r%SllsUHnL_U=u zAx}~hy_dtsbXbt)hjOv(q@ylg4X>llIC{sUC5!u^x@^L9nQGAW@a4ATpsK#aG=#*{ zHm9k%a&aAvFQdHua?9JW`Hb>+hvUc(t~@8L=f@;!#5CN*T&0<;A9#QJo1Anw`T>@I z8S|9W?@^Nw1)G#LYnr2>IyFuC+2ud!jaM%o70hUb5O+H}n8>2Dp%T6<<2JwbsCDu) zTSR8&g#gr@Br>7X7Z(sH7Z=6ccColUj%Z%etYfxgK}~cnc{oV{K|F@4Nr+A*YGR z5GPmT;X7{-z4Ojbrh`fEI@*69L?e<{`P|-c+7G<}Aa($5g0k{Gs&zQ_$2VS=dOjJV zKVUTvg3c3CA|yOcFR2BfF@3!lO(z#vl~14~PnE(wk}PL6kY6m|r?;fT zLB>6vT=@;63_*}BZwZyFaKbx(JcvTD91kpbiv}5<22fa9;Zo}rV0fjgd7U|HXQbu+ zY^cGY>S!440aEQJ%86E7fVq2vOE9+_>rECZlQBAKK78brXgqW>lt04YUW^fF8WShK zzWpYDeqQ({|9I!e{htmFKkOeq+1xxokH0xPd5OK0C-gN>DB$2J4(OfjQ{v6@jqU4o zOoolJw^4y0)y2!HK z$1zD`)mb81R$e(&N*qcVO67~Mg-0P==`S(iuA<;~jINwPUap9F*C#xYaLZ;fw}TgX z-L}VGjyWsm;dyT$zHf@j=Ss&Jqm8bs;+A3LUVJ2=(J>L=)bB=lLWQz2;28j9DP3oX zR)|P2EM>}MTC_)_T%CXfH(t;vCdJz*T1f#yw3@aXH4e{Sh=@Wyk29s`D3jJ5?g~KRjAP~KW2KI(y zuQ%~};*rm~p~wPPj=e2?WbMoJ-yVqNdXGG^^Pw?}M%O99?Rt)aq5!z&##`8Tom3M5 z)usB6XUlXyJ&TUU$hs7JgUTwqBnNbdGbgz;gEBh9?Z`t+Mm(VVPGpkYpeohmAm$YH zS}MtC?qbS_i3L3f@HbeF8YEh1#cgGkU1wQ_mq6nzSqY1fTwgpl)^`V9G#;bx!vO2u zF?992E273^d;{P^c(~gegdWwdmfRlJVxDaK4k!hPvLSy8K1m6AY736q4kkjF#GBky zhD=K~^KhszM2zZTMYw$<9BmGLzBe4*jC+??6EDBFNl|1f+HSVCb{vhbp;^ha1Wth2 zBkJ6Emt!B8Kiu-V;|Ltn^#ZJNE}@Y?Cx39`jiPak48zXEht&o2^SuC-ASD(iC?+0u zCjh)yc%TQcpbdi_v_}YW1kV7hCD^XQ;wSdO62AN;6{j1m9Jo&LEAf#BpB-L4Lm$r6Wt-PfDU7|g2g0gkHw;}eL3AMqW)*_ zi8^|E_RH?czISly9iJTi3^&{--umt-eq);aa&Y$S=;fJ*6eqg~z<`_5N{i z_+-o5|K~9Ry`FkUh<5$r`1!#;o*f+SJ%9P+;P598+aliK(V6%B;KczJ4Bpw1M-|GV z4)&4nsrO?4WbYYD-u)4mNu2$f9%ah}y(|uI` z1jQa696mikE&DI_56_CI70Y*i!BeA=~B@?aletEV}kEp&~{I_>@ zaCAuB*gHBrJHgK_^z!6PXZ_{ibbrg+Jvl&7%%8lcCr2-~)`*POG+fPxg18qbv;1A$L(F#%Qu_9rF#A?s3FWHh_)dW)#KWmmzkIPnOl4 z%;PV7>}3q1u@A+I477~bX8B_nAYfgOE=5VwoGx>Dz3WjwB6$#dFvde}5)#GQ0w1~l z*ucpUD1%q8MScaKH~i~P?{Yev#(Pv2Fiu9AQ9S;+H|%R0t|~+quAwP#KRS`$+!3pn zWJ$C^ld13sjYxEzponarSp@bRlKRZTNcNvgy&wv-nr3JTDZ8nqklXSXuc$8228<3m z6F_vxy?{Kw!qP-_6Fm@XASgzeJ9+skM>YtElN=Qfc}4pX(lS{r7R66q!obRv)lP|y z43^-p8Kl^Ch9|!fPDj|yfD$0k3p9WM%l)yP$M4C9Ztp!d9-`DBE{`xxsT5l13VVXvDk{XVtR09&`R@3VwU%LFOI7d1 zP)#tpLBNVeLZ13+Fi11u7q6nAy)L8oTizwCbJUQcG`G!S>BJzM-78@k=54C)A2C3= z0`7o`0#M~jZ(Y#(0#wh7r_eMmC-DnqgG-TR$0I9BLgBAyO7HCSF!v_`Y#eR|s$e{k z)xolJevVZ}j*O%j1PSKUjNo=|ddjrqj@qjY7829aKq z+)WxIDL{1)6}b4B?Sm-e_8pV$Ni|5fQOgJPnP$RQ(xxIB2N7e|#b!riHXKbSM_oi6 zAB3?n6Sy|mwGu)Ub81Zi+h7doPDq3B49b_xZckLFUez~Ou3Y&-+Glo)_%o5)bGyam zyM<$1P5G_skRp>Anvhu1&EyerV!}&2J{TwPe-Bpw?0Fz&Lo&>kLU_eRyym^sL}+_7 zy>)Y7y^?&F;}`m;P#tdff_~(W%?`#t-dHR`eZZuKD0X@x9%l7NyfJ6D?v*RFtnRVI zqWZYuT6eM3BBqoCBq5))%KPxav-e@>omi2{hGP>)?83~Mae}fuy;*M1BzI{AR{xqsq<5b#orJkpWfXe6qWWb64&KhkKj@XmW8X9WS zpSOU7LPo9#3<)r%T{50xIS#)L@kgLP)W##=?Gf+4FkmMrMRbrx7qk(ENk0uH3Y{gb zL6xttVK^dXh;?i3HJP@hptn+4J&*}h%c$Mh_ER+YRuzCs-6kUGxwa1e@dmpx{}pdy zv+_?gfUx&4gkQ!7{E$!(5HkmveR=V}r^88v=b}vBK6)qnp5MeriD)LBLjj+3>JtU` zPC@ZF|ClKLoGg$V8ACu?2~H^qEu1uVp2v^5|Efd`gUQfl%hb$v!H(Dz#UKk&gX+2e zRR5(~$Ve(vyVzwXc~f{q%{zwl0Zb+#MA}+4LFX?#Qkt0R+2o)6EpygeX?OJ4qD87; zG4mA!GVBli@C-{oycGK78XDWkoJ@3; zn+fNET}7#|d5Wc2qC^;Goc9J7oHV~-*0V*^As!b>sD!`N=nHp}YJLq<5D>D9L*}4z zvvBVk?m2|VhXXQU?Re{470eU-rM6Bck9AeqxYPWbbJ1K$+@vLXgl<7JokzFnviBBk z$w%}rst{WSJ}ju$a28aH8oam}PGP#@{e^e18ahn68p1LQXit?DbL(U>hij7F zQzB*Pk7?8~%fxOHc!Acvuv|iU=g9ekVb(_QjwB8gl-Lv(d&4C4-i1&o5TFTSq{N)MQ@$W23u38`cZmC)l6S2?#}5CEMM735>pcn6U;$a`=W-3wm3eY!p zzo6Z}RIMq35M^n{GKYV4EdT|oLXch5g^{_2Bg?qBoKjW?Y?2vOoNfbqJ;3fY6P{~V8rLc%U)otA%B7`M2i=liMbKnM};^; z&V04DLHs0#B8ygQkCPUx?Ihf%Yaqts_8N92Ri@MA)ZED_W~S{C*-B~)R=-^zEoQqu z*1yC4TkIc@wTCN_YDQEm`Okto%E7>+tv$(625K!lBF4n73&4kNDz(+(SMC(M%}{Y# zHc*)k&nnH$K!7YZs!$AWVzo(CK!bd@@)b+WFJCOP8KlD}`zZ?mxU7tKc4(V!kMKW) ztvQg)c1oM)(u$kpkrIry%WGr=eP zRGLh4m|DTfg4o_QV~8!E>`WHG3N;xho(qXe%gA6AsFw>CQ>9BZB(iLCVre9HV3{i` z;{-srg-6;(BPb>aY-88LBUH9W^ps>Zh)R9Y%2|)D7)-Ymg_(uqGX~V z<|}O(MruwF@&UvVvhj4ZiJ_AtAXR-L0F_U_ZH>V4zUj?V_-P#q6r-fkr2F&PU; zM1nyUh`GyVYZeyiO01M6kl88`0!fJ5#~%4(9UPuryx9FGeBjJvG;b0}Ac<=;pineo z`5?57MM=e@P(>^QE$Y+E`wBe+g7@KWa6tz8gd44-f~OoR3nLCU>swp$CPLwPG`M6a zt0~Coh1^5`XaWgBaQk%QMLueNQWlK^ZO&o@Wg$dXCyN?CPH_m?mCiLQ3kp|^0-|W8 z&k6tz(%5S}I{6ijci33n#aFZL&9x%&NRsISkRUscZkV3t)X5ojFDQnJFl2>XJpBpfXEUvJ(pjmXlok zkI9nt9;zV8!$MzFVTeFS0NJ82rG}wlkB!~v{Q4g0TgkH?9qJ)(9Bj)NG}6nu0B7p@ zH|$Ph$Qb4}g>p!rL39p6e;j)G@w6XdLReyxZTl95HjCcr@4e9od5h#q#q$S$HCbs? zmA%W}0Vm(vp3-of%+k)Tea~4{K{~Pi9nr!{>|`c}HM}$z{8(VK3@NcOK#E+-hykH7 z;g`9cT!yWHRz@m!(nbSBxXrW|WKJu^PcliuS)Co?h$Q%-R7%$FDkb1O97z6L4!?Z4 zx|F}gM0LBI^ch-FOFX6)c4aF(3(bOX*u`jxMWYIfWl@co4swh56k`xfei{#_qd1>~ zDcXBgDzyE=zj!J6&5j4th_}7Xe_qk!9dGk7{rACBKmY8#r^Tii4ETj$<5}ot-DUCc zRoNtz)x4y0Vy8>AmIRMW6E%?O&d=;S$pkR)#*`dZSh0Vb%-`s}w6^ntS2dfLJ-eRE3JT{|rDcMnkq{gH=fKaT~tUPAAMl24m}r zq5&2X;w`maLvO%4ftqwf6pZ2rOcl;1RRJ|uRm+95cIC806hhSrXIgS{4=!l9<@a|m z1z6zC2kcI4iV@#{`v;-qY?qg7v;ix6abOjDtY9qc%;e=(9gYB8zbauL-GiTttQj3t z(nUTg>iLlv<>dGXI#RonMS$BBWxi5AH#Av=w!#~H;e7)dSAp`xz(UVpv<*$4HY~ss zN}k>daUKEAgs@-J_Bs2l!piz?IR2dkn)c##Hq}wJAo{(#b}>0vZhKF$B&Q7>bec#c z_8(Z5TfSJ%z632Zg=7;GIoT$VDvXptEcd@LjmZ4Zvnr`WFU7PS#3^u&CxBTx&(_=Q zb!yy|c(<`u#kzM{_cytHDJ>d%*pr9b98plMGbM{{OkhRW8xFcX_M3Cx3M_yNo5ym0 zQ|H%5n!xMyQ8_oT(sLhO`|sI%C*neMO4@dP57L)JdO5N#@57~q0D)+u+UTTKV}DUO z)gz;cQoX_?5S;T_~DCdzU?A8(d$i^<|tV)hm&XDkYw2{{LjVt`)0Rr{=VDYc`YA78yQc}3$MTa z=Fi<8j`gn5hQXR@ut9pXGxvD?JonwUVbh1i67u->=mtJQ;;22$Mv{j!woQ}KGWLvB8Z^Zic{!CpFg#Ca#h#2R2Pp|*P)Q1=Qj%ZXYqz1~Mlv8@ z0?|sAc=U*90mZl`eCn06lVGLqlo=BZV#Ohin!ti*Lret{lK_KMN)g3;_ju`o$SIu~ zEiFTmVBjR~D%Xy|6YC02iiJh)F`xO8X+}{5&GSee-c1_u_z|gjqa5#>@4ZU0)Ti%Y z3a4l+xoAO~^zW3BXFkVkF{6)UIYk9D7LT2mPXO#@6c4h z3)xl%Q_#TQefQlf?|cH@&%Zj)t-pS}nNMa+tHd+Lo-pd+*Eq#_UWNugAD<6C(0}92 zKX(bQ89>dUr)Et~!s^;2-ZLCUzr5`pZdSz}B6BjY7~qwO_`({Uk`Ztg0<(&PjV!vr;w)hk4)$h_W@3Fzh!;{*>#GZNz# zQW#h+)1IYHnWiu}VHqm812dCr%ob$xi#f!Pv}%!>E1}Gibf+HDB2G3?ND#IX(E3f( z#SarAz_}{XA?C28mBqqwH%iSY6U0qA`;&QI&g5rNR5}6;06(u z`)5XD?$}X7FU;)fOlDgrRY-;|gQp-xFt0M_k5YH@CQujdRg))bX3<0<_@gHfi`3-j zbF~4X6|GK5(oFw27U-F#NsU-DN(vY$p0yL$_D0H6N z)+CuI7Zej}ff*uUPt{4G5DZk3R+C_qOw~A(@3J_LrH^7c!W|IT_5#}OiC6YaJa1q~ zrJF7NLt9c<(9yO770#Wt7N%MQ3TmpBz%ox~WFCvn)xQY{2nHpvn`)tjSWZntBji1o zBm1D5h&h>!S$KMWB%6S1=Mkak<{y-^Tpxs`g;({$s^YH6P0t8LNVYT@JIEZZ>wpo33u7>785s5j=?InT|(9Q$A zIT2ig&j`{%<1s`ST7Yk4*UBnapK;qlbRrxenRxIlfSVcCv^b(m#{_ZI>V~t+-#Vla zf*x=b0QfMfvsIWpYV6VNAF?Wx9(4jXD)%Loi9`~q{IAG>r0BtxXrZmKvl#oA>^4hw zG0&fP>o_l-c*-#earT(P#E{qI*kdaiktM{Li-9=X3ptvYV80ThlLQBlEo68wt3u~2 zzpAl^4TR)GaBS5h`-*9Eomfw~pI{6lsuOb(cUJ*g-lNgrcUF^dpg~$Q;Uox~ag33} zP-5#JmW>I{1l^GLV5c{r*i>*H42I+JbcF3GY9$6voZA0hoZe?obTAaJwrE78M-L}@ zm6L6NU%f>om=(y=VE^bjdCJl8>EvX!YPM|0Rn#AKr+sYj6pcKh5k~pkv^iX_z3w+; z`4BmRfI$mX%CkF!y=PHge6}O0dBS zCm5N`^CWz^CKy&>5(g{G1&Q2a+n|LOIm=3EvnR}w*b~(jD7Cd~6hjC?B|!cS`2O(n ziUf!}e?x1gHM^&Sv{ey2V z7vyMAFxS3C3@SneNcFAv%{Lo8B`)RUR9urA#JSh6HhSPeLQ$#5yyKJ;HZ-$A&}G`Y z%_c>)?hh<&@itJ=oN-5SeR3fISShw$RBHhZ|dmJ4q zN~48Ml>)ktZD|nVRgOGoau*l$gZn4_6%y5gtaR4noxNYCNG3*Aj1viuTr`+oi-s_C zV`bVO4&WmO%AqW=fldwQHb~K9xHbi-jFPiZ79`WA7mx;ENegO{MuMdc=M>5+3rlP;%=avX)iTV*Q4Sz7jNNK`CK1AD8DUZgIvQyD zDg8GaZH7fszQ7-a#6oHc;dkd(JGZo?2wQ@XS5K7`P{Q- zKfh4PFgTb{Z*edU;0h}H8frGmn<+3b)nb+S;?ehjY;K##?*4&?8Fb8OnP3625n^kJ zot=!GAQ@BOl{iO3wQ4;^KZGhH?sTe>yCZ6>lD9lulKJpw*1#E932d48d;mEUVR2Rj zrY#v+NoeCl8S0%znUWBYfw*Lw5~luP21G*0917gI#caVwQRx?;v@X9ETq$(nI&k#w zK#%eI4mQ4&a@cNd{LwVOF8&KkMv0f}!W+b@K?k8cAwpemg z#{jDVRc$wjt0GB3c`thq$5t!&HNpzh+pEcB6k}T-sjjA-B6_f$?nQA%8SvA(9S> zqb2&|k9yQ<)h9Sj4iP>!H9p%Y4OdC`x0i0c-Cic;-N=wp5TXFZ(`y=*`9#9CSW~Q` zvgw40S&;EGsid#7h(&2eI2|=J0TyBO?oG2;A!n*LE%4slag$9dgwfmVW}!p~Xj~sF z{&MMvUluZ{SghP}rBUNS4KKDn;?Pnwm6RLo9;KZ;wQ1 zdCppsS?cm{L;h_p>N~~vr4kUkt5GP`3+47%xx7

{Ke>luA1#w~y;;YW8~_3i|8x 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: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

|^5|UT=dBu__a9bFT6+ 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 + "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = :t LIMIT 1" + ); + $stmt->execute([':t' => $table]); + return (bool)$stmt->fetchColumn(); +} + +function qdb_column_exists(PDO $pdo, string $table, string $column): bool { + $stmt = $pdo->query('PRAGMA table_info(' . $table . ')'); + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $col) { + if (($col['name'] ?? '') === $column) { + return true; + } + } + return false; +} + +/** + * Apply incremental schema fixes. Returns true if the temp DB was modified. + */ +function qdb_apply_migrations(PDO $pdo, string $schemaSql): bool { + $changed = false; + + if (!qdb_column_exists($pdo, 'questionnaire', 'categoryKey')) { + $pdo->exec("ALTER TABLE questionnaire ADD COLUMN categoryKey TEXT NOT NULL DEFAULT ''"); + $changed = true; + } + + if (qdb_table_exists($pdo, 'client')) { + if (!qdb_column_exists($pdo, 'client', 'archived')) { + $pdo->exec('ALTER TABLE client ADD COLUMN archived INTEGER NOT NULL DEFAULT 0'); + $changed = true; + } + if (!qdb_column_exists($pdo, 'client', 'archivedAt')) { + $pdo->exec('ALTER TABLE client ADD COLUMN archivedAt INTEGER NOT NULL DEFAULT 0'); + $changed = true; + } + } + + if (qdb_table_exists($pdo, 'questionnaire')) { + if (!qdb_column_exists($pdo, 'questionnaire', 'structureRevision')) { + $pdo->exec('ALTER TABLE questionnaire ADD COLUMN structureRevision INTEGER NOT NULL DEFAULT 1'); + $changed = true; + } + if (!qdb_column_exists($pdo, 'questionnaire', 'structureChangedAt')) { + $pdo->exec('ALTER TABLE questionnaire ADD COLUMN structureChangedAt INTEGER NOT NULL DEFAULT 0'); + $changed = true; + } + } + + if (!qdb_table_exists($pdo, 'questionnaire_structure_snapshot')) { + $pdo->exec(" + CREATE TABLE questionnaire_structure_snapshot ( + questionnaireID TEXT NOT NULL, + structureRevision INTEGER NOT NULL, + createdAt INTEGER NOT NULL, + manifestJson TEXT NOT NULL DEFAULT '{}', + PRIMARY KEY (questionnaireID, structureRevision), + FOREIGN KEY(questionnaireID) REFERENCES questionnaire(questionnaireID) ON DELETE CASCADE + ) + "); + $changed = true; + } + + if (qdb_table_exists($pdo, 'question')) { + if (!qdb_column_exists($pdo, 'question', 'retiredAt')) { + $pdo->exec('ALTER TABLE question ADD COLUMN retiredAt INTEGER NOT NULL DEFAULT 0'); + $changed = true; + } + if (!qdb_column_exists($pdo, 'question', 'retiredInRevision')) { + $pdo->exec('ALTER TABLE question ADD COLUMN retiredInRevision INTEGER NOT NULL DEFAULT 0'); + $changed = true; + } + } + + if (qdb_table_exists($pdo, 'answer_option')) { + if (!qdb_column_exists($pdo, 'answer_option', 'retiredAt')) { + $pdo->exec('ALTER TABLE answer_option ADD COLUMN retiredAt INTEGER NOT NULL DEFAULT 0'); + $changed = true; + } + } + + if (qdb_table_exists($pdo, 'questionnaire_submission')) { + if (!qdb_column_exists($pdo, 'questionnaire_submission', 'structureRevision')) { + $pdo->exec('ALTER TABLE questionnaire_submission ADD COLUMN structureRevision INTEGER NOT NULL DEFAULT 1'); + $changed = true; + } + if (!qdb_column_exists($pdo, 'questionnaire_submission', 'structureSnapshotJson')) { + $pdo->exec("ALTER TABLE questionnaire_submission ADD COLUMN structureSnapshotJson TEXT NOT NULL DEFAULT '{}'"); + $changed = true; + } + } + + if (qdb_table_exists($pdo, 'questionnaire_structure_snapshot') + && qdb_table_exists($pdo, 'questionnaire')) { + require_once __DIR__ . '/lib/questionnaire_structure.php'; + if (qdb_backfill_structure_snapshots($pdo)) { + $changed = true; + } + } + + if (!qdb_table_exists($pdo, 'questionnaire_submission')) { + $pdo->exec(" + CREATE TABLE questionnaire_submission ( + submissionID TEXT NOT NULL PRIMARY KEY, + clientCode TEXT NOT NULL, + questionnaireID TEXT NOT NULL, + version INTEGER NOT NULL, + submittedAt INTEGER NOT NULL, + submittedByUserID TEXT NOT NULL DEFAULT '', + submittedByRole TEXT NOT NULL DEFAULT '', + assignedByCoach TEXT, + status TEXT NOT NULL DEFAULT '', + startedAt INTEGER, + completedAt INTEGER, + sumPoints INTEGER, + FOREIGN KEY(clientCode) REFERENCES client(clientCode), + FOREIGN KEY(questionnaireID) REFERENCES questionnaire(questionnaireID), + FOREIGN KEY(assignedByCoach) REFERENCES coach(coachID), + UNIQUE(clientCode, questionnaireID, version) + ) + "); + $pdo->exec('CREATE INDEX IF NOT EXISTS idx_submission_client_qn ON questionnaire_submission(clientCode, questionnaireID)'); + $pdo->exec('CREATE INDEX IF NOT EXISTS idx_submission_client_time ON questionnaire_submission(clientCode, submittedAt)'); + $changed = true; + } + + if (!qdb_table_exists($pdo, 'client_answer_submission')) { + $pdo->exec(" + CREATE TABLE client_answer_submission ( + submissionID TEXT NOT NULL, + questionID TEXT NOT NULL, + answerOptionID TEXT, + freeTextValue TEXT, + numericValue REAL, + answeredAt INTEGER, + PRIMARY KEY (submissionID, questionID), + FOREIGN KEY(submissionID) REFERENCES questionnaire_submission(submissionID) ON DELETE CASCADE, + FOREIGN KEY(questionID) REFERENCES question(questionID), + FOREIGN KEY(answerOptionID) REFERENCES answer_option(answerOptionID) + ) + "); + $changed = true; + } + + if (!qdb_table_exists($pdo, 'client_followup_note')) { + $pdo->exec(" + CREATE TABLE client_followup_note ( + clientCode TEXT NOT NULL PRIMARY KEY, + note TEXT NOT NULL DEFAULT '', + updatedByUserID TEXT NOT NULL DEFAULT '', + updatedAt INTEGER NOT NULL, + FOREIGN KEY(clientCode) REFERENCES client(clientCode) + ) + "); + $changed = true; + } + + if (!qdb_table_exists($pdo, 'session')) { + $pdo->exec(" + CREATE TABLE session ( + token TEXT NOT NULL PRIMARY KEY, + userID TEXT NOT NULL, + role TEXT NOT NULL, + entityID TEXT NOT NULL DEFAULT '', + createdAt INTEGER NOT NULL, + expiresAt INTEGER NOT NULL, + temp INTEGER NOT NULL DEFAULT 0 + ) + "); + $changed = true; + } + + if (!qdb_table_exists($pdo, 'system_setting')) { + $pdo->exec(" + CREATE TABLE system_setting ( + settingKey TEXT NOT NULL PRIMARY KEY, + settingValue TEXT NOT NULL DEFAULT '' + ) + "); + $changed = true; + } + + if (!qdb_table_exists($pdo, 'question_score_rule')) { + $pdo->exec(" + CREATE TABLE question_score_rule ( + ruleID TEXT NOT NULL PRIMARY KEY, + questionID TEXT NOT NULL, + scopeKey TEXT NOT NULL DEFAULT '', + levelKey TEXT NOT NULL, + points INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY(questionID) REFERENCES question(questionID) ON DELETE CASCADE, + UNIQUE(questionID, scopeKey, levelKey) + ) + "); + $pdo->exec('CREATE INDEX IF NOT EXISTS idx_score_rule_question ON question_score_rule(questionID)'); + $changed = true; + } + + if (!qdb_table_exists($pdo, 'scoring_profile')) { + $pdo->exec(" + CREATE TABLE scoring_profile ( + profileID TEXT NOT NULL PRIMARY KEY, + name TEXT NOT NULL DEFAULT '', + description TEXT NOT NULL DEFAULT '', + isActive INTEGER NOT NULL DEFAULT 1, + greenMin INTEGER NOT NULL DEFAULT 0, + greenMax INTEGER NOT NULL DEFAULT 12, + yellowMin INTEGER NOT NULL DEFAULT 13, + yellowMax INTEGER NOT NULL DEFAULT 36, + redMin INTEGER NOT NULL DEFAULT 37, + createdAt INTEGER NOT NULL DEFAULT 0, + updatedAt INTEGER NOT NULL DEFAULT 0 + ) + "); + $changed = true; + } + + if (!qdb_table_exists($pdo, 'scoring_profile_questionnaire')) { + $pdo->exec(" + CREATE TABLE scoring_profile_questionnaire ( + profileID TEXT NOT NULL, + questionnaireID TEXT NOT NULL, + weight REAL NOT NULL DEFAULT 1.0, + orderIndex INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY (profileID, questionnaireID), + FOREIGN KEY(profileID) REFERENCES scoring_profile(profileID) ON DELETE CASCADE, + FOREIGN KEY(questionnaireID) REFERENCES questionnaire(questionnaireID) ON DELETE CASCADE + ) + "); + $changed = true; + } + + if (!qdb_table_exists($pdo, 'client_scoring_profile_result')) { + $pdo->exec(" + CREATE TABLE client_scoring_profile_result ( + clientCode TEXT NOT NULL, + profileID TEXT NOT NULL, + weightedTotal REAL NOT NULL DEFAULT 0, + band TEXT NOT NULL DEFAULT '', + computedAt INTEGER NOT NULL DEFAULT 0, + questionnaireSnapshot TEXT NOT NULL DEFAULT '{}', + coachBand TEXT NOT NULL DEFAULT '', + coachReviewedAt INTEGER NOT NULL DEFAULT 0, + coachReviewedByUserID TEXT NOT NULL DEFAULT '', + PRIMARY KEY (clientCode, profileID), + FOREIGN KEY(clientCode) REFERENCES client(clientCode) ON DELETE CASCADE, + FOREIGN KEY(profileID) REFERENCES scoring_profile(profileID) ON DELETE CASCADE + ) + "); + $pdo->exec('CREATE INDEX IF NOT EXISTS idx_client_profile_result_profile ON client_scoring_profile_result(profileID)'); + $changed = true; + } + + if (qdb_table_exists($pdo, 'client_scoring_profile_result')) { + if (!qdb_column_exists($pdo, 'client_scoring_profile_result', 'coachBand')) { + $pdo->exec("ALTER TABLE client_scoring_profile_result ADD COLUMN coachBand TEXT NOT NULL DEFAULT ''"); + $changed = true; + } + if (!qdb_column_exists($pdo, 'client_scoring_profile_result', 'coachReviewedAt')) { + $pdo->exec('ALTER TABLE client_scoring_profile_result ADD COLUMN coachReviewedAt INTEGER NOT NULL DEFAULT 0'); + $changed = true; + } + if (!qdb_column_exists($pdo, 'client_scoring_profile_result', 'coachReviewedByUserID')) { + $pdo->exec("ALTER TABLE client_scoring_profile_result ADD COLUMN coachReviewedByUserID TEXT NOT NULL DEFAULT ''"); + $changed = true; + } + } + + if (qdb_table_exists($pdo, 'questionnaire_submission')) { + require_once __DIR__ . '/lib/submissions.php'; + if (qdb_backfill_submissions_from_live($pdo)) { + $changed = true; + } + } + + $currentVersion = (int)$pdo->query('PRAGMA user_version')->fetchColumn(); + if ($currentVersion < 10 + && qdb_table_exists($pdo, 'completed_questionnaire') + && qdb_table_exists($pdo, 'client_scoring_profile_result')) { + require_once __DIR__ . '/lib/scoring.php'; + if (qdb_recompute_all_questionnaire_scores($pdo) > 0) { + $changed = true; + } + } + + $currentVersion = (int)$pdo->query('PRAGMA user_version')->fetchColumn(); + if ($currentVersion < 7 && qdb_table_exists($pdo, 'question_score_rule')) { + require_once __DIR__ . '/lib/scoring.php'; + if (qdb_backfill_glass_score_rules($pdo) > 0) { + $changed = true; + } + if (qdb_recompute_all_questionnaire_scores($pdo) > 0) { + $changed = true; + } + if (qdb_seed_default_rhs_scoring_profile($pdo) !== null) { + $changed = true; + } + } + + $currentVersion = (int)$pdo->query('PRAGMA user_version')->fetchColumn(); + if ($currentVersion < 8 && qdb_table_exists($pdo, 'scoring_profile')) { + if (!qdb_column_exists($pdo, 'scoring_profile', 'greenMin')) { + $pdo->exec('ALTER TABLE scoring_profile ADD COLUMN greenMin INTEGER NOT NULL DEFAULT 0'); + $changed = true; + } + if (!qdb_column_exists($pdo, 'scoring_profile', 'yellowMin')) { + $pdo->exec('ALTER TABLE scoring_profile ADD COLUMN yellowMin INTEGER NOT NULL DEFAULT 13'); + $changed = true; + } + if (!qdb_column_exists($pdo, 'scoring_profile', 'redMin')) { + $pdo->exec('ALTER TABLE scoring_profile ADD COLUMN redMin INTEGER NOT NULL DEFAULT 37'); + $changed = true; + } + $pdo->exec( + 'UPDATE scoring_profile SET + greenMin = COALESCE(greenMin, 0), + yellowMin = greenMax + 1, + redMin = yellowMax + 1' + ); + $changed = true; + } + + $currentVersion = (int)$pdo->query('PRAGMA user_version')->fetchColumn(); + if ($currentVersion < 6 && qdb_table_exists($pdo, 'questionnaire_submission')) { + $pdo->exec( + 'UPDATE questionnaire_submission SET submittedAt = completedAt + WHERE completedAt IS NOT NULL AND submittedAt != completedAt' + ); + $changed = true; + } + + $currentVersion = (int)$pdo->query('PRAGMA user_version')->fetchColumn(); + if ($currentVersion < QDB_VERSION) { + $pdo->exec('PRAGMA foreign_keys = OFF;'); + $pdo->exec($schemaSql); + if (!qdb_column_exists($pdo, 'questionnaire', 'categoryKey')) { + $pdo->exec("ALTER TABLE questionnaire ADD COLUMN categoryKey TEXT NOT NULL DEFAULT ''"); + } + $pdo->exec('PRAGMA user_version = ' . QDB_VERSION . ';'); + $pdo->exec('PRAGMA foreign_keys = ON;'); + $changed = true; + } + + return $changed; +} + +/** + * Exclusive lock with short retries (non-blocking attempts). Avoids hung PHP workers + * when two writes overlap (app upload + website edit). + */ +function qdb_flock_exclusive_with_retry($lockFp, int $maxAttempts = 12, int $sleepMicros = 75000): void +{ + for ($attempt = 0; $attempt < $maxAttempts; $attempt++) { + if (flock($lockFp, LOCK_EX | LOCK_NB)) { + return; + } + if ($attempt < $maxAttempts - 1) { + usleep($sleepMicros); + } + } + throw new Exception('Could not acquire lock'); +} + +function qdb_open_writable_lock() +{ + $lockFp = fopen(QDB_LOCK, 'c'); + if ($lockFp === false) { + throw new Exception('Could not open lock file'); + } + qdb_flock_exclusive_with_retry($lockFp); + return $lockFp; +} + +function qdb_acquire_lock() { + return qdb_open_writable_lock(); +} + +/** + * Decrypt the master DB file into a temp SQLite file, or create a fresh one + * from schema.sql if no DB exists yet. + * + * Returns [PDO $pdo, string $tmpPath, resource|null $lockFp]. + * If $writable is true, an exclusive lock is held -- caller MUST call + * qdb_save() or qdb_discard() afterwards. + */ +function qdb_open(bool $writable = false): array { + if (!$writable) { + require_once __DIR__ . '/lib/read_db_cache.php'; + return qdb_read_db_open(); + } + + $dbPath = QDB_PATH; + + if (!is_dir(dirname($dbPath))) { + if (!mkdir(dirname($dbPath), 0755, true) && !is_dir(dirname($dbPath))) { + throw new Exception("Could not create uploads directory"); + } + } + + $lockFp = null; + if ($writable) { + $lockFp = qdb_open_writable_lock(); + } + + $tmpDb = tempnam(sys_get_temp_dir(), 'qdb_'); + $masterKey = get_master_key_bytes(); + + $sql = file_get_contents(QDB_SCHEMA); + if ($sql === false) { + if ($lockFp) { flock($lockFp, LOCK_UN); fclose($lockFp); } + throw new Exception("Could not read schema.sql"); + } + + if (file_exists($dbPath) && is_file($dbPath)) { + $storedEnc = @file_get_contents($dbPath); + if ($storedEnc === false) { + if ($lockFp) { flock($lockFp, LOCK_UN); fclose($lockFp); } + throw new Exception("Could not read stored DB"); + } + try { + $decrypted = aes256_cbc_decrypt_bytes($storedEnc, $masterKey); + } catch (Throwable $e) { + error_log(sprintf( + 'qdb_open decrypt failed: path=%s realpath=%s enc_bytes=%d key_sha=%s sapi=%s env_readable=%s common=%s', + $dbPath, + realpath($dbPath) ?: 'none', + strlen($storedEnc), + hash('sha256', $masterKey), + PHP_SAPI, + is_readable(__DIR__ . '/.env') ? 'yes' : 'no', + realpath(__DIR__ . '/common.php') ?: __DIR__ . '/common.php' + )); + throw $e; + } + if (file_put_contents($tmpDb, $decrypted) === false) { + if ($lockFp) { flock($lockFp, LOCK_UN); fclose($lockFp); } + throw new Exception("Could not write temp DB"); + } + } else { + $pdo = new PDO("sqlite:$tmpDb"); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $pdo->exec($sql); + $pdo->exec("PRAGMA user_version = " . QDB_VERSION . ";"); + $pdo = null; + } + + $pdo = new PDO("sqlite:$tmpDb"); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $pdo->exec("PRAGMA foreign_keys = ON;"); + + $schemaChanged = qdb_apply_migrations($pdo, $sql); + + // Read-only opens discard the temp file; persist one-off migrations to the master DB. + if ($schemaChanged && !$writable) { + $migrateLock = qdb_acquire_lock(); + try { + qdb_save($tmpDb, $migrateLock); + } catch (Throwable $e) { + @unlink($tmpDb); + flock($migrateLock, LOCK_UN); + fclose($migrateLock); + throw $e; + } + return qdb_open(false); + } + + return [$pdo, $tmpDb, $lockFp]; +} + +/** + * Encrypt the temp DB and atomically write it back to the master path. + * Releases the lock. + */ +function qdb_save(string $tmpDb, $lockFp): void { + $masterKey = get_master_key_bytes(); + try { + $ck = new PDO('sqlite:' . $tmpDb); + $ck->exec('PRAGMA wal_checkpoint(FULL);'); + } catch (Throwable $e) { + // best-effort before reading plaintext bytes + } + $plainDb = file_get_contents($tmpDb); + if ($plainDb === false) throw new Exception("Could not read temp DB for save"); + $enc = aes256_cbc_encrypt_bytes($plainDb, $masterKey); + + $dbPath = QDB_PATH; + $tmpEncrypted = tempnam(dirname($dbPath), 'enc_qdb_'); + if (file_put_contents($tmpEncrypted, $enc) === false) { + throw new Exception("Could not write encrypted DB"); + } + if (!@rename($tmpEncrypted, $dbPath)) { + if (!@copy($tmpEncrypted, $dbPath) || !@unlink($tmpEncrypted)) { + throw new Exception("Could not save encrypted DB (rename/copy failed)"); + } + } + @chmod($dbPath, 0644); + @unlink($tmpDb); + + if ($lockFp) { + flock($lockFp, LOCK_UN); + fclose($lockFp); + } + + require_once __DIR__ . '/lib/read_db_cache.php'; + qdb_read_cache_invalidate(); +} + +/** + * Discard changes -- clean up temp file and release lock without saving. + */ +function qdb_discard(string $tmpDb, $lockFp): void { + if (defined('QDB_READONLY_CACHE_HANDLE') && $tmpDb === QDB_READONLY_CACHE_HANDLE) { + return; + } + @unlink($tmpDb); + if ($lockFp) { + @flock($lockFp, LOCK_UN); + @fclose($lockFp); + } +} + +/** + * Open a read-only DB connection; on failure logs and returns a JSON error response. + * + * @return array{0: PDO, 1: string, 2: resource|null}|null + */ +/** + * @return array{0: PDO, 1: string, 2: resource|null} + */ +function qdb_open_read_or_fail(): array { + try { + return qdb_open(false); + } catch (Throwable $e) { + require_once __DIR__ . '/lib/errors.php'; + qdb_emit_api_error($e, 'Database read'); + } +} + +/** + * @return array{0: PDO, 1: string, 2: resource|null} + */ +function qdb_open_write_or_fail(): array { + try { + return qdb_open(true); + } catch (Throwable $e) { + require_once __DIR__ . '/lib/errors.php'; + qdb_emit_api_error($e, 'Database write'); + } +} diff --git a/handlers/activity_log.php b/handlers/activity_log.php new file mode 100644 index 0000000..8288664 --- /dev/null +++ b/handlers/activity_log.php @@ -0,0 +1,21 @@ + $clients]); + } + + qdb_discard($tmpDb, $lockFp); + json_error('BAD_REQUEST', 'Unknown query', 400); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Analytics', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'PUT': + $body = read_json_body(); + $clientCode = trim((string)($body['clientCode'] ?? '')); + $note = (string)($body['note'] ?? ''); + + try { + $opened = qdb_open_write_or_fail(); + [$pdo, $tmpDb, $lockFp] = $opened; + qdb_analytics_set_followup_note($pdo, $tokenRec, $clientCode, $note); + qdb_save($tmpDb, $lockFp); + json_success(['clientCode' => $clientCode, 'note' => $note]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Save follow-up note', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +default: + json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); +} diff --git a/handlers/answer_options.php b/handlers/answer_options.php new file mode 100644 index 0000000..0065a9c --- /dev/null +++ b/handlers/answer_options.php @@ -0,0 +1,217 @@ +prepare('SELECT answerOptionID, questionID, defaultText, points, orderIndex, nextQuestionId FROM answer_option WHERE questionID = :qid ORDER BY orderIndex'); + $stmt->execute([':qid' => $qID]); + $options = $stmt->fetchAll(PDO::FETCH_ASSOC); + foreach ($options as &$o) { + $o['points'] = (int)$o['points']; + $o['orderIndex'] = (int)$o['orderIndex']; + $o['optionKey'] = qdb_option_key($o); + $o['labelGerman'] = qdb_option_german_label($pdo, $o['answerOptionID'], $o['defaultText']); + $tr = $pdo->prepare('SELECT languageCode, text FROM answer_option_translation WHERE answerOptionID = :id'); + $tr->execute([':id' => $o['answerOptionID']]); + $o['translations'] = $tr->fetchAll(PDO::FETCH_ASSOC); + } + unset($o); + qdb_discard($tmpDb, $lockFp); + json_success(['answerOptions' => $options]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Load answer options', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'POST': + require_role(['admin', 'supervisor'], $tokenRec); + $body = read_json_body(); + if (empty($body['questionID']) || empty($body['optionKey']) || !isset($body['defaultText'])) { + json_error('MISSING_FIELDS', 'questionID, optionKey, and defaultText (German label) required', 400); + } + $id = bin2hex(random_bytes(16)); + $qID = $body['questionID']; + $optKey = qdb_validate_stable_key((string)$body['optionKey'], 'Option key'); + $text = trim($body['defaultText']); + qdb_require_non_empty_german($text, 'German label'); + $points = (int)($body['points'] ?? 0); + $order = (int)($body['orderIndex'] ?? 0); + $nextQ = trim($body['nextQuestionId'] ?? ''); + + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + try { + $chk = $pdo->prepare('SELECT questionnaireID FROM question WHERE questionID = :id'); + $chk->execute([':id' => $qID]); + $qnID = $chk->fetchColumn(); + if (!$qnID) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Question not found', 404); + } + $qnID = (string)$qnID; + if ($order === 0) { + $max = $pdo->prepare('SELECT COALESCE(MAX(orderIndex),0)+1 FROM answer_option WHERE questionID = :id'); + $max->execute([':id' => $qID]); + $order = (int)$max->fetchColumn(); + } + qdb_assert_unique_option_key($pdo, $qID, $optKey); + $pdo->prepare('INSERT INTO answer_option (answerOptionID, questionID, defaultText, points, orderIndex, nextQuestionId) VALUES (:id, :qid, :t, :p, :o, :nq)') + ->execute([':id' => $id, ':qid' => $qID, ':t' => $optKey, ':p' => $points, ':o' => $order, ':nq' => $nextQ]); + qdb_upsert_source_translation($pdo, 'answer_option', $id, $text); + $newRev = qdb_bump_structure_revision($pdo, $qnID, 'option_added'); + qdb_save($tmpDb, $lockFp); + json_success([ + 'structureRevision' => $newRev, + 'answerOption' => [ + 'answerOptionID' => $id, + 'questionID' => $qID, + 'optionKey' => $optKey, + 'defaultText' => $optKey, + 'labelGerman' => $text, + 'points' => $points, + 'orderIndex' => $order, + 'nextQuestionId' => $nextQ, + 'translations' => [], + ]]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Answer options', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'PUT': + require_role(['admin', 'supervisor'], $tokenRec); + $body = read_json_body(); + if (empty($body['answerOptionID'])) { + json_error('MISSING_FIELDS', 'answerOptionID is required', 400); + } + $id = $body['answerOptionID']; + + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + try { + $existing = $pdo->prepare('SELECT * FROM answer_option WHERE answerOptionID = :id'); + $existing->execute([':id' => $id]); + $row = $existing->fetch(PDO::FETCH_ASSOC); + if (!$row) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Answer option not found', 404); + } + $optKey = isset($body['optionKey']) + ? qdb_validate_stable_key((string)$body['optionKey'], 'Option key') + : qdb_option_key($row); + if ($optKey === '') { + json_error('MISSING_FIELDS', 'optionKey is required', 400); + } + $labelGerman = trim($body['defaultText'] ?? $body['labelGerman'] ?? ''); + if ($labelGerman === '') { + $labelGerman = qdb_option_german_label($pdo, $id, $row['defaultText']); + } + qdb_require_non_empty_german($labelGerman, 'German label'); + qdb_assert_unique_option_key($pdo, $row['questionID'], $optKey, $id); + $points = (int)($body['points'] ?? $row['points']); + $order = (int)($body['orderIndex'] ?? $row['orderIndex']); + $nextQ = trim($body['nextQuestionId'] ?? $row['nextQuestionId']); + + $keyChanged = $optKey !== qdb_option_key($row); + $pdo->prepare('UPDATE answer_option SET defaultText = :t, points = :p, orderIndex = :o, nextQuestionId = :nq WHERE answerOptionID = :id') + ->execute([':t' => $optKey, ':p' => $points, ':o' => $order, ':nq' => $nextQ, ':id' => $id]); + qdb_upsert_source_translation($pdo, 'answer_option', $id, $labelGerman); + $qnID = qdb_questionnaire_id_for_question($pdo, (string)$row['questionID']); + $newRev = null; + if ($keyChanged && $qnID !== null) { + $newRev = qdb_bump_structure_revision($pdo, $qnID, 'option_key_changed'); + } + qdb_save($tmpDb, $lockFp); + json_success([ + 'structureRevision' => $newRev ?? ($qnID !== null ? qdb_questionnaire_structure_revision($pdo, $qnID) : 1), + 'answerOption' => [ + 'answerOptionID' => $id, + 'questionID' => $row['questionID'], + 'optionKey' => $optKey, + 'defaultText' => $optKey, + 'labelGerman' => $labelGerman, + 'points' => $points, + 'orderIndex' => $order, + 'nextQuestionId' => $nextQ, + ]]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Answer options', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'DELETE': + require_role(['admin', 'supervisor'], $tokenRec); + $body = read_json_body(); + if (empty($body['answerOptionID'])) { + json_error('MISSING_FIELDS', 'answerOptionID is required', 400); + } + $id = $body['answerOptionID']; + + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + try { + $existing = $pdo->prepare('SELECT questionID FROM answer_option WHERE answerOptionID = :id'); + $existing->execute([':id' => $id]); + $qid = $existing->fetchColumn(); + if (!$qid) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Answer option not found', 404); + } + $qnID = qdb_questionnaire_id_for_question($pdo, (string)$qid); + if ($qnID === null) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Question not found', 404); + } + $pdo->beginTransaction(); + $newRev = qdb_bump_structure_revision($pdo, $qnID, 'option_removed'); + if (qdb_option_has_client_data($pdo, $id)) { + $pdo->prepare('UPDATE answer_option SET retiredAt = :ts WHERE answerOptionID = :id') + ->execute([':ts' => time(), ':id' => $id]); + $pdo->commit(); + qdb_save($tmpDb, $lockFp); + json_success(['deleted' => false, 'retired' => true, 'structureRevision' => $newRev]); + break; + } + $pdo->exec('PRAGMA foreign_keys = OFF;'); + $pdo->prepare('DELETE FROM answer_option_translation WHERE answerOptionID = :id')->execute([':id' => $id]); + $pdo->prepare('DELETE FROM answer_option WHERE answerOptionID = :id')->execute([':id' => $id]); + $pdo->exec('PRAGMA foreign_keys = ON;'); + $pdo->commit(); + qdb_save($tmpDb, $lockFp); + json_success(['deleted' => true, 'retired' => false, 'structureRevision' => $newRev]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Delete answer option', $pdo ?? null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'PATCH': + require_role(['admin', 'supervisor'], $tokenRec); + $body = read_json_body(); + if (empty($body['questionID']) || !is_array($body['order'] ?? null)) { + json_error('MISSING_FIELDS', 'questionID and order[] required', 400); + } + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + try { + $stmt = $pdo->prepare('UPDATE answer_option SET orderIndex = :o WHERE answerOptionID = :id AND questionID = :qid'); + foreach ($body['order'] as $idx => $aoid) { + $stmt->execute([':o' => $idx, ':id' => $aoid, ':qid' => $body['questionID']]); + } + qdb_save($tmpDb, $lockFp); + json_success(['reordered' => true]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Answer options', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +default: + json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); +} diff --git a/handlers/app_questionnaires.php b/handlers/app_questionnaires.php new file mode 100644 index 0000000..afa655f --- /dev/null +++ b/handlers/app_questionnaires.php @@ -0,0 +1,626 @@ + ordered questionnaire list with conditions + * GET ?id= -> single questionnaire in app JSON format + * GET ?translations=1 -> global app UI strings only (not questionnaire content); **no auth** (login screen). + * GET ?clients=1 -> list of clients assigned to the authenticated coach + * GET ?clientCode=X&answers=1 -> all completed questionnaire answers for one client (encrypted) + * GET ?answersBulk=1 -> all assigned clients' answers in one payload (coach, encrypted) + * GET ?scoringProfiles=1 -> active scoring profile definitions (encrypted) + * GET ?scoringReview=1 -> coach review state (coachBand only; computed on device) + * POST action=coachScoringReview -> coach agrees with or overrides calculated band (encrypted) + * POST -> submit interview answers for a client (coach / supervisor / admin). + * Re-posting the same clientCode + questionnaireID updates answers in place (re-edit). + * Sensitive POST/GET ?clients=1 bodies use encrypted payloads (HKDF from Bearer token). + */ + +// Public app UI catalog for the login screen (no token, no PII). +if ($method === 'GET' && !empty($_GET['translations'])) { + $opened = qdb_open_read_or_fail(); + [$pdo, $tmpDb, $lockFp] = $opened; + json_success(['translations' => qdb_build_app_translations_map($pdo)]); + qdb_discard($tmpDb, $lockFp); + return; +} + +$tokenRec = require_valid_token(); + +if ($method === 'POST') { + require_role(['admin', 'supervisor', 'coach'], $tokenRec); + + $body = read_encrypted_json_body($tokenRec['_token']); + + if (($body['action'] ?? '') === 'coachScoringReview') { + require_fields($body, ['clientCode', 'profileID', 'coachBand']); + $clientCode = trim((string)$body['clientCode']); + $profileID = trim((string)$body['profileID']); + $coachBand = trim((string)($body['coachBand'] ?? '')); + $calculatedBand = trim((string)($body['calculatedBand'] ?? '')); + $weightedTotal = isset($body['weightedTotal']) ? (float)$body['weightedTotal'] : null; + + try { + $opened = qdb_open_write_or_fail(); + [$pdo, $tmpDb, $lockFp] = $opened; + + [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl'); + $clStmt = $pdo->prepare( + "SELECT cl.clientCode FROM client cl WHERE cl.clientCode = :cc AND ($rbacClause)" + ); + $clStmt->execute(array_merge([':cc' => $clientCode], $rbacParams)); + if (!$clStmt->fetch()) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Client not found or not authorized', 404); + } + + require_once __DIR__ . '/../lib/scoring.php'; + if (!qdb_is_valid_scoring_band($coachBand)) { + qdb_discard($tmpDb, $lockFp); + json_error('INVALID_FIELD', 'coachBand must be green, yellow, or red', 400); + } + + $profile = qdb_save_coach_scoring_review( + $pdo, + $clientCode, + $profileID, + $coachBand, + (string)($tokenRec['userID'] ?? ''), + $weightedTotal, + $calculatedBand !== '' ? $calculatedBand : null, + ); + + qdb_save($tmpDb, $lockFp); + json_success(['profile' => $profile]); + } catch (InvalidArgumentException $e) { + qdb_discard($tmpDb ?? null, $lockFp ?? null); + json_error('INVALID_FIELD', $e->getMessage(), 400); + } catch (RuntimeException $e) { + qdb_discard($tmpDb ?? null, $lockFp ?? null); + json_error('NOT_FOUND', $e->getMessage(), 404); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Coach scoring review', $pdo ?? null, $tmpDb ?? null, $lockFp ?? null); + } + } + + require_fields($body, ['questionnaireID', 'clientCode', 'answers']); + + $qnID = trim($body['questionnaireID']); + $clientCode = trim($body['clientCode']); + $answers = $body['answers']; + + if (!is_array($answers)) { + json_error('INVALID_FIELD', 'answers must be an array', 400); + } + + $startedAt = isset($body['startedAt']) ? (int)$body['startedAt'] : null; + $completedAt = isset($body['completedAt']) ? (int)$body['completedAt'] : null; + + try { + $opened = qdb_open_write_or_fail(); + [$pdo, $tmpDb, $lockFp] = $opened; + + require_once __DIR__ . '/../lib/questionnaire_structure.php'; + + $qnStmt = $pdo->prepare( + 'SELECT questionnaireID, COALESCE(structureRevision, 1) AS structureRevision + FROM questionnaire WHERE questionnaireID = :id' + ); + $qnStmt->execute([':id' => $qnID]); + $qnRow = $qnStmt->fetch(PDO::FETCH_ASSOC); + if (!$qnRow) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Questionnaire not found', 404); + } + $currentRev = max(1, (int)$qnRow['structureRevision']); + $submitRev = max(1, (int)($body['structureRevision'] ?? 1)); + if ($submitRev > $currentRev) { + qdb_discard($tmpDb, $lockFp); + json_error('STRUCTURE_REVISION_NEWER', 'App structure revision is newer than server', 400); + } + + [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl'); + $clStmt = $pdo->prepare( + "SELECT cl.clientCode, cl.coachID FROM client cl WHERE cl.clientCode = :cc AND ($rbacClause)" + ); + $clStmt->execute(array_merge([':cc' => $clientCode], $rbacParams)); + $clientRow = $clStmt->fetch(PDO::FETCH_ASSOC); + if (!$clientRow) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Client not found or not authorized', 404); + } + + $assignedByCoach = ($tokenRec['role'] === 'coach') + ? ($tokenRec['entityID'] ?? '') + : ($clientRow['coachID'] ?? ''); + + $isLegacySubmit = $submitRev < $currentRev; + if ($isLegacySubmit) { + $submitManifest = qdb_load_structure_manifest($pdo, $qnID, $submitRev); + if ($submitManifest === null) { + qdb_discard($tmpDb, $lockFp); + json_error('STRUCTURE_REVISION_UNKNOWN', 'Unknown structure revision', 400); + } + } else { + $submitManifest = qdb_build_structure_manifest($pdo, $qnID, false); + $submitManifest['structureRevision'] = $currentRev; + } + + $maps = qdb_submit_maps_from_manifest($pdo, $qnID, $submitManifest); + $shortIdMap = $maps['shortIdMap']; + $shortIdToType = $maps['shortIdToType']; + $optionMap = $maps['optionMap']; + $symptomParentMap = $maps['symptomParentMap']; + $freeTextMaxLen = $maps['freeTextMaxLen']; + + require_once __DIR__ . '/../lib/app_submit_validate.php'; + $validationErrors = qdb_validate_app_submit_payload( + $pdo, + $qnID, + $answers, + $shortIdMap, + $shortIdToType, + $symptomParentMap, + $optionMap, + $submitManifest + ); + if ($validationErrors !== []) { + qdb_discard($tmpDb, $lockFp); + json_error( + 'VALIDATION_FAILED', + 'Some answers are invalid or missing', + 400, + ['errors' => $validationErrors] + ); + } + + $pdo->beginTransaction(); + + $glassByParent = []; + $submittedQuestionIds = []; + $answerInsert = $pdo->prepare(" + INSERT INTO client_answer (clientCode, questionID, answerOptionID, freeTextValue, numericValue, answeredAt) + VALUES (:cc, :qid, :aoid, :ftv, :nv, :at) + ON CONFLICT(clientCode, questionID) DO UPDATE SET + answerOptionID = excluded.answerOptionID, + freeTextValue = excluded.freeTextValue, + numericValue = excluded.numericValue, + answeredAt = excluded.answeredAt + "); + $answerDelete = $pdo->prepare( + 'DELETE FROM client_answer WHERE clientCode = :cc AND questionID = :qid' + ); + + require_once __DIR__ . '/../lib/app_answers.php'; + $answers = qdb_group_app_submit_answers($answers, $shortIdToType, $symptomParentMap); + + foreach ($answers as $a) { + $shortId = trim($a['questionID'] ?? ''); + if ($shortId === '') { + continue; + } + + $answeredAt = isset($a['answeredAt']) ? (int)$a['answeredAt'] : null; + + // Glass-scale symptoms use string keys (not question localIds). + if (isset($symptomParentMap[$shortId])) { + $label = trim((string)($a['answerOptionKey'] ?? $a['freeTextValue'] ?? '')); + if ($label !== '') { + $parentId = $symptomParentMap[$shortId]; + $glassByParent[$parentId][$shortId] = $label; + } + continue; + } + + // Resolve short ID to full questionID + $fullQID = $shortIdMap[$shortId] ?? null; + if ($fullQID === null) { + continue; + } + + $answerOptionID = null; + $freeTextValue = isset($a['freeTextValue']) ? (string)$a['freeTextValue'] : null; + $numericValue = isset($a['numericValue']) ? (float)$a['numericValue'] : null; + + if ($freeTextValue !== null && $freeTextValue !== '') { + $maxLen = $freeTextMaxLen[$fullQID] ?? 2000; + $freeTextValue = sanitize_free_text($freeTextValue, $maxLen); + if ($freeTextValue === '') { + $answerDelete->execute([':cc' => $clientCode, ':qid' => $fullQID]); + $submittedQuestionIds[$fullQID] = true; + continue; + } + } + + // Resolve option key to answerOptionID (points computed by scoring engine after save) + $optionKey = $a['answerOptionKey'] ?? null; + if ($optionKey !== null && isset($optionMap[$fullQID][(string)$optionKey])) { + $opt = $optionMap[$fullQID][(string)$optionKey]; + $answerOptionID = $opt['answerOptionID']; + } + + $hasValue = $answerOptionID !== null + || ($freeTextValue !== null && $freeTextValue !== '') + || $numericValue !== null; + if (!$hasValue) { + $answerDelete->execute([':cc' => $clientCode, ':qid' => $fullQID]); + $submittedQuestionIds[$fullQID] = true; + continue; + } + + $answerInsert->execute([ + ':cc' => $clientCode, + ':qid' => $fullQID, + ':aoid' => $answerOptionID, + ':ftv' => $freeTextValue, + ':nv' => $numericValue, + ':at' => $answeredAt, + ]); + $submittedQuestionIds[$fullQID] = true; + } + + foreach ($glassByParent as $parentQID => $symptomLabels) { + $json = qdb_build_glass_symptom_json($symptomLabels); + if ($json === null) { + $answerDelete->execute([':cc' => $clientCode, ':qid' => $parentQID]); + } else { + $answerInsert->execute([ + ':cc' => $clientCode, + ':qid' => $parentQID, + ':aoid' => null, + ':ftv' => $json, + ':nv' => null, + ':at' => $completedAt ?? $startedAt, + ]); + } + $submittedQuestionIds[$parentQID] = true; + } + + if (!$isLegacySubmit) { + $activeMaps = qdb_submit_maps_from_active_questions($pdo, $qnID); + $staleQuestionIds = array_diff( + array_values($activeMaps['shortIdMap']), + array_keys($submittedQuestionIds) + ); + if ($staleQuestionIds !== []) { + $placeholders = implode(',', array_fill(0, count($staleQuestionIds), '?')); + $staleDelete = $pdo->prepare( + "DELETE FROM client_answer WHERE clientCode = ? AND questionID IN ($placeholders)" + ); + $staleDelete->execute(array_merge([$clientCode], array_values($staleQuestionIds))); + } + } + + require_once __DIR__ . '/../lib/scoring.php'; + $sumPoints = $isLegacySubmit + ? qdb_compute_questionnaire_score_from_manifest($pdo, $clientCode, $submitManifest) + : qdb_compute_questionnaire_score($pdo, $clientCode, $qnID); + + $pdo->prepare(" + INSERT INTO completed_questionnaire (clientCode, questionnaireID, assignedByCoach, status, startedAt, completedAt, sumPoints) + VALUES (:cc, :qn, :abc, 'completed', :sa, :ca, :sp) + ON CONFLICT(clientCode, questionnaireID) DO UPDATE SET + assignedByCoach = excluded.assignedByCoach, + status = 'completed', + startedAt = COALESCE(excluded.startedAt, startedAt), + completedAt = excluded.completedAt, + sumPoints = excluded.sumPoints + ")->execute([ + ':cc' => $clientCode, + ':qn' => $qnID, + ':abc' => $assignedByCoach !== '' ? $assignedByCoach : null, + ':sa' => $startedAt, + ':ca' => $completedAt, + ':sp' => $sumPoints, + ]); + + require_once __DIR__ . '/../lib/submissions.php'; + qdb_record_submission_after_submit( + $pdo, + $clientCode, + $qnID, + $tokenRec, + $startedAt, + $completedAt, + $sumPoints, + $assignedByCoach, + $submitRev, + $submitManifest, + array_keys($submittedQuestionIds) + ); + + qdb_recompute_profile_scores_for_client($pdo, $clientCode, $qnID); + + $pdo->commit(); + qdb_save($tmpDb, $lockFp); + + json_success([ + 'submitted' => true, + 'sumPoints' => $sumPoints, + 'structureRevision' => $submitRev, + 'legacySubmit' => $isLegacySubmit, + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Submit questionnaire', $pdo ?? null, $tmpDb ?? null, $lockFp ?? null); + } +} + +if ($method !== 'GET') { + json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); +} + +$opened = qdb_open_read_or_fail(); +[$pdo, $tmpDb, $lockFp] = $opened; + +$qnID = $_GET['id'] ?? ''; +$translations = $_GET['translations'] ?? ''; +$fetchClients = $_GET['clients'] ?? ''; +$fetchAnswers = $_GET['answers'] ?? ''; +$fetchAnswersBulk = $_GET['answersBulk'] ?? ''; +$fetchScoringProfiles = $_GET['scoringProfiles'] ?? ''; +$fetchScoringReview = $_GET['scoringReview'] ?? ''; +$clientCode = trim($_GET['clientCode'] ?? ''); + +if ($fetchScoringProfiles) { + require_role(['coach'], $tokenRec); + require_once __DIR__ . '/../lib/scoring.php'; + $profiles = array_values(array_filter( + qdb_list_scoring_profiles($pdo), + static fn(array $p): bool => (int)($p['isActive'] ?? 0) === 1 + )); + qdb_discard($tmpDb, $lockFp); + json_success_sensitive(['profiles' => $profiles], $tokenRec['_token']); +} + +if ($fetchScoringReview) { + require_role(['coach'], $tokenRec); + require_once __DIR__ . '/../lib/scoring.php'; + + $coachID = $tokenRec['entityID'] ?? ''; + if ($clientCode !== '') { + [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl'); + $clStmt = $pdo->prepare( + "SELECT cl.clientCode FROM client cl WHERE cl.clientCode = :cc AND ($rbacClause)" + ); + $clStmt->execute(array_merge([':cc' => $clientCode], $rbacParams)); + if (!$clStmt->fetch()) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Client not found or not authorized', 404); + } + $clientCodes = [$clientCode]; + } else { + $stmt = $pdo->prepare( + 'SELECT clientCode FROM client + WHERE coachID = :cid AND COALESCE(archived, 0) = 0 + ORDER BY clientCode' + ); + $stmt->execute([':cid' => $coachID]); + $clientCodes = $stmt->fetchAll(PDO::FETCH_COLUMN); + } + + $clients = qdb_app_scoring_review_for_clients($pdo, $clientCodes); + qdb_discard($tmpDb, $lockFp); + json_success_sensitive(['clients' => $clients], $tokenRec['_token']); +} + +if ($fetchAnswersBulk) { + require_role(['coach'], $tokenRec); + require_once __DIR__ . '/../lib/app_answers.php'; + $clients = qdb_export_app_bulk_answers_for_coach($pdo, $tokenRec); + qdb_discard($tmpDb, $lockFp); + json_success_sensitive(['clients' => $clients], $tokenRec['_token']); +} + +if ($fetchAnswers) { + require_role(['admin', 'supervisor', 'coach'], $tokenRec); + if ($clientCode === '') { + qdb_discard($tmpDb, $lockFp); + json_error('MISSING_PARAM', 'clientCode query param required', 400); + } + + [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl'); + $clStmt = $pdo->prepare( + "SELECT cl.clientCode FROM client cl WHERE cl.clientCode = :cc AND ($rbacClause)" + ); + $clStmt->execute(array_merge([':cc' => $clientCode], $rbacParams)); + if (!$clStmt->fetch()) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Client not found or not authorized', 404); + } + + require_once __DIR__ . '/../lib/app_answers.php'; + $questionnaires = qdb_export_app_client_answers_bundle($pdo, $clientCode); + qdb_discard($tmpDb, $lockFp); + json_success_sensitive([ + 'clientCode' => $clientCode, + 'questionnaires' => $questionnaires, + ], $tokenRec['_token']); +} + +if ($fetchClients) { + require_role(['coach'], $tokenRec); + + $coachID = $tokenRec['entityID'] ?? ''; + + $stmt = $pdo->prepare(" + SELECT clientCode + FROM client + WHERE coachID = :cid AND COALESCE(archived, 0) = 0 + ORDER BY clientCode + "); + $stmt->execute([':cid' => $coachID]); + $clientCodes = $stmt->fetchAll(PDO::FETCH_COLUMN); + + $completions = []; + if (!empty($clientCodes)) { + $placeholders = implode(',', array_fill(0, count($clientCodes), '?')); + $stmt2 = $pdo->prepare(" + SELECT clientCode, questionnaireID, sumPoints, completedAt + FROM completed_questionnaire + WHERE clientCode IN ($placeholders) + "); + $stmt2->execute($clientCodes); + foreach ($stmt2->fetchAll(PDO::FETCH_ASSOC) as $row) { + $completions[$row['clientCode']][] = [ + 'questionnaireID' => $row['questionnaireID'], + 'sumPoints' => (int) $row['sumPoints'], + 'completedAt' => $row['completedAt'] !== null ? (int) $row['completedAt'] : null, + ]; + } + } + + $clients = array_map(function ($code) use ($completions) { + return [ + 'clientCode' => $code, + 'completedQuestionnaires' => $completions[$code] ?? [], + ]; + }, $clientCodes); + + qdb_discard($tmpDb, $lockFp); + json_success_sensitive(['coachID' => $coachID, 'clients' => $clients], $tokenRec['_token']); +} + +if ($qnID) { + require_once __DIR__ . '/../lib/questionnaire_structure.php'; + $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); + json_error('NOT_FOUND', 'Questionnaire not found', 404); + } + + $structureRevision = qdb_questionnaire_structure_revision($pdo, $qnID); + $stmt = $pdo->prepare( + 'SELECT questionID, defaultText, type, orderIndex, configJson + FROM question WHERE questionnaireID = :id AND ' . qdb_active_questions_clause('question') . ' + 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 ($layout === 'glass_scale_question' && !empty($config['symptoms'])) { + $q['glassSymptoms'] = qdb_glass_symptoms_with_labels($pdo, $config); + } + 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']; + } + + if (in_array($layout, ['glass_scale_question', 'slider_question', 'value_spinner'], true)) { + require_once __DIR__ . '/../lib/scoring.php'; + $ruleMap = qdb_score_rule_map_for_question($pdo, $dbQ['questionID']); + if ($ruleMap !== []) { + $q['scoreRuleMap'] = $ruleMap; + } + } + + $ao = $pdo->prepare( + 'SELECT defaultText, points, nextQuestionId + FROM answer_option WHERE questionID = :qid AND ' . qdb_active_options_clause('answer_option') . ' + 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']; + } + // DB answer_option rows are for choice layouts only — do not overwrite string/value spinners. + if (in_array($layout, ['radio_question', 'multi_check_box_question', 'glass_scale_question'], true)) { + $q['options'] = $optionsArr; + $q['pointsMap'] = $pointsMap; + } + } + + $questions[] = $q; + } + + qdb_discard($tmpDb, $lockFp); + json_success([ + 'meta' => ['id' => $qnID], + 'structureRevision' => $structureRevision, + 'questions' => $questions, + 'translations' => qdb_build_questionnaire_translations_map($pdo, $qnID), + ]); +} + +// Default: ordered questionnaire list +require_once __DIR__ . '/../lib/questionnaire_structure.php'; + +$rows = $pdo->query(" + SELECT questionnaireID, name, showPoints, conditionJson, categoryKey, + COALESCE(structureRevision, 1) AS structureRevision + FROM questionnaire + WHERE state = 'active' + ORDER BY orderIndex +")->fetchAll(PDO::FETCH_ASSOC); + +$list = []; +foreach ($rows as $r) { + $item = [ + 'id' => $r['questionnaireID'], + 'name' => $r['name'], + 'showPoints' => (bool)(int)$r['showPoints'], + 'structureRevision' => max(1, (int)$r['structureRevision']), + 'condition' => json_decode($r['conditionJson'], true) ?: new stdClass(), + ]; + $cat = trim($r['categoryKey'] ?? ''); + if ($cat !== '') { + $item['categoryKey'] = $cat; + } + $list[] = $item; +} + +qdb_discard($tmpDb, $lockFp); +json_success($list); diff --git a/handlers/assignments.php b/handlers/assignments.php new file mode 100644 index 0000000..3e8696c --- /dev/null +++ b/handlers/assignments.php @@ -0,0 +1,99 @@ +query( + "SELECT c.coachID, c.username, c.supervisorID, sv.username AS supervisorUsername + FROM coach c + LEFT JOIN supervisor sv ON sv.supervisorID = c.supervisorID + ORDER BY sv.username, c.username" + )->fetchAll(PDO::FETCH_ASSOC); + } else { + $stmt = $pdo->prepare( + "SELECT c.coachID, c.username, c.supervisorID, sv.username AS supervisorUsername + FROM coach c + LEFT JOIN supervisor sv ON sv.supervisorID = c.supervisorID + WHERE c.supervisorID = :sid ORDER BY c.username" + ); + $stmt->execute([':sid' => $callerEntityID]); + $coaches = $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + [$clause, $params] = rbac_client_filter($tokenRec, 'cl'); + $clientStmt = $pdo->prepare( + "SELECT cl.clientCode, cl.coachID, co.username AS coachUsername + FROM client cl + LEFT JOIN coach co ON co.coachID = cl.coachID + WHERE $clause + ORDER BY cl.coachID, cl.clientCode" + ); + foreach ($params as $k => $v) $clientStmt->bindValue($k, $v); + $clientStmt->execute(); + $clients = $clientStmt->fetchAll(PDO::FETCH_ASSOC); + + qdb_discard($tmpDb, $lockFp); + json_success(['coaches' => $coaches, 'clients' => $clients]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'assignments', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'POST': + $body = read_json_body(); + $clientCodes = $body['clientCodes'] ?? []; + $coachID = trim($body['coachID'] ?? ''); + + if (empty($clientCodes) || $coachID === '') { + json_error('MISSING_FIELDS', 'clientCodes and coachID are required', 400); + } + if (!is_array($clientCodes)) $clientCodes = [$clientCodes]; + + try { + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + + if ($callerRole === 'supervisor') { + $chk = $pdo->prepare("SELECT coachID FROM coach WHERE coachID = :cid AND supervisorID = :sid"); + $chk->execute([':cid' => $coachID, ':sid' => $callerEntityID]); + } else { + $chk = $pdo->prepare("SELECT coachID FROM coach WHERE coachID = :cid"); + $chk->execute([':cid' => $coachID]); + } + if (!$chk->fetch()) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Counselor not found or not authorized', 404); + } + + [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec); + $pdo->beginTransaction(); + $updStmt = $pdo->prepare( + "UPDATE client SET coachID = :cid WHERE clientCode = :cc AND ($rbacClause)" + ); + $count = 0; + foreach ($clientCodes as $cc) { + $cc = trim($cc); + if ($cc === '') continue; + $updStmt->execute(array_merge([':cid' => $coachID, ':cc' => $cc], $rbacParams)); + $count += $updStmt->rowCount(); + } + $pdo->commit(); + qdb_save($tmpDb, $lockFp); + + json_success(['assigned' => $count]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'assignments', $pdo ?? null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +default: + json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); +} diff --git a/handlers/auth.php b/handlers/auth.php new file mode 100644 index 0000000..69001b1 --- /dev/null +++ b/handlers/auth.php @@ -0,0 +1,286 @@ +prepare( + "SELECT userID, passwordHash, role, entityID, mustChangePassword + FROM users WHERE username = :u" + ); + $stmt->execute([':u' => $username]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$user || !password_verify($password, $user['passwordHash'])) { + qdb_discard($tmpDb, $lockFp); + $retryAfter = qdb_login_rate_limit_record_failure($username, $securitySettings); + if ($retryAfter > 0) { + qdb_login_rate_limit_deny($retryAfter); + } + json_error('INVALID_CREDENTIALS', 'Invalid username or password', 401); + } + + qdb_login_rate_limit_clear($username); + + $assignedClients = []; + if (($user['role'] ?? '') === 'coach' && ($user['entityID'] ?? '') !== '') { + $cStmt = $pdo->prepare( + "SELECT clientCode FROM client + WHERE coachID = :cid AND COALESCE(archived, 0) = 0 + ORDER BY clientCode" + ); + $cStmt->execute([':cid' => $user['entityID']]); + $assignedClients = array_map( + fn($code) => ['clientCode' => $code], + $cStmt->fetchAll(PDO::FETCH_COLUMN) + ); + } + + qdb_discard($tmpDb, $lockFp); + + qdb_reject_coach_web_login((string)($user['role'] ?? '')); + + if ((int)$user['mustChangePassword'] === 1) { + $tempToken = bin2hex(random_bytes(32)); + token_add($tempToken, qdb_session_ttl_seconds($securitySettings, true), [ + 'role' => $user['role'], + 'entityID' => $user['entityID'], + 'userID' => $user['userID'], + 'temp' => true, + ]); + json_success([ + 'mustChangePassword' => true, + 'token' => $tempToken, + 'user' => $username, + 'role' => $user['role'], + ]); + } + + $token = bin2hex(random_bytes(32)); + token_add($token, qdb_session_ttl_seconds($securitySettings, false), [ + 'role' => $user['role'], + 'entityID' => $user['entityID'], + 'userID' => $user['userID'], + ]); + $loginData = [ + 'token' => $token, + 'user' => $username, + 'role' => $user['role'], + ]; + if ($assignedClients !== []) { + $loginData['clientsPayload'] = qdb_sensitive_envelope( + json_encode(['clients' => $assignedClients], JSON_UNESCAPED_UNICODE), + $token + ); + } + json_success($loginData); + } catch (Throwable $e) { + qdb_handler_fail($e, 'auth', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'auth/change-password': + if ($method !== 'POST') { + json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); + } + + $bearerToken = get_bearer_token(); + if (!$bearerToken) { + json_error('UNAUTHORIZED', 'Bearer token required', 401); + } + $tokenRec = token_get_record($bearerToken); + if (!$tokenRec) { + json_error('FORBIDDEN', 'Invalid or expired token', 403); + } + + $body = read_json_body(); + $username = trim($body['username'] ?? ''); + $oldPassword = (string)($body['old_password'] ?? ''); + $newPassword = (string)($body['new_password'] ?? ''); + + if ($username === '' || $oldPassword === '' || $newPassword === '') { + json_error('MISSING_FIELDS', 'username, old_password, and new_password are required', 400); + } + if (strlen($newPassword) < 6) { + json_error('PASSWORD_TOO_SHORT', 'New password must be at least 6 characters', 400); + } + + if (($tokenRec['userID'] ?? '') === '') { + json_error('FORBIDDEN', 'Token not associated with a user', 403); + } + + try { + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + + $stmt = $pdo->prepare( + "SELECT userID, passwordHash, role, entityID FROM users WHERE username = :u" + ); + $stmt->execute([':u' => $username]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$user) { + qdb_discard($tmpDb, $lockFp); + json_error('INVALID_CREDENTIALS', 'Invalid credentials', 401); + } + + if ($user['userID'] !== $tokenRec['userID']) { + qdb_discard($tmpDb, $lockFp); + json_error('FORBIDDEN', 'Token does not match user', 403); + } + + qdb_reject_coach_web_login((string)($user['role'] ?? '')); + + if (!password_verify($oldPassword, $user['passwordHash'])) { + qdb_discard($tmpDb, $lockFp); + json_error('INVALID_CREDENTIALS', 'Old password incorrect', 401); + } + + $newHash = password_hash($newPassword, PASSWORD_DEFAULT); + $pdo->prepare( + "UPDATE users SET passwordHash = :h, mustChangePassword = 0 WHERE userID = :uid" + )->execute([':h' => $newHash, ':uid' => $user['userID']]); + + token_revoke_all_for_user_on_pdo($pdo, $user['userID']); + + qdb_save($tmpDb, $lockFp); + + $securitySettings = qdb_settings_get(); + + // Issue a fresh session for this browser only + $newToken = bin2hex(random_bytes(32)); + token_add($newToken, qdb_session_ttl_seconds($securitySettings, false), [ + 'role' => $user['role'], + 'entityID' => $user['entityID'], + 'userID' => $user['userID'], + ]); + + json_success([ + 'token' => $newToken, + 'user' => $username, + 'role' => $user['role'], + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'auth', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'auth/keycloak-config': + if ($method !== 'GET') { + json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); + } + $kcConfig = qdb_keycloak_config(); + json_success([ + 'enabled' => qdb_keycloak_is_configured($kcConfig), + 'displayName' => $kcConfig['display_name'], + 'loginUrl' => qdb_keycloak_is_configured($kcConfig) ? 'auth/keycloak-login' : '', + ]); + break; + +case 'auth/keycloak-login': + if ($method !== 'GET') { + json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); + } + $kcConfig = qdb_keycloak_config(); + if (!qdb_keycloak_is_configured($kcConfig)) { + json_error('KEYCLOAK_NOT_CONFIGURED', 'University login is not configured', 503); + } + try { + $discovery = qdb_keycloak_discovery($kcConfig); + $nonce = bin2hex(random_bytes(16)); + $params = [ + 'client_id' => $kcConfig['client_id'], + 'redirect_uri' => $kcConfig['redirect_uri'], + 'response_type' => 'code', + 'scope' => 'openid profile email', + 'state' => qdb_keycloak_create_state($nonce), + 'nonce' => $nonce, + ]; + qdb_keycloak_redirect($discovery['authorization_endpoint'] . '?' . http_build_query($params)); + } catch (Throwable $e) { + qdb_handler_fail($e, 'keycloak-login', null, null, null); + } + break; + +case 'auth/keycloak-callback': + if ($method !== 'GET') { + json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); + } + if (isset($_GET['error'])) { + json_error('KEYCLOAK_DENIED', 'University login was cancelled or denied', 401); + } + $code = trim((string)($_GET['code'] ?? '')); + $state = trim((string)($_GET['state'] ?? '')); + if ($code === '' || $state === '') { + json_error('MISSING_FIELDS', 'Keycloak callback requires code and state', 400); + } + $kcConfig = qdb_keycloak_config(); + if (!qdb_keycloak_is_configured($kcConfig)) { + json_error('KEYCLOAK_NOT_CONFIGURED', 'University login is not configured', 503); + } + try { + qdb_keycloak_verify_state($state); + $discovery = qdb_keycloak_discovery($kcConfig); + $tokenResponse = qdb_keycloak_exchange_code($kcConfig, $discovery, $code); + $accessToken = (string)($tokenResponse['access_token'] ?? ''); + if ($accessToken === '') { + json_error('KEYCLOAK_TOKEN_FAILED', 'University login did not return an access token', 401); + } + $claims = qdb_keycloak_userinfo($discovery, $accessToken); + $username = qdb_keycloak_claim_username($kcConfig, $claims); + + [$pdo, $tmpDb, $lockFp] = qdb_open_read_or_fail(); + $stmt = $pdo->prepare( + "SELECT userID, role, entityID, mustChangePassword + FROM users WHERE username = :u" + ); + $stmt->execute([':u' => $username]); + $user = $stmt->fetch(PDO::FETCH_ASSOC); + qdb_discard($tmpDb, $lockFp); + + if (!$user) { + json_error('KEYCLOAK_USER_NOT_ALLOWED', 'University account is not registered for this application', 403); + } + qdb_reject_coach_web_login((string)($user['role'] ?? '')); + if ((int)($user['mustChangePassword'] ?? 0) === 1) { + json_error('PASSWORD_CHANGE_REQUIRED', 'Please sign in locally once to change your temporary password before using University login', 403); + } + + $securitySettings = qdb_settings_get(); + $token = bin2hex(random_bytes(32)); + token_add($token, qdb_session_ttl_seconds($securitySettings, false), [ + 'role' => $user['role'], + 'entityID' => $user['entityID'], + 'userID' => $user['userID'], + ]); + qdb_keycloak_finish_html($token, $username, (string)$user['role']); + } catch (Throwable $e) { + qdb_handler_fail($e, 'keycloak-callback', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +default: + json_error('NOT_FOUND', 'Unknown auth route', 404); +} diff --git a/handlers/backup.php b/handlers/backup.php new file mode 100644 index 0000000..a8a8e3d --- /dev/null +++ b/handlers/backup.php @@ -0,0 +1,33 @@ + $filename]); diff --git a/handlers/clients.php b/handlers/clients.php new file mode 100644 index 0000000..594e0d4 --- /dev/null +++ b/handlers/clients.php @@ -0,0 +1,239 @@ +prepare( + "SELECT cl.clientCode, cl.coachID, cl.archived, cl.archivedAt, + co.username AS coachUsername, + CASE WHEN EXISTS ( + SELECT 1 FROM completed_questionnaire cq WHERE cq.clientCode = cl.clientCode + ) OR EXISTS ( + SELECT 1 FROM questionnaire_submission qs WHERE qs.clientCode = cl.clientCode + ) THEN 1 ELSE 0 END AS hasResponseData + FROM client cl + LEFT JOIN coach co ON co.coachID = cl.coachID + WHERE $clause + ORDER BY cl.archived ASC, hasResponseData DESC, cl.clientCode ASC" + ); + foreach ($params as $k => $v) $stmt->bindValue($k, $v); + $stmt->execute(); + $clients = $stmt->fetchAll(PDO::FETCH_ASSOC); + + require_once __DIR__ . '/../lib/submissions.php'; + $scoringSummary = qdb_clients_scoring_summary_for_list($pdo, $tokenRec); + foreach ($clients as &$client) { + $client['scoringProfiles'] = qdb_client_scoring_dots_for_client( + $client['clientCode'], + $scoringSummary + ); + } + unset($client); + + qdb_discard($tmpDb, $lockFp); + json_success([ + 'clients' => $clients, + 'scoringProfiles' => $scoringSummary['profiles'], + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'clients', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'POST': + $body = read_json_body(); + $clientCode = trim($body['clientCode'] ?? ''); + $coachID = trim($body['coachID'] ?? ''); + + if ($clientCode === '' || $coachID === '') { + json_error('MISSING_FIELDS', 'clientCode and coachID are required', 400); + } + + try { + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + + // Validate coach exists and caller is allowed to assign to them + if ($callerRole === 'supervisor') { + $chkCoach = $pdo->prepare("SELECT coachID FROM coach WHERE coachID = :cid AND supervisorID = :sid"); + $chkCoach->execute([':cid' => $coachID, ':sid' => $callerEntityID]); + } else { + $chkCoach = $pdo->prepare("SELECT coachID FROM coach WHERE coachID = :cid"); + $chkCoach->execute([':cid' => $coachID]); + } + if (!$chkCoach->fetch()) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Counselor not found or not authorized', 404); + } + + $chk = $pdo->prepare("SELECT clientCode FROM client WHERE clientCode = :cc"); + $chk->execute([':cc' => $clientCode]); + if ($chk->fetch()) { + qdb_discard($tmpDb, $lockFp); + json_error('DUPLICATE', 'Client code already exists', 409); + } + + $pdo->prepare( + "INSERT INTO client (clientCode, coachID, archived, archivedAt) VALUES (:cc, :cid, 0, 0)" + )->execute([':cc' => $clientCode, ':cid' => $coachID]); + + qdb_save($tmpDb, $lockFp); + json_success(['clientCode' => $clientCode, 'coachID' => $coachID]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'clients', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'PUT': + $body = read_json_body(); + $clientCode = trim((string)($body['clientCode'] ?? '')); + $profileID = trim((string)($body['profileID'] ?? '')); + $coachBand = trim((string)($body['coachBand'] ?? '')); + + if ($clientCode === '' || $profileID === '' || $coachBand === '') { + json_error('MISSING_FIELDS', 'clientCode, profileID, and coachBand are required', 400); + } + + $calculatedBand = trim((string)($body['calculatedBand'] ?? '')); + $weightedTotal = isset($body['weightedTotal']) ? (float)$body['weightedTotal'] : null; + + try { + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + + [$clause, $params] = rbac_client_filter($tokenRec, 'cl'); + $chk = $pdo->prepare( + "SELECT clientCode FROM client cl WHERE cl.clientCode = :cc AND ($clause)" + ); + $chk->execute(array_merge([':cc' => $clientCode], $params)); + if (!$chk->fetch()) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Client not found or not authorized', 404); + } + + require_once __DIR__ . '/../lib/scoring.php'; + if (!qdb_is_valid_scoring_band($coachBand)) { + qdb_discard($tmpDb, $lockFp); + json_error('INVALID_FIELD', 'coachBand must be green, yellow, or red', 400); + } + + $profile = qdb_save_coach_scoring_review( + $pdo, + $clientCode, + $profileID, + $coachBand, + (string)($tokenRec['userID'] ?? ''), + $weightedTotal, + $calculatedBand !== '' ? $calculatedBand : null, + ); + + qdb_save($tmpDb, $lockFp); + json_success(['profile' => $profile]); + } catch (InvalidArgumentException $e) { + qdb_discard($tmpDb ?? null, $lockFp ?? null); + json_error('INVALID_FIELD', $e->getMessage(), 400); + } catch (RuntimeException $e) { + qdb_discard($tmpDb ?? null, $lockFp ?? null); + json_error('NOT_FOUND', $e->getMessage(), 404); + } catch (Throwable $e) { + qdb_handler_fail($e, 'clients', $pdo ?? null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'PATCH': + $body = read_json_body(); + $clientCode = trim((string)($body['clientCode'] ?? '')); + if ($clientCode === '' || !array_key_exists('archived', $body)) { + json_error('MISSING_FIELDS', 'clientCode and archived are required', 400); + } + $archived = !empty($body['archived']) ? 1 : 0; + + try { + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + + [$clause, $params] = rbac_client_filter($tokenRec, 'cl', 'all'); + $chk = $pdo->prepare( + "SELECT clientCode, archived FROM client cl WHERE cl.clientCode = :cc AND ($clause)" + ); + $chk->execute(array_merge([':cc' => $clientCode], $params)); + $row = $chk->fetch(PDO::FETCH_ASSOC); + if (!$row) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Client not found or not authorized', 404); + } + + $archivedAt = $archived ? time() : 0; + $pdo->prepare( + 'UPDATE client SET archived = :a, archivedAt = :at WHERE clientCode = :cc' + )->execute([':a' => $archived, ':at' => $archivedAt, ':cc' => $clientCode]); + + qdb_save($tmpDb, $lockFp); + json_success([ + 'clientCode' => $clientCode, + 'archived' => $archived, + 'archivedAt' => $archivedAt, + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'clients', $pdo ?? null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'DELETE': + $body = read_json_body(); + $clientCode = trim($body['clientCode'] ?? ''); + + if ($clientCode === '') { + json_error('MISSING_FIELDS', 'clientCode is required', 400); + } + + try { + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + + // Verify the client exists and the caller can see it (RBAC) + [$clause, $params] = rbac_client_filter($tokenRec, 'cl'); + $chk = $pdo->prepare( + "SELECT clientCode FROM client cl WHERE cl.clientCode = :cc AND ($clause)" + ); + $chk->execute(array_merge([':cc' => $clientCode], $params)); + if (!$chk->fetch()) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Client not found or not authorized', 404); + } + + require_once __DIR__ . '/../lib/submissions.php'; + + $pdo->beginTransaction(); + qdb_delete_client_response_data($pdo, [$clientCode]); + $pdo->prepare('DELETE FROM client WHERE clientCode = :cc') + ->execute([':cc' => $clientCode]); + $pdo->commit(); + + qdb_save($tmpDb, $lockFp); + json_success([]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'clients', $pdo ?? null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +default: + json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); +} diff --git a/handlers/coaches.php b/handlers/coaches.php new file mode 100644 index 0000000..9f593f1 --- /dev/null +++ b/handlers/coaches.php @@ -0,0 +1,29 @@ + $coaches]); +} catch (Throwable $e) { + qdb_handler_fail($e, 'Load coaches', null, $tmpDb ?? null, $lockFp ?? null); +} diff --git a/handlers/dev.php b/handlers/dev.php new file mode 100644 index 0000000..f544713 --- /dev/null +++ b/handlers/dev.php @@ -0,0 +1,53 @@ + $body['usernamePrefix'] ?? 'dev_', + 'clientCodePrefix' => $body['clientCodePrefix'] ?? 'DEV-CL-', + ]); + qdb_save($tmpDb, $lockFp); + json_success($result); + } + + if ($action === 'wipeExceptAdmins') { + $result = qdb_wipe_all_data_except_admins($pdo); + qdb_save($tmpDb, $lockFp); + json_success($result); + } + + $fixture = $body['fixture'] ?? null; + if (!is_array($fixture)) { + qdb_discard($tmpDb, $lockFp); + json_error('MISSING_FIELDS', 'fixture object is required for import', 400); + } + + $result = qdb_import_dev_fixture($pdo, $fixture); + qdb_save($tmpDb, $lockFp); + json_success($result); +} catch (Throwable $e) { + $labels = [ + 'remove' => 'Remove dev data', + 'wipeExceptAdmins' => 'Wipe database', + 'import' => 'Import dev fixture', + ]; + $label = $labels[$action ?? 'import'] ?? 'Dev fixture'; + qdb_handler_fail($e, $label, $pdo ?? null, $tmpDb ?? null, $lockFp ?? null); +} diff --git a/handlers/export.php b/handlers/export.php new file mode 100644 index 0000000..39d6110 --- /dev/null +++ b/handlers/export.php @@ -0,0 +1,111 @@ + 'application/json; charset=UTF-8', + 'Content-Disposition' => 'attachment; filename="' . $filename . '"', + ] + ); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Export questionnaires bundle', null, $tmpDb ?? null, $lockFp ?? null); + } +} + +if (!empty($_GET['exportAll'])) { + require_role(['admin'], $tokenRec); + try { + $opened = qdb_open_read_or_fail(); + [$pdo, $tmpDb, $lockFp] = $opened; + $allVersions = !empty($_GET['allVersions']); + $zipPath = qdb_build_server_export_zip($pdo, $tokenRec, $allVersions); + qdb_discard($tmpDb, $lockFp); + + $label = $allVersions ? 'all_versions' : 'current'; + $filename = 'responses_export_' . $label . '_' . date('Y-m-d_His') . '.zip'; + $zipBody = (string)file_get_contents($zipPath); + @unlink($zipPath); + + qdb_http_download($zipBody, [ + 'Content-Type' => 'application/zip', + 'Content-Disposition' => 'attachment; filename="' . $filename . '"', + 'Content-Length' => (string)strlen($zipBody), + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Export responses ZIP', null, $tmpDb ?? null, $lockFp ?? null); + } +} + +$qnID = $_GET['questionnaireID'] ?? ''; +if (!$qnID) { + json_error('MISSING_PARAM', 'questionnaireID query param required', 400); +} + +$allVersions = !empty($_GET['allVersions']); + +try { + $opened = qdb_open_read_or_fail(); + [$pdo, $tmpDb, $lockFp] = $opened; + +$qn = $pdo->prepare('SELECT * FROM questionnaire WHERE questionnaireID = :id'); +$qn->execute([':id' => $qnID]); +$questionnaire = $qn->fetch(PDO::FETCH_ASSOC); +if (!$questionnaire) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Questionnaire not found', 404); +} + +$ctx = qdb_export_questionnaire_context($pdo, $qnID); +$questions = $ctx['questions']; +$resultColumns = $ctx['resultColumns']; +$optionTextMap = $ctx['optionTextMap']; + +if ($allVersions) { + $rows = qdb_export_all_versions_rows($pdo, $qnID, $questions, $resultColumns, $optionTextMap, $tokenRec); + qdb_discard($tmpDb, $lockFp); + + $safeName = qdb_export_safe_basename($questionnaire['name']); + $filename = $safeName . '_all_versions_' . date('Y-m-d') . '.csv'; + + qdb_http_download( + qdb_export_rows_to_csv_string($rows, qdb_export_all_versions_csv_fallback_header($resultColumns)), + [ + 'Content-Type' => 'text/csv; charset=UTF-8', + 'Content-Disposition' => 'attachment; filename="' . $filename . '"', + ] + ); +} + +$rows = qdb_export_current_rows($pdo, $qnID, $questions, $resultColumns, $optionTextMap, $tokenRec); +qdb_discard($tmpDb, $lockFp); + +$safeName = qdb_export_safe_basename($questionnaire['name']); +$filename = $safeName . '_v' . $questionnaire['version'] . '.csv'; + + qdb_http_download( + qdb_export_rows_to_csv_string($rows, qdb_export_current_csv_fallback_header($resultColumns)), + [ + 'Content-Type' => 'text/csv; charset=UTF-8', + 'Content-Disposition' => 'attachment; filename="' . $filename . '"', + ] + ); +} catch (Throwable $e) { + qdb_handler_fail($e, 'Export CSV', null, $tmpDb ?? null, $lockFp ?? null); +} diff --git a/handlers/logout.php b/handlers/logout.php new file mode 100644 index 0000000..3dd0aa6 --- /dev/null +++ b/handlers/logout.php @@ -0,0 +1,18 @@ + true]); diff --git a/handlers/questionnaires.php b/handlers/questionnaires.php new file mode 100644 index 0000000..3a172a9 --- /dev/null +++ b/handlers/questionnaires.php @@ -0,0 +1,244 @@ +prepare($sql); + $stmt->execute($rbacParams); + $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); + foreach ($rows as &$r) { + $r['orderIndex'] = (int)$r['orderIndex']; + $r['showPoints'] = (int)$r['showPoints']; + $r['questionCount'] = (int)$r['questionCount']; + $r['completedCount'] = (int)$r['completedCount']; + $r['conditionJson'] = $r['conditionJson'] ?: '{}'; + } + unset($r); + $categoryKeys = qdb_list_category_keys_for_dashboard($pdo); + qdb_discard($tmpDb, $lockFp); + json_success(['questionnaires' => $rows, 'categoryKeys' => $categoryKeys]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Load questionnaires', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'POST': + require_role(['admin', 'supervisor'], $tokenRec); + $body = read_json_body(); + + if (($body['action'] ?? '') === 'updateConditionMessageLabel') { + $messageKey = trim((string)($body['messageKey'] ?? '')); + $germanLabel = (string)($body['germanLabel'] ?? ''); + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + try { + qdb_set_app_string_german_label($pdo, $messageKey, $germanLabel); + qdb_save($tmpDb, $lockFp); + json_success([ + 'messageKey' => qdb_validate_stable_key($messageKey, 'messageKey'), + 'germanLabel' => trim($germanLabel), + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Update condition message label', null, $tmpDb, $lockFp); + } + break; + } + + if (($body['action'] ?? '') === 'updateCategoryLabel') { + $categoryKey = trim((string)($body['categoryKey'] ?? '')); + $germanLabel = (string)($body['germanLabel'] ?? ''); + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + try { + $stringKey = qdb_set_category_german_label($pdo, $categoryKey, $germanLabel); + qdb_save($tmpDb, $lockFp); + json_success([ + 'categoryKey' => $categoryKey, + 'stringKey' => $stringKey, + 'germanLabel' => trim($germanLabel), + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Update category label', null, $tmpDb, $lockFp); + } + break; + } + + if (($body['action'] ?? '') === 'deleteCategoryKey') { + $categoryKey = trim((string)($body['categoryKey'] ?? '')); + if ($categoryKey === '') { + json_error('INVALID_FIELD', 'categoryKey is required', 400); + } + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + try { + $cleared = qdb_delete_category_key($pdo, $categoryKey); + qdb_save($tmpDb, $lockFp); + json_success(['deleted' => $categoryKey, 'questionnairesCleared' => $cleared]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Delete category key', null, $tmpDb, $lockFp); + } + break; + } + + if (($body['action'] ?? '') === 'importBundle') { + $bundle = $body['bundle'] ?? null; + if (!is_array($bundle)) { + json_error('MISSING_FIELDS', 'bundle object is required', 400); + } + $replaceIfExists = !empty($body['replaceIfExists']); + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + try { + $pdo->exec('PRAGMA foreign_keys = OFF;'); + $result = qdb_import_questionnaires_bundle($pdo, $bundle, $replaceIfExists); + $pdo->exec('PRAGMA foreign_keys = ON;'); + qdb_save($tmpDb, $lockFp); + json_success($result); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Import questionnaires', null, $tmpDb, $lockFp); + } + break; + } + + if (empty($body['name'])) { + json_error('NAME_REQUIRED', 'name is required', 400); + } + $id = bin2hex(random_bytes(16)); + $name = trim($body['name']); + $version = trim($body['version'] ?? ''); + $state = trim($body['state'] ?? 'draft'); + $order = (int)($body['orderIndex'] ?? 0); + $showPts = (int)($body['showPoints'] ?? 0); + $condJson = $body['conditionJson'] ?? '{}'; + + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + try { + if ($order === 0) { + $order = (int)$pdo->query("SELECT COALESCE(MAX(orderIndex),0)+1 FROM questionnaire")->fetchColumn(); + } + $catKey = trim($body['categoryKey'] ?? ''); + if ($catKey !== '') { + $catKey = qdb_normalize_stable_key($catKey); + } + $pdo->prepare("INSERT INTO questionnaire (questionnaireID, name, version, state, orderIndex, showPoints, conditionJson, categoryKey) + VALUES (:id, :n, :v, :s, :o, :sp, :cj, :ck)") + ->execute([':id' => $id, ':n' => $name, ':v' => $version, ':s' => $state, + ':o' => $order, ':sp' => $showPts, ':cj' => $condJson, ':ck' => $catKey]); + qdb_save($tmpDb, $lockFp); + json_success(['questionnaire' => [ + 'questionnaireID' => $id, 'name' => $name, 'version' => $version, + 'state' => $state, 'orderIndex' => $order, 'showPoints' => $showPts, + 'conditionJson' => $condJson, 'questionCount' => 0, + ]]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Create questionnaire', null, $tmpDb, $lockFp); + } + break; + +case 'PUT': + require_role(['admin', 'supervisor'], $tokenRec); + $body = read_json_body(); + if (empty($body['questionnaireID'])) { + json_error('QUESTIONNAIRE_ID_REQUIRED', 'questionnaireID is required', 400); + } + $id = $body['questionnaireID']; + + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + try { + $existing = $pdo->prepare('SELECT * FROM questionnaire WHERE questionnaireID = :id'); + $existing->execute([':id' => $id]); + $row = $existing->fetch(PDO::FETCH_ASSOC); + if (!$row) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Questionnaire not found', 404); + } + $name = trim($body['name'] ?? $row['name']); + $version = trim($body['version'] ?? $row['version']); + $state = trim($body['state'] ?? $row['state']); + $order = (int)($body['orderIndex'] ?? $row['orderIndex']); + $showPts = (int)($body['showPoints'] ?? $row['showPoints']); + $condJson = $body['conditionJson'] ?? $row['conditionJson']; + $catKey = array_key_exists('categoryKey', $body) + ? trim((string)$body['categoryKey']) + : trim($row['categoryKey'] ?? ''); + if ($catKey !== '') { + $catKey = qdb_normalize_stable_key($catKey); + } + + $pdo->prepare("UPDATE questionnaire SET name = :n, version = :v, state = :s, + orderIndex = :o, showPoints = :sp, conditionJson = :cj, categoryKey = :ck + WHERE questionnaireID = :id") + ->execute([':n' => $name, ':v' => $version, ':s' => $state, + ':o' => $order, ':sp' => $showPts, ':cj' => $condJson, ':ck' => $catKey, ':id' => $id]); + qdb_save($tmpDb, $lockFp); + json_success(['questionnaire' => [ + 'questionnaireID' => $id, 'name' => $name, 'version' => $version, 'state' => $state, + 'orderIndex' => $order, 'showPoints' => $showPts, 'conditionJson' => $condJson, + ]]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Update questionnaire', null, $tmpDb, $lockFp); + } + break; + +case 'DELETE': + require_role(['admin'], $tokenRec); + $body = read_json_body(); + if (empty($body['questionnaireID'])) { + json_error('QUESTIONNAIRE_ID_REQUIRED', 'questionnaireID is required', 400); + } + $id = $body['questionnaireID']; + + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + try { + $pdo->exec('PRAGMA foreign_keys = OFF;'); + $pdo->beginTransaction(); + + $qIds = $pdo->prepare('SELECT questionID FROM question WHERE questionnaireID = :id'); + $qIds->execute([':id' => $id]); + $questionIDs = $qIds->fetchAll(PDO::FETCH_COLUMN); + + foreach ($questionIDs as $qid) { + $aoIds = $pdo->prepare('SELECT answerOptionID FROM answer_option WHERE questionID = :qid'); + $aoIds->execute([':qid' => $qid]); + $optionIDs = $aoIds->fetchAll(PDO::FETCH_COLUMN); + foreach ($optionIDs as $aoid) { + $pdo->prepare('DELETE FROM answer_option_translation WHERE answerOptionID = :id')->execute([':id' => $aoid]); + } + $pdo->prepare('DELETE FROM answer_option WHERE questionID = :qid')->execute([':qid' => $qid]); + $pdo->prepare('DELETE FROM question_translation WHERE questionID = :qid')->execute([':qid' => $qid]); + $pdo->prepare('DELETE FROM client_answer WHERE questionID = :qid')->execute([':qid' => $qid]); + } + $pdo->prepare('DELETE FROM question WHERE questionnaireID = :id')->execute([':id' => $id]); + $pdo->prepare('DELETE FROM completed_questionnaire WHERE questionnaireID = :id')->execute([':id' => $id]); + $pdo->prepare('DELETE FROM questionnaire WHERE questionnaireID = :id')->execute([':id' => $id]); + + $pdo->commit(); + $pdo->exec('PRAGMA foreign_keys = ON;'); + qdb_save($tmpDb, $lockFp); + json_success([]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Delete questionnaire', $pdo, $tmpDb, $lockFp); + } + break; + +default: + json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); +} diff --git a/handlers/questions.php b/handlers/questions.php new file mode 100644 index 0000000..8bebf9d --- /dev/null +++ b/handlers/questions.php @@ -0,0 +1,357 @@ +prepare( + "SELECT questionID, questionnaireID, defaultText, type, orderIndex, isRequired, configJson, + retiredAt, retiredInRevision + FROM question WHERE $qWhere ORDER BY orderIndex" + ); + $stmt->execute([':qid' => $qnID]); + $questions = $stmt->fetchAll(PDO::FETCH_ASSOC); + $structureRevision = qdb_questionnaire_structure_revision($pdo, $qnID); + foreach ($questions as &$q) { + $q['isRequired'] = (int)$q['isRequired']; + $q['orderIndex'] = (int)$q['orderIndex']; + $q['retiredAt'] = (int)($q['retiredAt'] ?? 0); + $q['retiredInRevision'] = (int)($q['retiredInRevision'] ?? 0); + $q['retired'] = $q['retiredAt'] > 0; + $cfg = qdb_parse_config_json($q['configJson']); + $q['configJson'] = json_encode($cfg, JSON_UNESCAPED_UNICODE); + $q['questionKey'] = qdb_question_key($cfg, $q['defaultText']); + $q['localId'] = qdb_question_local_id($q['questionID'], $qnID); + $ao = $pdo->prepare( + "SELECT answerOptionID, questionID, defaultText, points, orderIndex, nextQuestionId, retiredAt + FROM answer_option WHERE questionID = :qid AND " . qdb_active_options_clause('answer_option') . ' + ORDER BY orderIndex' + ); + $ao->execute([':qid' => $q['questionID']]); + $q['answerOptions'] = $ao->fetchAll(PDO::FETCH_ASSOC); + foreach ($q['answerOptions'] as &$opt) { + $opt['points'] = (int)$opt['points']; + $opt['orderIndex'] = (int)$opt['orderIndex']; + $opt['optionKey'] = qdb_option_key($opt); + $opt['labelGerman'] = qdb_option_german_label($pdo, $opt['answerOptionID'], $opt['defaultText']); + } + unset($opt); + if (($q['type'] ?? '') === 'glass_scale_question') { + $q['glassSymptoms'] = qdb_glass_symptoms_with_score_rules($pdo, $q['questionID'], $cfg); + } elseif (in_array($q['type'] ?? '', ['slider_question', 'value_spinner'], true)) { + $q['scoreRules'] = qdb_get_score_rules_for_question($pdo, $q['questionID']); + } + $tr = $pdo->prepare("SELECT languageCode, text FROM question_translation WHERE questionID = :qid"); + $tr->execute([':qid' => $q['questionID']]); + $q['translations'] = $tr->fetchAll(PDO::FETCH_ASSOC); + } + unset($q); + qdb_discard($tmpDb, $lockFp); + json_success([ + 'questions' => $questions, + 'structureRevision' => $structureRevision, + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Load questions', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'POST': + require_role(['admin', 'supervisor'], $tokenRec); + $body = read_json_body(); + if (empty($body['questionnaireID']) || !isset($body['defaultText']) || empty($body['questionKey'])) { + json_error('BAD_REQUEST', 'questionnaireID, questionKey, and defaultText required', 400); + } + $qnID = $body['questionnaireID']; + $text = trim($body['defaultText']); + qdb_require_non_empty_german($text); + $questionKey = qdb_validate_stable_key((string)$body['questionKey'], 'Question key'); + $type = trim($body['type'] ?? ''); + $order = (int)($body['orderIndex'] ?? 0); + $req = (int)($body['isRequired'] ?? 0); + $cfg = qdb_parse_config_json($body['configJson'] ?? '{}'); + $glassSymptoms = qdb_parse_glass_symptoms_body($body, $cfg); + if ($type === 'glass_scale_question') { + $cfg = qdb_apply_glass_symptoms_config($cfg, $glassSymptoms); + } + $config = qdb_normalize_question_config( + $cfg, + $questionKey, + $body['noteBefore'] ?? ($cfg['noteBefore'] ?? null), + $body['noteAfter'] ?? ($cfg['noteAfter'] ?? null) + ); + $configJson = json_encode($config, JSON_UNESCAPED_UNICODE); + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + try { + $chk = $pdo->prepare("SELECT 1 FROM questionnaire WHERE questionnaireID = :id"); + $chk->execute([':id' => $qnID]); + if (!$chk->fetch()) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Questionnaire not found', 404); + } + if ($order === 0) { + $max = $pdo->prepare("SELECT COALESCE(MAX(orderIndex),0)+1 FROM question WHERE questionnaireID = :id"); + $max->execute([':id' => $qnID]); + $order = (int)$max->fetchColumn(); + } + $localId = trim($body['localId'] ?? ''); + if ($localId === '') { + $localId = qdb_allocate_question_local_id($pdo, $qnID); + } + $id = qdb_make_question_id($qnID, $localId); + $dup = $pdo->prepare('SELECT 1 FROM question WHERE questionID = :id'); + $dup->execute([':id' => $id]); + if ($dup->fetch()) { + qdb_discard($tmpDb, $lockFp); + json_error('CONFLICT', "Question id \"$localId\" already exists in this questionnaire", 409); + } + $pdo->prepare("INSERT INTO question (questionID, questionnaireID, defaultText, type, orderIndex, isRequired, configJson) + VALUES (:id, :qid, :t, :ty, :o, :r, :cj)") + ->execute([':id' => $id, ':qid' => $qnID, ':t' => $text, ':ty' => $type, + ':o' => $order, ':r' => $req, ':cj' => $configJson]); + qdb_upsert_source_translation($pdo, 'question', $id, $text); + qdb_sync_question_note_strings($pdo, $questionKey, $config); + if ($type === 'glass_scale_question') { + qdb_sync_glass_symptom_strings($pdo, $glassSymptoms); + } + $scoreRules = qdb_parse_score_rules_body($body, $type, $config); + if ($scoreRules !== [] || in_array($type, ['slider_question', 'value_spinner', 'glass_scale_question'], true)) { + qdb_sync_question_score_rules($pdo, $id, $scoreRules); + } + $newRev = qdb_bump_structure_revision($pdo, $qnID, 'question_added'); + qdb_save($tmpDb, $lockFp); + json_success([ + 'structureRevision' => $newRev, + 'question' => [ + 'questionID' => $id, 'questionnaireID' => $qnID, 'defaultText' => $text, + 'questionKey' => $questionKey, 'localId' => $localId, + 'type' => $type, 'orderIndex' => $order, 'isRequired' => $req, + 'configJson' => $configJson, 'answerOptions' => [], 'translations' => [], + 'glassSymptoms' => $type === 'glass_scale_question' + ? qdb_glass_symptoms_with_score_rules($pdo, $id, $config) : [], + 'scoreRules' => in_array($type, ['slider_question', 'value_spinner'], true) + ? qdb_get_score_rules_for_question($pdo, $id) : [], + ], + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Questions', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'PUT': + require_role(['admin', 'supervisor'], $tokenRec); + $body = read_json_body(); + if (empty($body['questionID'])) { + json_error('BAD_REQUEST', 'questionID is required', 400); + } + $id = $body['questionID']; + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + try { + $existing = $pdo->prepare("SELECT * FROM question WHERE questionID = :id"); + $existing->execute([':id' => $id]); + $row = $existing->fetch(PDO::FETCH_ASSOC); + if (!$row) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Question not found', 404); + } + $text = trim($body['defaultText'] ?? $row['defaultText']); + qdb_require_non_empty_german($text); + $type = trim($body['type'] ?? $row['type']); + $order = (int)($body['orderIndex'] ?? $row['orderIndex']); + $req = (int)($body['isRequired'] ?? $row['isRequired']); + $oldCfg = qdb_parse_config_json($row['configJson']); + $questionKey = isset($body['questionKey']) + ? qdb_validate_stable_key((string)$body['questionKey'], 'Question key') + : qdb_question_key($oldCfg, $row['defaultText']); + if ($questionKey === '') { + json_error('MISSING_FIELDS', 'questionKey is required', 400); + } + $cfgIn = isset($body['configJson']) ? qdb_parse_config_json($body['configJson']) : $oldCfg; + $glassSymptoms = qdb_parse_glass_symptoms_body($body, $cfgIn); + if ($type === 'glass_scale_question') { + $cfgIn = qdb_apply_glass_symptoms_config($cfgIn, $glassSymptoms); + } + $config = qdb_normalize_question_config( + $cfgIn, + $questionKey, + $body['noteBefore'] ?? ($cfgIn['noteBefore'] ?? null), + $body['noteAfter'] ?? ($cfgIn['noteAfter'] ?? null) + ); + $configJson = json_encode($config, JSON_UNESCAPED_UNICODE); + $oldKey = qdb_question_key($oldCfg, $row['defaultText']); + $oldSymptoms = json_encode($oldCfg['symptoms'] ?? [], JSON_UNESCAPED_UNICODE); + $newSymptoms = json_encode($config['symptoms'] ?? [], JSON_UNESCAPED_UNICODE); + $structuralChange = ($type !== (string)$row['type']) + || ($req !== (int)$row['isRequired']) + || ($questionKey !== $oldKey) + || ($newSymptoms !== $oldSymptoms); + if ((int)($row['retiredAt'] ?? 0) > 0) { + qdb_discard($tmpDb, $lockFp); + json_error('INVALID_FIELD', 'Cannot edit a retired question', 400); + } + $pdo->prepare("UPDATE question SET defaultText = :t, type = :ty, orderIndex = :o, isRequired = :r, configJson = :cj WHERE questionID = :id") + ->execute([':t' => $text, ':ty' => $type, ':o' => $order, ':r' => $req, ':cj' => $configJson, ':id' => $id]); + qdb_upsert_source_translation($pdo, 'question', $id, $text); + qdb_sync_question_note_strings($pdo, $questionKey, $config); + if ($type === 'glass_scale_question') { + qdb_sync_glass_symptom_strings($pdo, $glassSymptoms); + } + $scoreRules = qdb_parse_score_rules_body($body, $type, $config); + if ($scoreRules !== [] || in_array($type, ['slider_question', 'value_spinner', 'glass_scale_question'], true)) { + qdb_sync_question_score_rules($pdo, $id, $scoreRules); + } + $newRev = null; + if ($structuralChange) { + $newRev = qdb_bump_structure_revision($pdo, (string)$row['questionnaireID'], 'question_updated'); + } + qdb_save($tmpDb, $lockFp); + json_success([ + 'structureRevision' => $newRev ?? qdb_questionnaire_structure_revision($pdo, (string)$row['questionnaireID']), + 'question' => [ + 'questionID' => $id, 'questionnaireID' => $row['questionnaireID'], + 'defaultText' => $text, 'questionKey' => $questionKey, + 'type' => $type, 'orderIndex' => $order, 'isRequired' => $req, + 'configJson' => $configJson, + 'glassSymptoms' => $type === 'glass_scale_question' + ? qdb_glass_symptoms_with_score_rules($pdo, $id, $config) : [], + 'scoreRules' => in_array($type, ['slider_question', 'value_spinner'], true) + ? qdb_get_score_rules_for_question($pdo, $id) : [], + ], + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Questions', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'DELETE': + require_role(['admin', 'supervisor'], $tokenRec); + $body = read_json_body(); + if (empty($body['questionID'])) { + json_error('BAD_REQUEST', 'questionID is required', 400); + } + $id = $body['questionID']; + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + try { + $existing = $pdo->prepare('SELECT questionnaireID, retiredAt FROM question WHERE questionID = :id'); + $existing->execute([':id' => $id]); + $row = $existing->fetch(PDO::FETCH_ASSOC); + if (!$row) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Question not found', 404); + } + $qnID = (string)$row['questionnaireID']; + if ((int)($row['retiredAt'] ?? 0) > 0) { + qdb_discard($tmpDb, $lockFp); + json_success(['deleted' => false, 'retired' => true, 'structureRevision' => qdb_questionnaire_structure_revision($pdo, $qnID)]); + break; + } + + $pdo->beginTransaction(); + $newRev = qdb_bump_structure_revision($pdo, $qnID, 'question_removed'); + + if (qdb_question_has_client_data($pdo, $id)) { + qdb_retire_question($pdo, $id, $newRev); + $pdo->commit(); + qdb_save($tmpDb, $lockFp); + json_success([ + 'deleted' => false, + 'retired' => true, + 'structureRevision' => $newRev, + ]); + break; + } + + $pdo->exec('PRAGMA foreign_keys = OFF;'); + $aoIds = $pdo->prepare('SELECT answerOptionID FROM answer_option WHERE questionID = :id'); + $aoIds->execute([':id' => $id]); + foreach ($aoIds->fetchAll(PDO::FETCH_COLUMN) as $aoid) { + $pdo->prepare('DELETE FROM answer_option_translation WHERE answerOptionID = :id')->execute([':id' => $aoid]); + } + $pdo->prepare('DELETE FROM answer_option WHERE questionID = :id')->execute([':id' => $id]); + $pdo->prepare('DELETE FROM question_score_rule WHERE questionID = :id')->execute([':id' => $id]); + $pdo->prepare('DELETE FROM question_translation WHERE questionID = :id')->execute([':id' => $id]); + $pdo->prepare('DELETE FROM question WHERE questionID = :id')->execute([':id' => $id]); + $pdo->exec('PRAGMA foreign_keys = ON;'); + $pdo->commit(); + qdb_save($tmpDb, $lockFp); + json_success([ + 'deleted' => true, + 'retired' => false, + 'structureRevision' => $newRev, + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Delete question', $pdo ?? null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'PATCH': + require_role(['admin', 'supervisor'], $tokenRec); + $body = read_json_body(); + if (empty($body['questionnaireID']) || !is_array($body['order'] ?? null)) { + json_error('BAD_REQUEST', 'questionnaireID and order[] required', 400); + } + $qnID = (string)$body['questionnaireID']; + $orderIds = array_values(array_filter( + array_map('strval', $body['order']), + static fn($id) => $id !== '' + )); + if ($orderIds === []) { + json_error('BAD_REQUEST', 'order[] must list at least one questionID', 400); + } + try { + $opened = qdb_open_write_or_fail(); + [$pdo, $tmpDb, $lockFp] = $opened; + $chk = $pdo->prepare('SELECT 1 FROM questionnaire WHERE questionnaireID = :id'); + $chk->execute([':id' => $qnID]); + if (!$chk->fetch()) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Questionnaire not found', 404); + } + $ph = implode(',', array_fill(0, count($orderIds), '?')); + $stmt = $pdo->prepare( + "SELECT questionID FROM question WHERE questionnaireID = ? AND questionID IN ($ph)" + ); + $stmt->execute(array_merge([$qnID], $orderIds)); + $found = $stmt->fetchAll(PDO::FETCH_COLUMN); + if (count($found) !== count($orderIds)) { + $missing = array_values(array_diff($orderIds, $found)); + qdb_discard($tmpDb, $lockFp); + json_error( + 'INVALID_FIELD', + 'order[] contains questionIDs not in this questionnaire', + 400, + ['missingQuestionIDs' => $missing] + ); + } + $upd = $pdo->prepare( + 'UPDATE question SET orderIndex = :o WHERE questionID = :id AND questionnaireID = :qid' + ); + foreach ($orderIds as $idx => $qid) { + $upd->execute([':o' => $idx, ':id' => $qid, ':qid' => $qnID]); + } + qdb_save($tmpDb, $lockFp); + json_success(['reordered' => true]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Reorder questions', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +default: + json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); +} diff --git a/handlers/results.php b/handlers/results.php new file mode 100644 index 0000000..234c97d --- /dev/null +++ b/handlers/results.php @@ -0,0 +1,124 @@ +prepare("SELECT * FROM questionnaire WHERE questionnaireID = :id"); +$qn->execute([':id' => $qnID]); +$questionnaire = $qn->fetch(PDO::FETCH_ASSOC); +if (!$questionnaire) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Questionnaire not found', 404); +} + +$qStmt = $pdo->prepare(" + SELECT questionID, defaultText, type, orderIndex, isRequired, configJson + FROM question WHERE questionnaireID = :id ORDER BY orderIndex +"); +$qStmt->execute([':id' => $qnID]); +$questions = $qStmt->fetchAll(PDO::FETCH_ASSOC); +$questionIDs = array_column($questions, 'questionID'); + +foreach ($questions as &$q) { + $q['isRequired'] = (int)$q['isRequired']; + $q['orderIndex'] = (int)$q['orderIndex']; + $cfg = json_decode($q['configJson'] ?? '{}', true) ?: []; + $q['questionKey'] = qdb_question_column_label($cfg, $q['defaultText'], $q['questionID'], $qnID); + $ao = $pdo->prepare("SELECT answerOptionID, defaultText, points, orderIndex + FROM answer_option WHERE questionID = :qid ORDER BY orderIndex"); + $ao->execute([':qid' => $q['questionID']]); + $q['answerOptions'] = $ao->fetchAll(PDO::FETCH_ASSOC); + foreach ($q['answerOptions'] as &$opt) { + $opt['points'] = (int)$opt['points']; + $opt['orderIndex'] = (int)$opt['orderIndex']; + } +} +unset($q); + +[$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl'); + +$sql = " + SELECT cl.clientCode, cl.coachID, + co.username AS coachUsername, + sv.username AS supervisorUsername, + cq.status, cq.startedAt, cq.completedAt, cq.sumPoints, cq.assignedByCoach + FROM client cl + INNER JOIN completed_questionnaire cq ON cq.clientCode = cl.clientCode AND cq.questionnaireID = :qnid + LEFT JOIN coach co ON co.coachID = cl.coachID + LEFT JOIN supervisor sv ON sv.supervisorID = co.supervisorID + WHERE $rbacClause +"; +$params = array_merge([':qnid' => $qnID], $rbacParams); + +if ($clientCode) { + $sql .= " AND cl.clientCode = :cc"; + $params[':cc'] = $clientCode; +} +$sql .= " ORDER BY cl.clientCode"; + +$cStmt = $pdo->prepare($sql); +$cStmt->execute($params); +$clients = $cStmt->fetchAll(PDO::FETCH_ASSOC); + +if (!empty($questionIDs) && !empty($clients)) { + $qPlaceholders = implode(',', array_fill(0, count($questionIDs), '?')); + $answerStmt = $pdo->prepare(" + SELECT clientCode, questionID, answerOptionID, freeTextValue, numericValue, answeredAt + FROM client_answer + WHERE clientCode = ? AND questionID IN ($qPlaceholders) + "); + + foreach ($clients as &$c) { + $c['sumPoints'] = $c['sumPoints'] !== null ? (int)$c['sumPoints'] : null; + $c['startedAt'] = $c['startedAt'] !== null ? (int)$c['startedAt'] : null; + $c['completedAt'] = $c['completedAt'] !== null ? (int)$c['completedAt'] : null; + + $bindParams = array_merge([$c['clientCode']], $questionIDs); + $answerStmt->execute($bindParams); + $answers = $answerStmt->fetchAll(PDO::FETCH_ASSOC); + $answerMap = []; + foreach ($answers as $a) { + $answerMap[$a['questionID']] = [ + 'answerOptionID' => $a['answerOptionID'], + 'freeTextValue' => $a['freeTextValue'], + 'numericValue' => $a['numericValue'] !== null ? (float)$a['numericValue'] : null, + 'answeredAt' => $a['answeredAt'] !== null ? (int)$a['answeredAt'] : null, + ]; + } + $c['answers'] = $answerMap; + } + unset($c); +} else { + foreach ($clients as &$c) { + $c['sumPoints'] = $c['sumPoints'] !== null ? (int)$c['sumPoints'] : null; + $c['startedAt'] = $c['startedAt'] !== null ? (int)$c['startedAt'] : null; + $c['completedAt'] = $c['completedAt'] !== null ? (int)$c['completedAt'] : null; + $c['answers'] = (object)[]; + } + unset($c); +} + + qdb_discard($tmpDb, $lockFp); + + json_success([ + 'questionnaire' => $questionnaire, + 'questions' => $questions, + 'clients' => $clients, + ]); +} catch (Throwable $e) { + qdb_handler_fail($e, 'Load results', null, $tmpDb ?? null, $lockFp ?? null); +} diff --git a/handlers/scoring_profiles.php b/handlers/scoring_profiles.php new file mode 100644 index 0000000..f15c37d --- /dev/null +++ b/handlers/scoring_profiles.php @@ -0,0 +1,203 @@ + $profile]); + } + json_success(['profiles' => qdb_list_scoring_profiles($pdo)]); + qdb_discard($tmpDb, $lockFp); + } catch (Throwable $e) { + qdb_handler_fail($e, 'List scoring profiles', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'POST': + require_role(['admin', 'supervisor'], $tokenRec); + $body = read_json_body(); + $name = trim($body['name'] ?? ''); + if ($name === '') { + json_error('BAD_REQUEST', 'name is required', 400); + } + $bands = qdb_parse_scoring_bands_body($body); + $bandErr = qdb_validate_scoring_bands($bands); + if ($bandErr !== null) { + json_error('INVALID_FIELD', $bandErr, 400); + } + $members = qdb_parse_profile_questionnaires($body['questionnaires'] ?? []); + if ($members === []) { + json_error('INVALID_FIELD', 'At least one questionnaire is required', 400); + } + try { + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + $profileID = bin2hex(random_bytes(16)); + $now = time(); + $pdo->prepare( + 'INSERT INTO scoring_profile (profileID, name, description, isActive, + greenMin, greenMax, yellowMin, yellowMax, redMin, createdAt, updatedAt) + VALUES (:id, :name, :desc, :act, :gmin, :gmax, :ymin, :ymax, :rmin, :ca, :ua)' + )->execute([ + ':id' => $profileID, + ':name' => $name, + ':desc' => trim($body['description'] ?? ''), + ':act' => !empty($body['isActive']) ? 1 : 0, + ':gmin' => $bands['greenMin'], + ':gmax' => $bands['greenMax'], + ':ymin' => $bands['yellowMin'], + ':ymax' => $bands['yellowMax'], + ':rmin' => $bands['redMin'], + ':ca' => $now, + ':ua' => $now, + ]); + qdb_sync_profile_questionnaires($pdo, $profileID, $members); + qdb_recompute_all_profiles_after_change($pdo); + qdb_save($tmpDb, $lockFp); + json_success(['profile' => qdb_get_scoring_profile($pdo, $profileID)]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Create scoring profile', $pdo ?? null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'PUT': + require_role(['admin', 'supervisor'], $tokenRec); + $body = read_json_body(); + $profileID = trim($body['profileID'] ?? ''); + if ($profileID === '') { + json_error('BAD_REQUEST', 'profileID is required', 400); + } + try { + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + $existing = qdb_get_scoring_profile($pdo, $profileID); + if (!$existing) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Scoring profile not found', 404); + } + $name = trim($body['name'] ?? $existing['name']); + $bands = qdb_normalize_scoring_bands(array_merge($existing, $body)); + $bandErr = qdb_validate_scoring_bands($bands); + if ($bandErr !== null) { + qdb_discard($tmpDb, $lockFp); + json_error('INVALID_FIELD', $bandErr, 400); + } + $members = isset($body['questionnaires']) + ? qdb_parse_profile_questionnaires($body['questionnaires']) + : null; + if ($members !== null && $members === []) { + qdb_discard($tmpDb, $lockFp); + json_error('INVALID_FIELD', 'At least one questionnaire is required', 400); + } + $pdo->prepare( + 'UPDATE scoring_profile SET name = :name, description = :desc, isActive = :act, + greenMin = :gmin, greenMax = :gmax, yellowMin = :ymin, yellowMax = :ymax, redMin = :rmin, + updatedAt = :ua WHERE profileID = :id' + )->execute([ + ':name' => $name, + ':desc' => trim($body['description'] ?? $existing['description']), + ':act' => isset($body['isActive']) ? (!empty($body['isActive']) ? 1 : 0) : $existing['isActive'], + ':gmin' => $bands['greenMin'], + ':gmax' => $bands['greenMax'], + ':ymin' => $bands['yellowMin'], + ':ymax' => $bands['yellowMax'], + ':rmin' => $bands['redMin'], + ':ua' => time(), + ':id' => $profileID, + ]); + if ($members !== null) { + qdb_sync_profile_questionnaires($pdo, $profileID, $members); + } + qdb_recompute_all_profiles_after_change($pdo, $profileID); + qdb_save($tmpDb, $lockFp); + json_success(['profile' => qdb_get_scoring_profile($pdo, $profileID)]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Update scoring profile', $pdo ?? null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'DELETE': + require_role(['admin', 'supervisor'], $tokenRec); + $body = read_json_body(); + $profileID = trim($body['profileID'] ?? ''); + if ($profileID === '') { + json_error('BAD_REQUEST', 'profileID is required', 400); + } + try { + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + $pdo->prepare('DELETE FROM scoring_profile WHERE profileID = :id')->execute([':id' => $profileID]); + qdb_save($tmpDb, $lockFp); + json_success(['deleted' => true]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Delete scoring profile', $pdo ?? null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +default: + json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); +} + +/** + * @return list + */ +function qdb_parse_profile_questionnaires(array $rows): array { + $out = []; + $idx = 0; + foreach ($rows as $row) { + if (!is_array($row)) { + continue; + } + $qnID = trim((string)($row['questionnaireID'] ?? '')); + if ($qnID === '') { + continue; + } + $weight = (float)($row['weight'] ?? 1.0); + if ($weight <= 0) { + $weight = 1.0; + } + $out[] = [ + 'questionnaireID' => $qnID, + 'weight' => $weight, + 'orderIndex' => (int)($row['orderIndex'] ?? $idx), + ]; + $idx++; + } + return $out; +} + +function qdb_sync_profile_questionnaires(PDO $pdo, string $profileID, array $members): void { + $pdo->prepare('DELETE FROM scoring_profile_questionnaire WHERE profileID = :pid') + ->execute([':pid' => $profileID]); + $ins = $pdo->prepare( + 'INSERT INTO scoring_profile_questionnaire (profileID, questionnaireID, weight, orderIndex) + VALUES (:pid, :qn, :w, :o)' + ); + foreach ($members as $m) { + $ins->execute([ + ':pid' => $profileID, + ':qn' => $m['questionnaireID'], + ':w' => $m['weight'], + ':o' => $m['orderIndex'], + ]); + } +} + +function qdb_recompute_all_profiles_after_change(PDO $pdo, ?string $profileID = null): void { + $clients = $pdo->query('SELECT DISTINCT clientCode FROM completed_questionnaire WHERE status = \'completed\'') + ->fetchAll(PDO::FETCH_COLUMN); + foreach ($clients as $clientCode) { + qdb_recompute_profile_scores_for_client($pdo, (string)$clientCode); + } +} diff --git a/handlers/session.php b/handlers/session.php new file mode 100644 index 0000000..226a054 --- /dev/null +++ b/handlers/session.php @@ -0,0 +1,44 @@ +prepare('SELECT username FROM users WHERE userID = :uid'); + $stmt->execute([':uid' => $rec['userID']]); + $row = $stmt->fetchColumn(); + $username = $row !== false ? (string)$row : ''; + qdb_discard($tmpDb, $lockFp); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Session check', null, $tmpDb ?? null, $lockFp ?? null); + } +} + +json_success([ + 'valid' => true, + 'user' => $username, + 'role' => $role, + 'userID' => $rec['userID'] ?? '', + 'mustChangePassword' => !empty($rec['temp']), +]); diff --git a/handlers/settings.php b/handlers/settings.php new file mode 100644 index 0000000..cc1433b --- /dev/null +++ b/handlers/settings.php @@ -0,0 +1,70 @@ +query('SELECT COUNT(*) FROM session')->fetchColumn(); + qdb_discard($tmpDb, $lockFp); + json_success([ + 'settings' => $settings, + 'activeSessions' => $sessionCount, + 'defaults' => qdb_settings_defaults(), + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Load settings', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'PUT': +case 'PATCH': + $body = read_json_body(); + try { + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + $current = qdb_settings_get_on_pdo($pdo); + $merged = qdb_settings_validate_and_merge($body, $current); + qdb_settings_save_on_pdo($pdo, $merged); + qdb_save($tmpDb, $lockFp); + json_success(['settings' => $merged]); + } catch (InvalidArgumentException $e) { + json_error('INVALID_FIELD', $e->getMessage(), 400); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Save settings', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'POST': + $body = read_json_body(); + $action = trim((string)($body['action'] ?? '')); + if ($action !== 'revokeAllSessions') { + json_error('BAD_REQUEST', 'Unknown action', 400); + } + $confirm = trim((string)($body['confirmPhrase'] ?? '')); + $revokeAllPhrase = 'REVOKE ALL SESSIONS'; + if ($confirm !== $revokeAllPhrase) { + json_error( + 'CONFIRMATION_REQUIRED', + 'Type exactly "' . $revokeAllPhrase . '" to revoke every session', + 400 + ); + } + try { + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + $removed = token_revoke_all_on_pdo($pdo); + qdb_save($tmpDb, $lockFp); + json_success(['revokedSessions' => $removed]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Revoke all sessions', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +default: + json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); +} diff --git a/handlers/translations.php b/handlers/translations.php new file mode 100644 index 0000000..44d109d --- /dev/null +++ b/handlers/translations.php @@ -0,0 +1,302 @@ + 'application/json; charset=UTF-8', + 'Content-Disposition' => 'attachment; filename="' . $filename . '"', + ] + ); + } + + if (isset($_GET['languages'])) { + try { + $opened = qdb_open_read_or_fail(); + [$pdo, $tmpDb, $lockFp] = $opened; + $rows = $pdo->query("SELECT languageCode, name FROM language ORDER BY languageCode")->fetchAll(PDO::FETCH_ASSOC); + qdb_discard($tmpDb, $lockFp); + json_success(['languages' => $rows]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Load languages', null, $tmpDb ?? null, $lockFp ?? null); + } + } + + if (!empty($_GET['all'])) { + try { + $opened = qdb_open_read_or_fail(); + [$pdo, $tmpDb, $lockFp] = $opened; + + $languages = $pdo->query("SELECT languageCode, name FROM language ORDER BY languageCode")->fetchAll(PDO::FETCH_ASSOC); + if (!array_filter($languages, fn($l) => $l['languageCode'] === QDB_SOURCE_LANGUAGE)) { + array_unshift($languages, ['languageCode' => QDB_SOURCE_LANGUAGE, 'name' => 'German']); + } + + $qnRows = $pdo->query(" + SELECT questionnaireID, name, orderIndex FROM questionnaire ORDER BY orderIndex + ")->fetchAll(PDO::FETCH_ASSOC); + + $appEntries = qdb_app_ui_string_entries_for_website($pdo); + $appTranslations = qdb_load_translations_for_entries($pdo, $appEntries); + qdb_attach_translation_texts($appEntries, $appTranslations); + + $questionnaires = []; + $allEntries = $appEntries; + + foreach ($qnRows as $qn) { + $lists = qdb_translation_entry_lists($pdo, $qn['questionnaireID']); + $entries = array_merge($lists['stringEntries'], $lists['contentEntries']); + if (!$entries) { + continue; + } + $translations = qdb_load_translations_for_entries($pdo, $entries); + qdb_attach_translation_texts($entries, $translations); + $questionnaires[] = [ + 'questionnaireID' => $qn['questionnaireID'], + 'name' => $qn['name'], + 'orderIndex' => (int)$qn['orderIndex'], + 'entries' => $entries, + ]; + foreach ($entries as $e) { + $allEntries[] = $e; + } + } + + qdb_discard($tmpDb, $lockFp); + json_success([ + 'languages' => $languages, + 'appStrings' => $appEntries, + 'questionnaires' => $questionnaires, + 'entries' => $allEntries, + 'sourceLanguage' => QDB_SOURCE_LANGUAGE, + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Load translations', null, $tmpDb ?? null, $lockFp ?? null); + } + } + + $qnID = trim((string)($_GET['questionnaireID'] ?? '')); + if ($qnID !== '') { + try { + $opened = qdb_open_read_or_fail(); + [$pdo, $tmpDb, $lockFp] = $opened; + + $languages = $pdo->query("SELECT languageCode, name FROM language ORDER BY languageCode")->fetchAll(PDO::FETCH_ASSOC); + if (!array_filter($languages, fn($l) => $l['languageCode'] === QDB_SOURCE_LANGUAGE)) { + array_unshift($languages, ['languageCode' => QDB_SOURCE_LANGUAGE, 'name' => 'German']); + } + + $qnRow = $pdo->prepare('SELECT name FROM questionnaire WHERE questionnaireID = :id'); + $qnRow->execute([':id' => $qnID]); + $qnName = $qnRow->fetchColumn(); + if ($qnName === false) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Questionnaire not found', 404); + } + + $lists = qdb_translation_entry_lists($pdo, $qnID); + $entries = array_merge($lists['stringEntries'], $lists['contentEntries']); + $translations = qdb_load_translations_for_entries($pdo, $entries); + qdb_attach_translation_texts($entries, $translations); + + qdb_discard($tmpDb, $lockFp); + json_success([ + 'languages' => $languages, + 'entries' => $entries, + 'questionnaire' => ['questionnaireID' => $qnID, 'name' => (string)$qnName], + 'sourceLanguage' => QDB_SOURCE_LANGUAGE, + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Load translations', null, $tmpDb ?? null, $lockFp ?? null); + } + } + + $type = $_GET['type'] ?? ''; + $id = $_GET['id'] ?? ''; + if (!in_array($type, $validTypes, true) || (!$id && $type !== 'string')) { + json_error('BAD_REQUEST', 'type (question|answer_option|string) and id required', 400); + } + + try { + $opened = qdb_open_read_or_fail(); + [$pdo, $tmpDb, $lockFp] = $opened; + if ($type === 'question') { + $stmt = $pdo->prepare("SELECT languageCode, text FROM question_translation WHERE questionID = :id"); + $stmt->execute([':id' => $id]); + } elseif ($type === 'answer_option') { + $stmt = $pdo->prepare("SELECT languageCode, text FROM answer_option_translation WHERE answerOptionID = :id"); + $stmt->execute([':id' => $id]); + } else { + if ($id) { + $stmt = $pdo->prepare("SELECT stringKey, languageCode, text FROM string_translation WHERE stringKey = :id"); + $stmt->execute([':id' => $id]); + } else { + $stmt = $pdo->query("SELECT stringKey, languageCode, text FROM string_translation ORDER BY stringKey, languageCode"); + } + } + if ($type === 'question' || $type === 'answer_option') { + if (!qdb_translation_entity_exists($pdo, $type, (string)$id)) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Translation entity not found', 404); + } + } + + $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); + qdb_discard($tmpDb, $lockFp); + json_success(['translations' => $rows]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Load translations', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'POST': + require_role(['admin', 'supervisor'], $tokenRec); + $body = read_json_body(); + if (($body['action'] ?? '') === 'importBundle') { + $bundle = $body['bundle'] ?? null; + if (!is_array($bundle)) { + json_error('MISSING_FIELDS', 'bundle object is required', 400); + } + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + try { + $result = qdb_import_translations_bundle($pdo, $bundle); + qdb_save($tmpDb, $lockFp); + json_success($result); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Import translations', null, $tmpDb, $lockFp); + } + break; + } + json_error('BAD_REQUEST', 'Unknown action', 400); + break; + +case 'PUT': + require_role(['admin', 'supervisor'], $tokenRec); + $body = read_json_body(); + $type = $body['type'] ?? ''; + + if ($type === 'language') { + $lang = trim($body['languageCode'] ?? ''); + $name = trim($body['name'] ?? ''); + if (!$lang) { + json_error('MISSING_FIELDS', 'languageCode required', 400); + } + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + try { + if ($lang === QDB_SOURCE_LANGUAGE) { + qdb_ensure_source_language($pdo); + } else { + $pdo->prepare("INSERT INTO language (languageCode, name) VALUES (:lc, :n) + ON CONFLICT(languageCode) DO UPDATE SET name = excluded.name") + ->execute([':lc' => $lang, ':n' => $name]); + } + qdb_save($tmpDb, $lockFp); + json_success([]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Save language', null, $tmpDb, $lockFp); + } + break; + } + + $id = $body['id'] ?? ''; + $lang = trim($body['languageCode'] ?? ''); + $text = $body['text'] ?? ''; + if (!in_array($type, $validTypes, true) || !$id || !$lang) { + json_error('MISSING_FIELDS', 'type, id, and languageCode required', 400); + } + + try { + $opened = qdb_open_write_or_fail(); + [$pdo, $tmpDb, $lockFp] = $opened; + if (!qdb_translation_entity_exists($pdo, $type, (string)$id)) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Translation entity not found', 404); + } + qdb_put_translation($pdo, $type, $id, $lang, $text); + qdb_save($tmpDb, $lockFp); + json_success([]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Save translation', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'DELETE': + require_role(['admin', 'supervisor'], $tokenRec); + $body = read_json_body(); + $type = $body['type'] ?? ''; + + if ($type === 'language') { + $lang = trim($body['languageCode'] ?? ''); + if (!$lang) { + json_error('MISSING_FIELDS', 'languageCode required', 400); + } + if ($lang === QDB_SOURCE_LANGUAGE) { + json_error('BAD_REQUEST', 'German (de) cannot be removed', 400); + } + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + try { + $pdo->prepare("DELETE FROM language WHERE languageCode = :lc")->execute([':lc' => $lang]); + $pdo->prepare("DELETE FROM question_translation WHERE languageCode = :lc")->execute([':lc' => $lang]); + $pdo->prepare("DELETE FROM answer_option_translation WHERE languageCode = :lc")->execute([':lc' => $lang]); + $pdo->prepare("DELETE FROM string_translation WHERE languageCode = :lc")->execute([':lc' => $lang]); + qdb_save($tmpDb, $lockFp); + json_success([]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Delete language', null, $tmpDb, $lockFp); + } + break; + } + + $id = $body['id'] ?? ''; + $lang = trim($body['languageCode'] ?? ''); + if (!in_array($type, $validTypes, true) || !$id || !$lang) { + json_error('MISSING_FIELDS', 'type, id, and languageCode required', 400); + } + + try { + $opened = qdb_open_write_or_fail(); + [$pdo, $tmpDb, $lockFp] = $opened; + if ($type === 'question' || $type === 'answer_option') { + if (!qdb_translation_entity_exists($pdo, $type, (string)$id)) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Translation entity not found', 404); + } + } + if ($type === 'question') { + $pdo->prepare('DELETE FROM question_translation WHERE questionID = :id AND languageCode = :lang') + ->execute([':id' => $id, ':lang' => $lang]); + } elseif ($type === 'answer_option') { + $pdo->prepare('DELETE FROM answer_option_translation WHERE answerOptionID = :id AND languageCode = :lang') + ->execute([':id' => $id, ':lang' => $lang]); + } else { + $pdo->prepare('DELETE FROM string_translation WHERE stringKey = :id AND languageCode = :lang') + ->execute([':id' => $id, ':lang' => $lang]); + } + qdb_save($tmpDb, $lockFp); + json_success([]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Delete translation', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +default: + json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); +} diff --git a/handlers/users.php b/handlers/users.php new file mode 100644 index 0000000..bc1b69c --- /dev/null +++ b/handlers/users.php @@ -0,0 +1,427 @@ +query( + "SELECT u.userID, u.username, u.role, u.entityID, u.mustChangePassword, u.createdAt, + COALESCE(a.location, s.location, '') AS location, + c.supervisorID, + sv.username AS supervisorUsername + FROM users u + LEFT JOIN admin a ON a.adminID = u.entityID AND u.role = 'admin' + LEFT JOIN supervisor s ON s.supervisorID = u.entityID AND u.role = 'supervisor' + LEFT JOIN coach c ON c.coachID = u.entityID AND u.role = 'coach' + LEFT JOIN supervisor sv ON sv.supervisorID = c.supervisorID + ORDER BY u.role, u.username" + )->fetchAll(PDO::FETCH_ASSOC); + + $supervisors = $pdo->query( + "SELECT supervisorID, username, location FROM supervisor ORDER BY username" + )->fetchAll(PDO::FETCH_ASSOC); + } else { + $svNameStmt = $pdo->prepare("SELECT username FROM supervisor WHERE supervisorID = :svid"); + $svNameStmt->execute([':svid' => $callerEntityID]); + $svName = $svNameStmt->fetchColumn() ?: ''; + + $stmt = $pdo->prepare( + "SELECT u.userID, u.username, u.role, u.entityID, u.mustChangePassword, u.createdAt, + '' AS location, c.supervisorID, :svname AS supervisorUsername + FROM users u + JOIN coach c ON c.coachID = u.entityID AND u.role = 'coach' + WHERE c.supervisorID = :svid + ORDER BY u.username" + ); + $stmt->execute([':svid' => $callerEntityID, ':svname' => $svName]); + $users = $stmt->fetchAll(PDO::FETCH_ASSOC); + $supervisors = []; + } + + qdb_discard($tmpDb, $lockFp); + json_success([ + 'users' => $users, + 'supervisors' => $supervisors, + 'callerRole' => $callerRole, + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'users', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'POST': + $body = read_json_body(); + $username = trim($body['username'] ?? ''); + $password = (string)($body['password'] ?? ''); + $role = strtolower(trim($body['role'] ?? '')); + $location = trim($body['location'] ?? ''); + $supervisorID = trim($body['supervisorID'] ?? ''); + $mustChange = isset($body['mustChangePassword']) ? (int)(bool)$body['mustChangePassword'] : 1; + + if ($username === '' || $password === '' || $role === '') { + json_error('MISSING_FIELDS', 'username, password, and role are required', 400); + } + if (!in_array($role, ['admin', 'supervisor', 'coach'], true)) { + json_error('INVALID_ROLE', 'role must be admin, supervisor, or coach', 400); + } + if (strlen($password) < 6) { + json_error('PASSWORD_TOO_SHORT', 'Password must be at least 6 characters', 400); + } + if ($callerRole === 'supervisor' && $role !== 'coach') { + json_error('FORBIDDEN', 'Supervisors may only create coaches', 403); + } + if ($callerRole === 'supervisor') { + $supervisorID = $callerEntityID; + } + if ($role === 'coach' && $supervisorID === '') { + json_error('MISSING_FIELDS', 'supervisorID is required for coaches', 400); + } + + try { + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + + $chk = $pdo->prepare("SELECT userID FROM users WHERE username = :u"); + $chk->execute([':u' => $username]); + if ($chk->fetch()) { + qdb_discard($tmpDb, $lockFp); + json_error('DUPLICATE', 'Username already exists', 409); + } + + if ($role === 'coach') { + $svCheck = $pdo->prepare("SELECT supervisorID FROM supervisor WHERE supervisorID = :sid"); + $svCheck->execute([':sid' => $supervisorID]); + if (!$svCheck->fetch()) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Supervisor not found', 400); + } + if ($callerRole === 'supervisor' && $supervisorID !== $callerEntityID) { + qdb_discard($tmpDb, $lockFp); + json_error('FORBIDDEN', 'Cannot create coaches for another supervisor', 403); + } + } + + $entityID = bin2hex(random_bytes(16)); + $userID = bin2hex(random_bytes(16)); + $hash = password_hash($password, PASSWORD_DEFAULT); + $now = time(); + + $pdo->beginTransaction(); + switch ($role) { + case 'admin': + $pdo->prepare("INSERT INTO admin (adminID, username, location) VALUES (:id, :u, :loc)") + ->execute([':id' => $entityID, ':u' => $username, ':loc' => $location]); + break; + case 'supervisor': + $pdo->prepare("INSERT INTO supervisor (supervisorID, username, location) VALUES (:id, :u, :loc)") + ->execute([':id' => $entityID, ':u' => $username, ':loc' => $location]); + break; + case 'coach': + $pdo->prepare("INSERT INTO coach (coachID, supervisorID, username) VALUES (:id, :sid, :u)") + ->execute([':id' => $entityID, ':sid' => $supervisorID, ':u' => $username]); + break; + } + $pdo->prepare( + "INSERT INTO users (userID, username, passwordHash, role, entityID, mustChangePassword, createdAt) + VALUES (:uid, :u, :hash, :role, :eid, :mcp, :now)" + )->execute([ + ':uid' => $userID, ':u' => $username, ':hash' => $hash, + ':role' => $role, ':eid' => $entityID, ':mcp' => $mustChange, ':now' => $now, + ]); + $pdo->commit(); + qdb_save($tmpDb, $lockFp); + + $svUsername = null; + if ($role === 'coach') { + [$pdo2, $tmp2, $lk2] = qdb_open_read_or_fail(); + $svr = $pdo2->prepare("SELECT username FROM supervisor WHERE supervisorID = :sid"); + $svr->execute([':sid' => $supervisorID]); + $svUsername = $svr->fetchColumn() ?: null; + qdb_discard($tmp2, $lk2); + } + + json_success([ + 'userID' => $userID, + 'entityID' => $entityID, + 'username' => $username, + 'role' => $role, + 'location' => $location, + 'supervisorID' => $role === 'coach' ? $supervisorID : null, + 'supervisorUsername' => $svUsername, + 'mustChangePassword' => $mustChange, + 'createdAt' => $now, + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'users', $pdo ?? null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'PUT': +case 'PATCH': + $body = read_json_body(); + $targetUserID = trim($body['userID'] ?? ''); + $action = trim((string)($body['action'] ?? '')); + $newPassword = (string)($body['password'] ?? $body['newPassword'] ?? ''); + + if ($action === 'revokeSessions') { + if ($targetUserID === '') { + json_error('MISSING_FIELDS', 'userID is required', 400); + } + if ($targetUserID === ($tokenRec['userID'] ?? '')) { + json_error('FORBIDDEN', 'Use Admin settings to revoke your own other sessions, or sign out', 400); + } + + try { + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + + $row = $pdo->prepare('SELECT userID, username, role, entityID FROM users WHERE userID = :uid'); + $row->execute([':uid' => $targetUserID]); + $target = $row->fetch(PDO::FETCH_ASSOC); + + if (!$target) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'User not found', 404); + } + + $targetRole = $target['role'] ?? ''; + if ($callerRole === 'admin') { + if (!in_array($targetRole, ['admin', 'supervisor', 'coach'], true)) { + qdb_discard($tmpDb, $lockFp); + json_error('FORBIDDEN', 'Cannot revoke sessions for this user', 403); + } + } elseif ($callerRole === 'supervisor') { + if ($targetRole !== 'coach') { + qdb_discard($tmpDb, $lockFp); + json_error('FORBIDDEN', 'Supervisors may only revoke sessions for their coaches', 403); + } + $chk = $pdo->prepare( + 'SELECT coachID FROM coach WHERE coachID = :cid AND supervisorID = :svid' + ); + $chk->execute([':cid' => $target['entityID'], ':svid' => $callerEntityID]); + if (!$chk->fetch()) { + qdb_discard($tmpDb, $lockFp); + json_error('FORBIDDEN', 'Counselor is not under your supervision', 403); + } + } else { + qdb_discard($tmpDb, $lockFp); + json_error('FORBIDDEN', 'Not allowed to revoke sessions', 403); + } + + $removed = token_revoke_all_for_user_on_pdo($pdo, $targetUserID); + qdb_save($tmpDb, $lockFp); + + json_success([ + 'userID' => $targetUserID, + 'username' => $target['username'], + 'revokedSessions' => $removed, + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'Revoke user sessions', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + } + + if ($newPassword !== '') { + if ($targetUserID === '') { + json_error('MISSING_FIELDS', 'userID is required', 400); + } + if ($targetUserID === ($tokenRec['userID'] ?? '')) { + json_error('FORBIDDEN', 'Cannot reset your own password', 400); + } + if (strlen($newPassword) < 6) { + json_error('PASSWORD_TOO_SHORT', 'Password must be at least 6 characters', 400); + } + $mustChange = isset($body['mustChangePassword']) ? (int)(bool)$body['mustChangePassword'] : 1; + + try { + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + + $row = $pdo->prepare( + "SELECT u.userID, u.username, u.role, u.entityID, c.supervisorID + FROM users u + LEFT JOIN coach c ON c.coachID = u.entityID AND u.role = 'coach' + WHERE u.userID = :uid" + ); + $row->execute([':uid' => $targetUserID]); + $target = $row->fetch(PDO::FETCH_ASSOC); + + if (!$target) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'User not found', 404); + } + + $targetRole = $target['role'] ?? ''; + if ($callerRole === 'admin') { + if (!in_array($targetRole, ['supervisor', 'coach'], true)) { + qdb_discard($tmpDb, $lockFp); + json_error('FORBIDDEN', 'Admins may only reset passwords for supervisors and coaches', 403); + } + } elseif ($callerRole === 'supervisor') { + if ($targetRole !== 'coach') { + qdb_discard($tmpDb, $lockFp); + json_error('FORBIDDEN', 'Supervisors may only reset passwords for their coaches', 403); + } + $chk = $pdo->prepare( + 'SELECT coachID FROM coach WHERE coachID = :cid AND supervisorID = :svid' + ); + $chk->execute([':cid' => $target['entityID'], ':svid' => $callerEntityID]); + if (!$chk->fetch()) { + qdb_discard($tmpDb, $lockFp); + json_error('FORBIDDEN', 'Counselor is not under your supervision', 403); + } + } else { + qdb_discard($tmpDb, $lockFp); + json_error('FORBIDDEN', 'Not allowed to reset passwords', 403); + } + + $hash = password_hash($newPassword, PASSWORD_DEFAULT); + $pdo->prepare( + 'UPDATE users SET passwordHash = :h, mustChangePassword = :mcp WHERE userID = :uid' + )->execute([':h' => $hash, ':mcp' => $mustChange, ':uid' => $targetUserID]); + + token_revoke_all_for_user_on_pdo($pdo, $targetUserID); + + qdb_save($tmpDb, $lockFp); + + json_success([ + 'userID' => $targetUserID, + 'username' => $target['username'], + 'mustChangePassword' => $mustChange, + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'users', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + } + + if ($callerRole !== 'admin') { + json_error('FORBIDDEN', 'Only admins can reassign coaches', 403); + } + + $supervisorID = trim($body['supervisorID'] ?? ''); + + if ($targetUserID === '' || $supervisorID === '') { + json_error('MISSING_FIELDS', 'userID and supervisorID are required', 400); + } + + try { + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + + $row = $pdo->prepare( + "SELECT u.userID, u.username, u.role, u.entityID, c.supervisorID + FROM users u + LEFT JOIN coach c ON c.coachID = u.entityID AND u.role = 'coach' + WHERE u.userID = :uid" + ); + $row->execute([':uid' => $targetUserID]); + $target = $row->fetch(PDO::FETCH_ASSOC); + + if (!$target) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'User not found', 404); + } + if (($target['role'] ?? '') !== 'coach') { + qdb_discard($tmpDb, $lockFp); + json_error('INVALID_FIELD', 'Only coaches can be reassigned to a supervisor', 400); + } + + $svCheck = $pdo->prepare('SELECT username FROM supervisor WHERE supervisorID = :sid'); + $svCheck->execute([':sid' => $supervisorID]); + $svUsername = $svCheck->fetchColumn(); + if ($svUsername === false) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'Supervisor not found', 404); + } + + if (($target['supervisorID'] ?? '') === $supervisorID) { + qdb_discard($tmpDb, $lockFp); + json_success([ + 'userID' => $targetUserID, + 'supervisorID' => $supervisorID, + 'supervisorUsername' => $svUsername, + ]); + } + + $pdo->prepare('UPDATE coach SET supervisorID = :sid WHERE coachID = :cid') + ->execute([':sid' => $supervisorID, ':cid' => $target['entityID']]); + + qdb_save($tmpDb, $lockFp); + + json_success([ + 'userID' => $targetUserID, + 'supervisorID' => $supervisorID, + 'supervisorUsername' => $svUsername, + ]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'users', null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +case 'DELETE': + $body = read_json_body(); + $targetUserID = trim($body['userID'] ?? ''); + + if ($targetUserID === '') { + json_error('MISSING_FIELDS', 'userID is required', 400); + } + if ($targetUserID === ($tokenRec['userID'] ?? '')) { + json_error('FORBIDDEN', 'Cannot delete your own account', 400); + } + + try { + [$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail(); + + $row = $pdo->prepare("SELECT role, entityID FROM users WHERE userID = :uid"); + $row->execute([':uid' => $targetUserID]); + $target = $row->fetch(PDO::FETCH_ASSOC); + + if (!$target) { + qdb_discard($tmpDb, $lockFp); + json_error('NOT_FOUND', 'User not found', 404); + } + + if ($callerRole === 'supervisor') { + if ($target['role'] !== 'coach') { + qdb_discard($tmpDb, $lockFp); + json_error('FORBIDDEN', 'Supervisors can only delete coaches', 403); + } + $chk = $pdo->prepare("SELECT coachID FROM coach WHERE coachID = :cid AND supervisorID = :svid"); + $chk->execute([':cid' => $target['entityID'], ':svid' => $callerEntityID]); + if (!$chk->fetch()) { + qdb_discard($tmpDb, $lockFp); + json_error('FORBIDDEN', 'Counselor is not under your supervision', 403); + } + } + + $pdo->beginTransaction(); + $pdo->prepare("DELETE FROM users WHERE userID = :uid")->execute([':uid' => $targetUserID]); + switch ($target['role']) { + case 'admin': + $pdo->prepare("DELETE FROM admin WHERE adminID = :id")->execute([':id' => $target['entityID']]); + break; + case 'supervisor': + $pdo->prepare("DELETE FROM supervisor WHERE supervisorID = :id")->execute([':id' => $target['entityID']]); + break; + case 'coach': + $pdo->prepare("DELETE FROM coach WHERE coachID = :id")->execute([':id' => $target['entityID']]); + break; + } + $pdo->commit(); + qdb_save($tmpDb, $lockFp); + + json_success([]); + } catch (Throwable $e) { + qdb_handler_fail($e, 'users', $pdo ?? null, $tmpDb ?? null, $lockFp ?? null); + } + break; + +default: + json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); +} diff --git a/lib/analytics.php b/lib/analytics.php new file mode 100644 index 0000000..1e69341 --- /dev/null +++ b/lib/analytics.php @@ -0,0 +1,401 @@ + + */ +function qdb_analytics_submissions_by_day(PDO $pdo, array $tokenRec, int $days = 14): array { + $days = max(1, min(90, $days)); + [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl'); + + $startTs = strtotime('today', time()) - ($days - 1) * 86400; + + $stmt = $pdo->prepare( + "SELECT qs.submittedAt + FROM questionnaire_submission qs + INNER JOIN client cl ON cl.clientCode = qs.clientCode + WHERE $rbacClause AND qs.submittedAt >= :start" + ); + $stmt->execute(array_merge($rbacParams, [':start' => $startTs])); + $byDate = []; + foreach ($stmt->fetchAll(PDO::FETCH_COLUMN) as $submittedAt) { + $key = date('Y-m-d', (int)$submittedAt); + $byDate[$key] = ($byDate[$key] ?? 0) + 1; + } + + $out = []; + for ($i = 0; $i < $days; $i++) { + $key = date('Y-m-d', $startTs + $i * 86400); + $out[] = ['date' => $key, 'count' => $byDate[$key] ?? 0]; + } + return $out; +} + +function qdb_analytics_overview(PDO $pdo, array $tokenRec): array { + [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl'); + + $stmt = $pdo->prepare("SELECT COUNT(*) FROM client cl WHERE $rbacClause"); + $stmt->execute($rbacParams); + $clientCount = (int)$stmt->fetchColumn(); + + $stmt = $pdo->prepare( + "SELECT COUNT(DISTINCT cq.clientCode) FROM completed_questionnaire cq + INNER JOIN client cl ON cl.clientCode = cq.clientCode + WHERE $rbacClause" + ); + $stmt->execute($rbacParams); + $clientsWithCompletions = (int)$stmt->fetchColumn(); + + $now = time(); + $d7 = $now - 7 * 86400; + $d30 = $now - 30 * 86400; + + $stmt = $pdo->prepare( + "SELECT COUNT(*) FROM questionnaire_submission qs + INNER JOIN client cl ON cl.clientCode = qs.clientCode + WHERE $rbacClause AND qs.submittedAt >= :t" + ); + $stmt->execute(array_merge($rbacParams, [':t' => $d7])); + $submissions7d = (int)$stmt->fetchColumn(); + + $stmt = $pdo->prepare( + "SELECT COUNT(*) FROM questionnaire_submission qs + INNER JOIN client cl ON cl.clientCode = qs.clientCode + WHERE $rbacClause AND qs.submittedAt >= :t" + ); + $stmt->execute(array_merge($rbacParams, [':t' => $d30])); + $submissions30d = (int)$stmt->fetchColumn(); + + $qnRows = $pdo->query( + "SELECT questionnaireID, name, orderIndex FROM questionnaire WHERE state = 'active' ORDER BY orderIndex, name" + )->fetchAll(PDO::FETCH_ASSOC); + + $perQuestionnaire = []; + foreach ($qnRows as $qn) { + $qnID = $qn['questionnaireID']; + $stmt = $pdo->prepare( + "SELECT COUNT(DISTINCT cq.clientCode) FROM completed_questionnaire cq + INNER JOIN client cl ON cl.clientCode = cq.clientCode + WHERE cq.questionnaireID = :qn AND $rbacClause" + ); + $stmt->execute(array_merge([':qn' => $qnID], $rbacParams)); + $completed = (int)$stmt->fetchColumn(); + $ratio = $clientCount > 0 ? round(100 * $completed / $clientCount, 1) : 0; + $perQuestionnaire[] = [ + 'questionnaireID' => $qnID, + 'name' => $qn['name'], + 'completedCount' => $completed, + 'eligibleCount' => $clientCount, + 'completionRatio' => $ratio, + ]; + } + + return [ + 'clientCount' => $clientCount, + 'clientsWithCompletions' => $clientsWithCompletions, + 'submissionsLast7d' => $submissions7d, + 'submissionsLast30d' => $submissions30d, + 'questionnaires' => $perQuestionnaire, + 'submissionsByDay' => qdb_analytics_submissions_by_day($pdo, $tokenRec, 14), + 'scoringProfiles' => qdb_analytics_scoring_profiles($pdo, $tokenRec), + ]; +} + +/** + * Band distribution and averages per active scoring profile (RBAC-scoped). + * + * @return list> + */ +function qdb_analytics_scoring_profiles(PDO $pdo, array $tokenRec): array { + require_once __DIR__ . '/scoring.php'; + [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl'); + + $profiles = $pdo->query( + "SELECT profileID, name, description, + greenMin, greenMax, yellowMin, yellowMax, redMin, isActive + FROM scoring_profile WHERE isActive = 1 ORDER BY name" + )->fetchAll(PDO::FETCH_ASSOC); + + $out = []; + foreach ($profiles as $p) { + $profileID = $p['profileID']; + $stmt = $pdo->prepare( + "SELECT r.band, r.coachBand, r.weightedTotal + FROM client_scoring_profile_result r + INNER JOIN client cl ON cl.clientCode = r.clientCode + WHERE r.profileID = :pid AND ($rbacClause)" + ); + $stmt->execute(array_merge([':pid' => $profileID], $rbacParams)); + $computedBands = ['green' => 0, 'yellow' => 0, 'red' => 0]; + $coachBands = ['green' => 0, 'yellow' => 0, 'red' => 0]; + $pendingReview = 0; + $totalSum = 0.0; + $count = 0; + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) { + $calcBand = $row['band'] ?? ''; + if (isset($computedBands[$calcBand])) { + $computedBands[$calcBand]++; + } + $coachBand = trim((string)($row['coachBand'] ?? '')); + if ($coachBand === '') { + $pendingReview++; + } elseif (isset($coachBands[$coachBand])) { + $coachBands[$coachBand]++; + } + $totalSum += (float)$row['weightedTotal']; + $count++; + } + $members = qdb_scoring_profile_members($pdo, $profileID); + $bandRanges = qdb_normalize_scoring_bands($p); + $out[] = [ + 'profileID' => $profileID, + 'name' => $p['name'], + 'description' => $p['description'] ?? '', + 'greenMin' => $bandRanges['greenMin'], + 'greenMax' => $bandRanges['greenMax'], + 'yellowMin' => $bandRanges['yellowMin'], + 'yellowMax' => $bandRanges['yellowMax'], + 'redMin' => $bandRanges['redMin'], + 'questionnaires' => $members, + 'clientCount' => $count, + 'bands' => $computedBands, + 'computedBands' => $computedBands, + 'coachBands' => $coachBands, + 'pendingReview' => $pendingReview, + 'averageTotal' => $count > 0 ? round($totalSum / $count, 2) : null, + ]; + } + return $out; +} + +function qdb_analytics_stale_clients(PDO $pdo, array $tokenRec): array { + [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl'); + $now = time(); + + $sql = " + SELECT cl.clientCode, + co.username AS coachUsername, + co.coachID, + lc.lastCompletedAt, + lc.lastQuestionnaireID, + qn.name AS lastQuestionnaireName, + la.lastActivityAt, + n.note AS followupNote + FROM client cl + INNER JOIN ( + SELECT cq.clientCode, cq.questionnaireID AS lastQuestionnaireID, cq.completedAt AS lastCompletedAt + FROM completed_questionnaire cq + WHERE cq.completedAt IS NOT NULL + AND cq.rowid = ( + SELECT c2.rowid FROM completed_questionnaire c2 + WHERE c2.clientCode = cq.clientCode AND c2.completedAt IS NOT NULL + ORDER BY c2.completedAt DESC, c2.questionnaireID DESC + LIMIT 1 + ) + ) lc ON lc.clientCode = cl.clientCode + LEFT JOIN questionnaire qn ON qn.questionnaireID = lc.lastQuestionnaireID + LEFT JOIN coach co ON co.coachID = cl.coachID + LEFT JOIN ( + SELECT qs.clientCode, MAX(qs.submittedAt) AS lastActivityAt + FROM questionnaire_submission qs + GROUP BY qs.clientCode + ) la ON la.clientCode = cl.clientCode + LEFT JOIN client_followup_note n ON n.clientCode = cl.clientCode + WHERE $rbacClause + ORDER BY COALESCE(la.lastActivityAt, 0) ASC, cl.clientCode ASC + "; + $stmt = $pdo->prepare($sql); + $stmt->execute($rbacParams); + $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $out = []; + foreach ($rows as $r) { + $completedTs = $r['lastCompletedAt'] ? (int)$r['lastCompletedAt'] : null; + $uploadTs = $r['lastActivityAt'] !== null ? (int)$r['lastActivityAt'] : null; + $lastAt = null; + if ($completedTs !== null && $uploadTs !== null) { + $lastAt = max($completedTs, $uploadTs); + } elseif ($completedTs !== null) { + $lastAt = $completedTs; + } else { + $lastAt = $uploadTs; + } + $daysSince = $lastAt !== null + ? (int)floor(($now - $lastAt) / 86400) + : null; + $out[] = [ + 'clientCode' => $r['clientCode'], + 'coachUsername' => $r['coachUsername'] ?? $r['coachID'], + 'lastQuestionnaireID' => $r['lastQuestionnaireID'], + 'lastQuestionnaireName' => $r['lastQuestionnaireName'] ?? $r['lastQuestionnaireID'], + 'lastCompletedAt' => $completedTs ? date('Y-m-d H:i', $completedTs) : '', + 'daysSinceLastActivity' => $daysSince, + 'note' => $r['followupNote'] ?? '', + ]; + } + return $out; +} + +function qdb_analytics_set_followup_note(PDO $pdo, array $tokenRec, string $clientCode, string $note): void { + $clientCode = trim($clientCode); + if ($clientCode === '') { + json_error('INVALID_FIELD', 'clientCode is required', 400); + } + + [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl'); + $stmt = $pdo->prepare("SELECT 1 FROM client cl WHERE cl.clientCode = :cc AND ($rbacClause)"); + $stmt->execute(array_merge([':cc' => $clientCode], $rbacParams)); + if (!$stmt->fetchColumn()) { + json_error('NOT_FOUND', 'Client not found or not authorized', 404); + } + + $pdo->prepare( + 'INSERT INTO client_followup_note (clientCode, note, updatedByUserID, updatedAt) + VALUES (:cc, :n, :uid, :ts) + ON CONFLICT(clientCode) DO UPDATE SET + note = excluded.note, + updatedByUserID = excluded.updatedByUserID, + updatedAt = excluded.updatedAt' + )->execute([ + ':cc' => $clientCode, + ':n' => $note, + ':uid' => $tokenRec['userID'] ?? '', + ':ts' => time(), + ]); +} + +function qdb_coach_activity_list(PDO $pdo, array $tokenRec): array { + $role = $tokenRec['role'] ?? ''; + $entityID = $tokenRec['entityID'] ?? ''; + + if ($role === 'supervisor') { + $coachWhere = 'co.supervisorID = :eid'; + $coachParams = [':eid' => $entityID]; + } else { + $coachWhere = '1=1'; + $coachParams = []; + } + + $now = time(); + $d7 = $now - 7 * 86400; + $d30 = $now - 30 * 86400; + + $sql = " + SELECT co.coachID, co.username, + sv.username AS supervisorUsername, + (SELECT COUNT(*) FROM client c + WHERE c.coachID = co.coachID AND COALESCE(c.archived, 0) = 0) AS clientCount, + (SELECT COUNT(*) FROM questionnaire_submission qs + INNER JOIN client c ON c.clientCode = qs.clientCode + WHERE c.coachID = co.coachID) AS totalSubmissions, + (SELECT COUNT(*) FROM questionnaire_submission qs + INNER JOIN client c ON c.clientCode = qs.clientCode + WHERE c.coachID = co.coachID AND qs.submittedAt >= :d7) AS submissions7d, + (SELECT COUNT(*) FROM questionnaire_submission qs + INNER JOIN client c ON c.clientCode = qs.clientCode + WHERE c.coachID = co.coachID AND qs.submittedAt >= :d30) AS submissions30d, + (SELECT MAX(qs.submittedAt) FROM questionnaire_submission qs + INNER JOIN client c ON c.clientCode = qs.clientCode + WHERE c.coachID = co.coachID) AS lastSubmissionAt + FROM coach co + LEFT JOIN supervisor sv ON sv.supervisorID = co.supervisorID + WHERE $coachWhere + ORDER BY co.username + "; + $stmt = $pdo->prepare($sql); + $stmt->execute(array_merge([':d7' => $d7, ':d30' => $d30], $coachParams)); + $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $out = []; + foreach ($rows as $r) { + $last = $r['lastSubmissionAt'] !== null ? (int)$r['lastSubmissionAt'] : null; + $out[] = [ + 'coachID' => $r['coachID'], + 'username' => $r['username'], + 'supervisorUsername'=> $r['supervisorUsername'] ?? '', + 'clientCount' => (int)$r['clientCount'], + 'totalSubmissions' => (int)$r['totalSubmissions'], + 'submissionsLast7d' => (int)$r['submissions7d'], + 'submissionsLast30d'=> (int)$r['submissions30d'], + 'lastSubmissionAt' => $last ? date('Y-m-d H:i', $last) : '', + ]; + } + return $out; +} + +function qdb_coach_recent_submissions( + PDO $pdo, + array $tokenRec, + string $coachID, + int $limit = 100, + int $offset = 0 +): array { + $coachID = trim($coachID); + if ($coachID === '') { + json_error('INVALID_FIELD', 'coachID is required', 400); + } + + $role = $tokenRec['role'] ?? ''; + $entityID = $tokenRec['entityID'] ?? ''; + + $chk = $pdo->prepare('SELECT coachID, supervisorID FROM coach WHERE coachID = :id'); + $chk->execute([':id' => $coachID]); + $coach = $chk->fetch(PDO::FETCH_ASSOC); + if (!$coach) { + json_error('NOT_FOUND', 'Counselor not found', 404); + } + if ($role === 'supervisor' && ($coach['supervisorID'] ?? '') !== $entityID) { + json_error('FORBIDDEN', 'Not authorized for this coach', 403); + } + + [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl'); + $limit = max(1, min($limit, 200)); + $offset = max(0, $offset); + + $countSql = " + SELECT COUNT(*) + FROM questionnaire_submission qs + INNER JOIN client cl ON cl.clientCode = qs.clientCode + WHERE cl.coachID = :cid AND ($rbacClause) + "; + $countStmt = $pdo->prepare($countSql); + $countStmt->execute(array_merge([':cid' => $coachID], $rbacParams)); + $total = (int)$countStmt->fetchColumn(); + + $sql = " + SELECT qs.submissionID, qs.version, qs.submittedAt, qs.submittedByRole, + qs.clientCode, qs.questionnaireID, qn.name AS questionnaireName + FROM questionnaire_submission qs + INNER JOIN client cl ON cl.clientCode = qs.clientCode + LEFT JOIN questionnaire qn ON qn.questionnaireID = qs.questionnaireID + WHERE cl.coachID = :cid AND ($rbacClause) + ORDER BY qs.submittedAt DESC + LIMIT $limit OFFSET $offset + "; + $stmt = $pdo->prepare($sql); + $stmt->execute(array_merge([':cid' => $coachID], $rbacParams)); + $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $submissions = array_map(static function ($r) { + return [ + 'submissionID' => $r['submissionID'], + 'version' => (int)$r['version'], + 'submittedAt' => $r['submittedAt'] ? date('Y-m-d H:i', (int)$r['submittedAt']) : '', + 'submittedByRole' => $r['submittedByRole'] ?? '', + 'clientCode' => $r['clientCode'], + 'questionnaireID' => $r['questionnaireID'], + 'questionnaireName'=> $r['questionnaireName'] ?? $r['questionnaireID'], + ]; + }, $rows); + + return [ + 'submissions' => $submissions, + 'total' => $total, + 'limit' => $limit, + 'offset' => $offset, + ]; +} diff --git a/lib/api_log.php b/lib/api_log.php new file mode 100644 index 0000000..4224273 --- /dev/null +++ b/lib/api_log.php @@ -0,0 +1,998 @@ + */ +const QDB_API_LOG_ACTIVITIES = ['app_sync', 'app_change', 'web_change', 'web_export', 'api_error']; + +function qdb_api_log_enabled(): bool +{ + $v = qdb_env_get('QDB_API_LOG'); + if ($v === null || $v === '') { + return true; + } + $v = strtolower(trim($v)); + return !in_array($v, ['0', 'false', 'no', 'off'], true); +} + +function qdb_api_log_dir(): string +{ + static $resolved = null; + if ($resolved !== null) { + return $resolved; + } + + $custom = qdb_env_get('QDB_API_LOG_DIR'); + if ($custom !== null && $custom !== '') { + $resolved = rtrim($custom, '/\\'); + return $resolved; + } + + $root = dirname(__DIR__); + foreach ([$root . '/uploads/logs/api', $root . '/logs/api'] as $candidate) { + if (qdb_api_log_dir_is_usable($candidate)) { + $resolved = $candidate; + return $resolved; + } + } + + $resolved = $root . '/uploads/logs/api'; + return $resolved; +} + +function qdb_api_log_dir_is_usable(string $dir): bool +{ + if (is_dir($dir)) { + return is_writable($dir); + } + $parent = dirname($dir); + if (is_dir($parent) && is_writable($parent)) { + return true; + } + if (@mkdir($dir, 0775, true)) { + return is_writable($dir); + } + return is_dir($dir) && is_writable($dir); +} + +/** + * Diagnostics for admin UI (permissions, path, enabled flag). + * + * @return array + */ +function qdb_api_log_status(): array +{ + $dir = qdb_api_log_dir(); + $enabled = qdb_api_log_enabled(); + $exists = is_dir($dir); + $writable = false; + $probeError = null; + + if ($enabled) { + if (!$exists) { + if (!@mkdir($dir, 0775, true)) { + $probeError = 'Cannot create directory: ' . $dir; + } + $exists = is_dir($dir); + } + if ($exists && !is_writable($dir)) { + $probeError = 'Directory exists but is not writable by PHP: ' . $dir; + } elseif ($exists) { + $probe = $dir . '/.write_probe'; + if (@file_put_contents($probe, (string)time() . "\n", LOCK_EX) === false) { + $probeError = 'Cannot write files in: ' . $dir; + } else { + $writable = true; + @unlink($probe); + } + } + } + + $phpUser = null; + if (function_exists('posix_geteuid') && function_exists('posix_getpwuid')) { + $info = posix_getpwuid(posix_geteuid()); + if (is_array($info) && !empty($info['name'])) { + $phpUser = (string)$info['name']; + } + } + + return [ + 'enabled' => $enabled, + 'dir' => $dir, + 'exists' => $exists, + 'writable' => $writable, + 'error' => $probeError, + 'php_user' => $phpUser, + 'fix_hint' => $writable || !$enabled + ? null + : 'On the server run: mkdir -p ' . $dir . ' && chown -R www-data:www-data ' + . dirname($dir) . ' && chmod -R 775 ' . dirname($dir) + . ' (adjust www-data to your PHP user). Or set QDB_API_LOG_DIR in .env to a writable path.', + ]; +} + +function qdb_api_log_max_bytes(): int +{ + $v = qdb_env_get('QDB_API_LOG_MAX_BYTES'); + if ($v !== null && $v !== '' && ctype_digit($v)) { + return max(1024 * 1024, (int)$v); + } + return 10 * 1024 * 1024; +} + +function qdb_api_log_body_max_chars(): int +{ + $v = qdb_env_get('QDB_API_LOG_BODY_MAX'); + if ($v !== null && $v !== '' && ctype_digit($v)) { + return max(256, (int)$v); + } + return 4096; +} + +/** + * Whether this request should be written to the activity log. + */ +function qdb_api_log_classify_activity(string $method, string $client, string $route): ?string +{ + $method = strtoupper($method); + $client = strtolower(trim($client)); + $route = strtolower(trim($route)); + + if ($method === 'OPTIONS' || in_array($route, ['activity-log', 'session'], true)) { + return null; + } + + if (in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { + return $client === 'web' ? 'web_change' : 'app_change'; + } + + if ($method === 'GET') { + if ($route === 'app_questionnaires' && $client !== 'web') { + return 'app_sync'; + } + if (qdb_api_log_is_website_download($route)) { + return 'web_export'; + } + } + + return null; +} + +/** Website GET that downloads or exports data (not routine list/load). */ +function qdb_api_log_is_website_download(string $route): bool +{ + if (in_array($route, ['export', 'backup'], true)) { + return true; + } + if ($route === 'translations' && !empty($_GET['exportBundle'])) { + return true; + } + return false; +} + +/** @param array $context */ +function qdb_api_log_begin(string $method, string $route, array $context = []): void +{ + if (!qdb_api_log_enabled()) { + return; + } + + $client = trim((string)($_SERVER['HTTP_X_QDB_CLIENT'] ?? '')); + $activity = qdb_api_log_classify_activity($method, $client, $route); + + $GLOBALS['qdb_api_log_ctx'] = array_merge([ + 'started_at' => microtime(true), + 'method' => strtoupper($method), + 'route' => $route, + 'uri' => (string)($_SERVER['REQUEST_URI'] ?? ''), + 'ip' => qdb_api_log_client_ip(), + 'client' => $client, + 'user_agent' => trim((string)($_SERVER['HTTP_USER_AGENT'] ?? '')), + ], $context); + + if ($activity !== null) { + $GLOBALS['qdb_api_log_ctx']['activity'] = $activity; + } else { + $GLOBALS['qdb_api_log_ctx']['skip_success'] = true; + } +} + +function qdb_api_log_note_exception(Throwable $e): void +{ + if (empty($GLOBALS['qdb_api_log_ctx'])) { + return; + } + $GLOBALS['qdb_api_log_ctx']['exception'] = $e->getMessage(); + $GLOBALS['qdb_api_log_ctx']['exception_class'] = $e::class; +} + +function qdb_api_log_note_api_error(string $code, string $message, int $status): void +{ + if (empty($GLOBALS['qdb_api_log_ctx'])) { + return; + } + $GLOBALS['qdb_api_log_ctx']['api_error_code'] = $code; + $GLOBALS['qdb_api_log_ctx']['api_error_message'] = $message; + $GLOBALS['qdb_api_log_ctx']['api_error_status'] = $status; +} + +function qdb_api_log_finish(): void +{ + $ctx = $GLOBALS['qdb_api_log_ctx'] ?? null; + unset($GLOBALS['qdb_api_log_ctx']); + if (!is_array($ctx) || !qdb_api_log_enabled()) { + return; + } + + $started = (float)($ctx['started_at'] ?? microtime(true)); + $status = http_response_code(); + if ($status === false || $status === 0) { + $status = 200; + } + + $isError = $status >= 400 + || !empty($ctx['exception']) + || !empty($ctx['api_error_code']); + + if (!empty($ctx['skip_success']) && !$isError) { + return; + } + + $token = get_bearer_token(); + $auth = ['role' => null, 'userID' => null, 'token_hint' => null]; + if ($token !== null && $token !== '') { + $auth['token_hint'] = qdb_api_log_token_hint($token); + try { + $rec = token_get_record($token, false); + if (is_array($rec)) { + $auth['role'] = $rec['role'] ?? null; + $auth['userID'] = $rec['userID'] ?? null; + if (!empty($rec['temp'])) { + $auth['temp_token'] = true; + } + } + } catch (Throwable) { + $auth['token_lookup'] = 'failed'; + } + } + + $rawBody = function_exists('qdb_raw_request_body') ? qdb_raw_request_body() : ''; + $bodyLog = qdb_api_log_format_body($rawBody); + $queryLog = qdb_api_log_redact_query_string((string)($_SERVER['QUERY_STRING'] ?? '')); + + $actorUsername = qdb_api_log_lookup_username($auth['userID'] ?? null); + $targetUsername = qdb_api_log_extract_target_username($bodyLog, $queryLog); + + $activity = $ctx['activity'] ?? null; + if ($isError) { + $activity = 'api_error'; + } + + $entry = [ + 'ts' => gmdate('c'), + 'activity' => $activity, + 'method' => $ctx['method'] ?? '', + 'route' => $ctx['route'] ?? '', + 'status' => $status, + 'duration_ms' => (int)round((microtime(true) - $started) * 1000), + 'ip' => $ctx['ip'] ?? '', + 'client' => $ctx['client'] ?? '', + 'uri' => $ctx['uri'] ?? '', + 'query' => $queryLog, + 'role' => $auth['role'], + 'userID' => $auth['userID'], + 'actor_username' => $actorUsername, + 'target_username' => $targetUsername, + 'token_hint' => $auth['token_hint'], + 'temp_token' => $auth['temp_token'] ?? null, + 'changes' => qdb_api_log_build_changes( + (string)($ctx['method'] ?? ''), + (string)($ctx['route'] ?? ''), + $bodyLog, + $queryLog + ), + ]; + + if (!empty($ctx['api_error_code'])) { + $entry['error_code'] = (string)$ctx['api_error_code']; + $entry['error_message'] = (string)($ctx['api_error_message'] ?? ''); + $entry['error'] = $entry['error_code'] . ': ' . $entry['error_message']; + } + if (!empty($ctx['exception'])) { + $entry['error_internal'] = (string)$ctx['exception']; + if (!empty($ctx['exception_class'])) { + $entry['error_class'] = (string)$ctx['exception_class']; + } + if (empty($entry['error'])) { + $entry['error'] = 'SERVER_ERROR: ' . $entry['error_internal']; + } + } + + if (($ctx['user_agent'] ?? '') !== '') { + $entry['user_agent'] = $ctx['user_agent']; + } + + try { + $line = json_encode($entry, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + if ($line === false) { + return; + } + qdb_api_log_append_line($line . "\n"); + } catch (Throwable $e) { + error_log('api_log write failed: ' . $e->getMessage()); + } +} + +function qdb_api_log_client_ip(): string +{ + foreach ([ + 'HTTP_CF_CONNECTING_IP', + 'HTTP_X_FORWARDED_FOR', + 'HTTP_X_REAL_IP', + 'REMOTE_ADDR', + ] as $key) { + $v = trim((string)($_SERVER[$key] ?? '')); + if ($v === '') { + continue; + } + if ($key === 'HTTP_X_FORWARDED_FOR') { + $v = trim(explode(',', $v)[0]); + } + if (filter_var($v, FILTER_VALIDATE_IP)) { + return $v; + } + } + return ''; +} + +function qdb_api_log_token_hint(string $token): string +{ + $len = strlen($token); + if ($len <= 8) { + return '[token]'; + } + return substr($token, 0, 4) . '…' . substr($token, -4) . " ($len)"; +} + +function qdb_api_log_redact_query_string(string $qs): ?string +{ + if ($qs === '') { + return null; + } + parse_str($qs, $params); + if (!is_array($params) || $params === []) { + return $qs; + } + foreach ($params as $k => $_) { + if (in_array(strtolower((string)$k), ['token', 'password', 'authorization'], true)) { + $params[$k] = '[REDACTED]'; + } + } + return http_build_query($params); +} + +/** + * @return array|string|null + */ +function qdb_api_log_format_body(string $raw): array|string|null +{ + $raw = trim($raw); + if ($raw === '') { + return null; + } + + $max = qdb_api_log_body_max_chars(); + $decoded = json_decode($raw, true); + if (is_array($decoded)) { + $redacted = qdb_api_log_redact_value($decoded); + $json = json_encode($redacted, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + if ($json === false) { + return '[unencodable body]'; + } + if (strlen($json) > $max) { + return substr($json, 0, $max) . '…[truncated]'; + } + return $redacted; + } + + if (strlen($raw) > $max) { + return substr($raw, 0, $max) . '…[truncated]'; + } + return $raw; +} + +/** @param mixed $value @return mixed */ +function qdb_api_log_redact_value(mixed $value): mixed +{ + if (!is_array($value)) { + return $value; + } + + $isList = array_keys($value) === range(0, count($value) - 1); + $out = []; + foreach ($value as $k => $v) { + $lk = strtolower((string)$k); + if (in_array($lk, [ + 'password', 'old_password', 'new_password', 'passwordhash', + 'token', 'authorization', 'secret', + ], true)) { + $out[$k] = '[REDACTED]'; + } elseif ($lk === 'encrypted' && is_string($v)) { + $out[$k] = '[ENCRYPTED:' . strlen($v) . ' chars]'; + } elseif (is_array($v)) { + $out[$k] = qdb_api_log_redact_value($v); + } else { + $out[$k] = $v; + } + } + return $isList ? array_values($out) : $out; +} + +/** + * Field-level change list for the activity log UI (passwords stay redacted). + * + * @param mixed $body Redacted decoded body or null + * @return list + */ +function qdb_api_log_build_changes(string $method, string $route, mixed $body, ?string $queryString): array +{ + $changes = []; + $max = 100; + + if ($queryString !== null && $queryString !== '') { + parse_str($queryString, $params); + if (is_array($params)) { + foreach ($params as $k => $v) { + if (count($changes) >= $max) { + break; + } + if (!is_scalar($v)) { + continue; + } + $changes[] = [ + 'field' => 'query.' . (string)$k, + 'value' => qdb_api_log_change_scalar($v), + ]; + } + } + } + + if (is_array($body)) { + qdb_api_log_flatten_for_changes($body, '', $changes, 0, 4, $max); + } elseif (is_string($body) && $body !== '') { + $changes[] = ['field' => 'body', 'value' => qdb_api_log_change_scalar($body)]; + } + + if ($changes === [] && strtoupper($method) === 'DELETE') { + $changes[] = ['field' => '_action', 'value' => 'delete']; + } + + return $changes; +} + +/** + * @param list $out + */ +function qdb_api_log_flatten_for_changes( + array $data, + string $prefix, + array &$out, + int $depth, + int $maxDepth, + int $maxFields +): void { + if ($depth >= $maxDepth || count($out) >= $maxFields) { + return; + } + + foreach ($data as $k => $v) { + if (count($out) >= $maxFields) { + return; + } + $key = (string)$k; + $field = $prefix === '' ? $key : $prefix . '.' . $key; + + if (is_array($v)) { + $childList = array_keys($v) === range(0, count($v) - 1); + $n = count($v); + if ($n === 0) { + $out[] = ['field' => $field, 'value' => '[]']; + continue; + } + if ($childList && $n > 8) { + $out[] = ['field' => $field, 'value' => '[list: ' . $n . ' items]']; + continue; + } + if (!$childList && $depth + 1 >= $maxDepth) { + $out[] = ['field' => $field, 'value' => '[object: ' . $n . ' keys]']; + continue; + } + qdb_api_log_flatten_for_changes($v, $field, $out, $depth + 1, $maxDepth, $maxFields); + } else { + $out[] = ['field' => $field, 'value' => qdb_api_log_change_scalar($v)]; + } + } +} + +/** @return string|int|float|bool|null */ +function qdb_api_log_change_scalar(mixed $v): string|int|float|bool|null +{ + if ($v === null) { + return null; + } + if (is_bool($v) || is_int($v) || is_float($v)) { + return $v; + } + $s = is_string($v) ? $v : json_encode($v, JSON_UNESCAPED_UNICODE); + if ($s === false) { + return ''; + } + if (strlen($s) > 800) { + return substr($s, 0, 800) . '…'; + } + return $s; +} + +function qdb_api_log_append_line(string $line): void +{ + $dir = qdb_api_log_dir(); + if (!is_dir($dir) && !@mkdir($dir, 0775, true) && !is_dir($dir)) { + $err = error_get_last(); + throw new RuntimeException( + 'Cannot create log directory: ' . $dir + . ($err ? ' (' . ($err['message'] ?? '') . ')' : '') + ); + } + if (!is_writable($dir)) { + throw new RuntimeException('Log directory not writable: ' . $dir); + } + + $path = qdb_api_log_resolve_write_path($dir); + if (@file_put_contents($path, $line, FILE_APPEND | LOCK_EX) === false) { + $err = error_get_last(); + throw new RuntimeException( + 'Cannot write API log: ' . $path + . ($err ? ' (' . ($err['message'] ?? '') . ')' : '') + ); + } +} + +function qdb_api_log_resolve_write_path(string $dir): string +{ + $date = date('Y-m-d'); + $base = $dir . '/api-' . $date; + $max = qdb_api_log_max_bytes(); + + $primary = $base . '.log'; + if (!is_file($primary) || filesize($primary) < $max) { + return $primary; + } + + for ($part = 2; $part <= 999; $part++) { + $path = sprintf('%s-%03d.log', $base, $part); + if (!is_file($path) || filesize($path) < $max) { + return $path; + } + } + + return sprintf('%s-%03d.log', $base, 999); +} + +/** + * Read activity log entries for admin UI (newest first). + * + * @return array{date: string, activity: string, entries: list>, files: list, truncated: bool} + */ +function qdb_api_log_read_entries(string $date, string $activityFilter = '', int $limit = 200, bool $errorsOnly = false): array +{ + if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) { + $date = date('Y-m-d'); + } + $limit = max(1, min(1000, $limit)); + $activityFilter = strtolower(trim($activityFilter)); + if ($activityFilter === 'errors') { + $errorsOnly = true; + $activityFilter = ''; + } + if ($activityFilter !== '' && !in_array($activityFilter, QDB_API_LOG_ACTIVITIES, true)) { + $activityFilter = ''; + } + + $dir = qdb_api_log_dir(); + $files = qdb_api_log_files_for_date($dir, $date); + $entries = []; + + foreach ($files as $file) { + $lines = @file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if ($lines === false) { + continue; + } + foreach ($lines as $line) { + $row = json_decode($line, true); + if (!is_array($row)) { + continue; + } + if ($activityFilter !== '' && ($row['activity'] ?? '') !== $activityFilter) { + continue; + } + if ($errorsOnly) { + $rowActivity = $row['activity'] ?? ''; + $rowStatus = (int)($row['status'] ?? 0); + if ($rowActivity !== 'api_error' && $rowStatus < 400 && empty($row['error'])) { + continue; + } + } + $entries[] = qdb_api_log_entry_for_ui($row); + } + } + + usort($entries, static function (array $a, array $b): int { + return strcmp($b['ts'] ?? '', $a['ts'] ?? ''); + }); + + $truncated = count($entries) > $limit; + if ($truncated) { + $entries = array_slice($entries, 0, $limit); + } + + $entries = qdb_api_log_enrich_entries_usernames($entries); + + return [ + 'date' => $date, + 'activity' => $activityFilter, + 'errorsOnly' => $errorsOnly, + 'entries' => $entries, + 'files' => array_map('basename', $files), + 'truncated' => $truncated, + 'kinds' => QDB_API_LOG_ACTIVITIES, + 'logStatus' => qdb_api_log_status(), + ]; +} + +/** @return list */ +function qdb_api_log_files_for_date(string $dir, string $date): array +{ + if (!is_dir($dir)) { + return []; + } + $pattern = $dir . '/api-' . $date . '*.log'; + $files = glob($pattern) ?: []; + sort($files); + return $files; +} + +/** @param array $row @return array */ +function qdb_api_log_entry_for_ui(array $row): array +{ + $changes = $row['changes'] ?? null; + if (!is_array($changes) || $changes === []) { + $changes = qdb_api_log_build_changes( + (string)($row['method'] ?? ''), + (string)($row['route'] ?? ''), + $row['body'] ?? null, + isset($row['query']) ? (string)$row['query'] : null + ); + } + $changes = qdb_api_log_sort_changes_for_display($changes); + + $actorUsername = $row['actor_username'] ?? null; + $targetUsername = $row['target_username'] ?? null; + + return [ + 'ts' => $row['ts'] ?? '', + 'activity' => $row['activity'] ?? '', + 'method' => $row['method'] ?? '', + 'route' => $row['route'] ?? '', + 'status' => $row['status'] ?? null, + 'duration_ms' => $row['duration_ms'] ?? null, + 'role' => $row['role'] ?? null, + 'userID' => $row['userID'] ?? null, + 'actor_username' => is_string($actorUsername) ? $actorUsername : null, + 'target_username' => is_string($targetUsername) ? $targetUsername : null, + 'client' => $row['client'] ?? '', + 'ip' => $row['ip'] ?? '', + 'query' => $row['query'] ?? null, + 'changes' => $changes, + 'summary' => qdb_api_log_summarize_changes($changes, $targetUsername), + 'error' => $row['error'] ?? null, + 'error_code' => $row['error_code'] ?? null, + 'error_message' => $row['error_message'] ?? null, + ]; +} + +function qdb_api_log_lookup_username(?string $userID): ?string +{ + if ($userID === null || $userID === '') { + return null; + } + try { + require_once __DIR__ . '/../db_init.php'; + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $stmt = $pdo->prepare('SELECT username FROM users WHERE userID = :uid'); + $stmt->execute([':uid' => $userID]); + $name = $stmt->fetchColumn(); + qdb_discard($tmpDb, $lockFp); + return $name !== false ? (string)$name : null; + } catch (Throwable) { + return null; + } +} + +function qdb_api_log_extract_target_username(mixed $body, ?string $queryString): ?string +{ + if (is_array($body) && !empty($body['username']) && is_scalar($body['username'])) { + return trim((string)$body['username']); + } + if ($queryString !== null && $queryString !== '') { + parse_str($queryString, $params); + if (!empty($params['username']) && is_scalar($params['username'])) { + return trim((string)$params['username']); + } + } + return null; +} + +/** + * Resolve userID / supervisorID in log rows to usernames for retraceability. + * + * @param list> $entries + * @return list> + */ +function qdb_api_log_enrich_entries_usernames(array $entries): array +{ + if ($entries === []) { + return $entries; + } + + $userIds = []; + $supervisorIds = []; + + foreach ($entries as $entry) { + if (!empty($entry['userID']) && is_string($entry['userID'])) { + $userIds[$entry['userID']] = true; + } + if (empty($entry['actor_username']) && !empty($entry['userID'])) { + $userIds[$entry['userID']] = true; + } + foreach ($entry['changes'] ?? [] as $change) { + if (!is_array($change)) { + continue; + } + $field = strtolower((string)($change['field'] ?? '')); + $value = $change['value'] ?? null; + if (!is_string($value) || $value === '' || str_starts_with($value, '[')) { + continue; + } + if ($field === 'userid' || str_ends_with($field, '.userid')) { + $userIds[$value] = true; + } + if ($field === 'supervisorid' || str_ends_with($field, '.supervisorid')) { + $supervisorIds[$value] = true; + } + } + } + + $userMap = qdb_api_log_load_user_map(array_keys($userIds)); + $svMap = qdb_api_log_load_supervisor_map(array_keys($supervisorIds)); + + foreach ($entries as $i => $entry) { + if (empty($entry['actor_username']) && !empty($entry['userID'])) { + $entry['actor_username'] = $userMap[$entry['userID']] ?? null; + } + if (empty($entry['target_username'])) { + $entry['target_username'] = qdb_api_log_target_from_changes($entry['changes'] ?? []); + } + + $enriched = []; + foreach ($entry['changes'] ?? [] as $change) { + if (!is_array($change)) { + continue; + } + $change['display'] = qdb_api_log_change_display_label($change, $userMap, $svMap); + $enriched[] = $change; + } + $entry['changes'] = qdb_api_log_sort_changes_for_display($enriched); + $entry['summary'] = qdb_api_log_summarize_changes( + $entry['changes'], + $entry['target_username'] ?? null + ); + $entries[$i] = $entry; + } + + return $entries; +} + +/** @param list $userIds @return array */ +function qdb_api_log_load_user_map(array $userIds): array +{ + $userIds = array_values(array_filter($userIds, static fn($id) => $id !== '')); + if ($userIds === []) { + return []; + } + try { + require_once __DIR__ . '/../db_init.php'; + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $placeholders = implode(',', array_fill(0, count($userIds), '?')); + $stmt = $pdo->prepare( + "SELECT userID, username FROM users WHERE userID IN ($placeholders)" + ); + $stmt->execute($userIds); + $map = []; + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $map[(string)$row['userID']] = (string)$row['username']; + } + qdb_discard($tmpDb, $lockFp); + return $map; + } catch (Throwable) { + return []; + } +} + +/** @param list $supervisorIds @return array */ +function qdb_api_log_load_supervisor_map(array $supervisorIds): array +{ + $supervisorIds = array_values(array_filter($supervisorIds, static fn($id) => $id !== '')); + if ($supervisorIds === []) { + return []; + } + try { + require_once __DIR__ . '/../db_init.php'; + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $placeholders = implode(',', array_fill(0, count($supervisorIds), '?')); + $stmt = $pdo->prepare( + "SELECT supervisorID, username FROM supervisor WHERE supervisorID IN ($placeholders)" + ); + $stmt->execute($supervisorIds); + $map = []; + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $map[(string)$row['supervisorID']] = (string)$row['username']; + } + qdb_discard($tmpDb, $lockFp); + return $map; + } catch (Throwable) { + return []; + } +} + +/** @param list $changes */ +function qdb_api_log_target_from_changes(array $changes): ?string +{ + foreach ($changes as $c) { + $field = strtolower((string)($c['field'] ?? '')); + if ($field === 'username' && isset($c['value']) && is_scalar($c['value'])) { + return trim((string)$c['value']); + } + } + return null; +} + +/** + * @param array $userMap + * @param array $svMap + */ +function qdb_api_log_change_display_label(array $change, array $userMap, array $svMap): string +{ + $field = strtolower((string)($change['field'] ?? '')); + $value = $change['value'] ?? null; + if ($value === null) { + return 'null'; + } + if (!is_scalar($value)) { + return ''; + } + $str = (string)$value; + + if ($field === 'username') { + return $str; + } + if ($field === 'userid' || str_ends_with($field, '.userid')) { + return $userMap[$str] ?? $str; + } + if ($field === 'supervisorid' || str_ends_with($field, '.supervisorid')) { + return $svMap[$str] ?? $str; + } + if ($field === 'clientcode' || str_ends_with($field, 'clientcode')) { + return $str; + } + if ($field === 'mustchangepassword') { + return ((int)$value === 1) ? 'yes (temporary password)' : 'no (permanent password)'; + } + if ($field === 'password' || str_contains($field, 'password')) { + return (string)$value; + } + + return $str; +} + +/** + * @param list $changes + * @return list + */ +function qdb_api_log_sort_changes_for_display(array $changes): array +{ + $priority = [ + 'username' => 0, + 'type' => 1, + 'text' => 2, + 'languagecode' => 3, + 'id' => 4, + 'role' => 5, + 'clientcode' => 6, + 'action' => 7, + 'query.id' => 10, + 'query.clients' => 11, + 'query.clientcode' => 12, + 'query.answers' => 13, + 'query.translations' => 14, + 'query.bundle' => 15, + 'query.exportall' => 16, + 'query.exportbundle' => 17, + 'supervisorid' => 18, + 'userid' => 19, + 'mustchangepassword' => 20, + 'password' => 21, + 'location' => 22, + ]; + + usort($changes, static function (array $a, array $b) use ($priority): int { + $fa = strtolower((string)($a['field'] ?? '')); + $fb = strtolower((string)($b['field'] ?? '')); + $pa = $priority[$fa] ?? 50; + $pb = $priority[$fb] ?? 50; + if ($pa !== $pb) { + return $pa <=> $pb; + } + return strcmp($fa, $fb); + }); + + return $changes; +} + +/** @param list $changes */ +function qdb_api_log_summarize_changes(array $changes, ?string $targetUsername = null): string +{ + if ($targetUsername !== null && $targetUsername !== '') { + return 'user=' . $targetUsername; + } + if ($changes === []) { + return ''; + } + $parts = []; + foreach (array_slice($changes, 0, 8) as $c) { + $field = (string)($c['field'] ?? ''); + if ($field === '') { + continue; + } + $label = (string)($c['display'] ?? ''); + $value = $c['value'] ?? ''; + if ($label !== '' && $label !== (string)$value) { + $parts[] = $field . '=' . $label; + } elseif ($value === null) { + $parts[] = $field . '=null'; + } elseif (is_bool($value)) { + $parts[] = $field . '=' . ($value ? 'true' : 'false'); + } else { + $parts[] = $field . '=' . (string)$value; + } + } + $summary = implode(', ', $parts); + if (count($changes) > 8) { + $summary .= ', …'; + } + if (strlen($summary) > 240) { + $summary = substr($summary, 0, 240) . '…'; + } + return $summary; +} diff --git a/lib/app_answers.php b/lib/app_answers.php new file mode 100644 index 0000000..8c85783 --- /dev/null +++ b/lib/app_answers.php @@ -0,0 +1,306 @@ + */ +function qdb_decode_multi_check_values(?string $raw): array { + if ($raw === null || trim($raw) === '') { + return []; + } + $trimmed = trim($raw); + if ($trimmed[0] === '[') { + $decoded = json_decode($trimmed, true); + if (is_array($decoded)) { + $out = []; + foreach ($decoded as $v) { + $s = trim((string)$v); + if ($s !== '') { + $out[] = $s; + } + } + return $out; + } + } + $sep = str_contains($trimmed, ',') ? ',' : (str_contains($trimmed, ';') ? ';' : null); + if ($sep !== null) { + $parts = []; + foreach (explode($sep, $trimmed) as $p) { + $s = trim($p, " \t\n\r\0\x0B\"'"); + if ($s !== '') { + $parts[] = $s; + } + } + return $parts; + } + return [$trimmed]; +} + +/** + * Build glass-scale symptom JSON from the current submit only (no merge with prior rows). + * + * @param array $symptomLabels symptom key => selected label + */ +function qdb_build_glass_symptom_json(array $symptomLabels): ?string { + $data = []; + foreach ($symptomLabels as $key => $label) { + $k = trim((string)$key); + $v = trim((string)$label); + if ($k !== '' && $v !== '') { + $data[$k] = $v; + } + } + if ($data === []) { + return null; + } + return json_encode($data, JSON_UNESCAPED_UNICODE); +} + +/** + * Group raw POST answer rows by question short id; merge multi_check option keys. + * + * @param array $shortIdToType question localId => layout type + * @return list> + */ +function qdb_group_app_submit_answers(array $answers, array $shortIdToType, array $symptomParentMap): array { + $grouped = []; + $order = []; + + foreach ($answers as $a) { + if (!is_array($a)) { + continue; + } + $shortId = trim($a['questionID'] ?? ''); + if ($shortId === '') { + continue; + } + + if (isset($symptomParentMap[$shortId])) { + $order[] = $shortId; + $grouped[$shortId] = $a; + continue; + } + + $type = $shortIdToType[$shortId] ?? ''; + if ($type === 'multi_check_box_question') { + if (!isset($grouped[$shortId])) { + $order[] = $shortId; + $grouped[$shortId] = [ + 'questionID' => $shortId, + 'multiOptionKeys' => [], + 'answeredAt' => $a['answeredAt'] ?? null, + ]; + } + $key = trim((string)($a['answerOptionKey'] ?? '')); + if ($key !== '') { + $grouped[$shortId]['multiOptionKeys'][$key] = true; + } + continue; + } + + $order[] = $shortId; + $grouped[$shortId] = $a; + } + + $out = []; + foreach ($order as $shortId) { + $row = $grouped[$shortId]; + if (!empty($row['multiOptionKeys'])) { + $keys = array_keys($row['multiOptionKeys']); + $out[] = [ + 'questionID' => $shortId, + 'freeTextValue' => qdb_encode_multi_check_values($keys), + 'answeredAt' => $row['answeredAt'] ?? null, + ]; + } else { + unset($row['multiOptionKeys']); + $out[] = $row; + } + } + return $out; +} + +/** + * Export one client's answers for one questionnaire into app submit shape. + * + * @return array{answers: list>, sumPoints: int, completedAt: ?int} + */ +function qdb_export_app_questionnaire_answers( + PDO $pdo, + string $clientCode, + string $qnID +): array { + $qStmt = $pdo->prepare( + "SELECT questionID, type, configJson FROM question WHERE questionnaireID = :qn" + ); + $qStmt->execute([':qn' => $qnID]); + $shortToFull = []; + $fullToShort = []; + $fullToType = []; + $symptomParentMap = qdb_glass_symptom_parent_map($pdo, $qnID); + + foreach ($qStmt->fetchAll(PDO::FETCH_ASSOC) as $row) { + $full = $row['questionID']; + $parts = explode('__', $full); + $short = end($parts); + $shortToFull[$short] = $full; + $fullToShort[$full] = $short; + $fullToType[$full] = $row['type'] ?? ''; + } + + $aoStmt = $pdo->prepare(" + SELECT ao.answerOptionID, ao.questionID, ao.defaultText + FROM answer_option ao + JOIN question q ON q.questionID = ao.questionID + WHERE q.questionnaireID = :qn + "); + $aoStmt->execute([':qn' => $qnID]); + $optionIdToKey = []; + foreach ($aoStmt->fetchAll(PDO::FETCH_ASSOC) as $ao) { + $optionIdToKey[$ao['answerOptionID']] = $ao['defaultText']; + } + + $caStmt = $pdo->prepare(" + SELECT ca.questionID, ca.answerOptionID, ca.freeTextValue, ca.numericValue, ca.answeredAt + FROM client_answer ca + JOIN question q ON q.questionID = ca.questionID + WHERE ca.clientCode = :cc AND q.questionnaireID = :qn + "); + $caStmt->execute([':cc' => $clientCode, ':qn' => $qnID]); + $rows = $caStmt->fetchAll(PDO::FETCH_ASSOC); + + $export = []; + foreach ($rows as $row) { + $fullQid = $row['questionID']; + $shortId = $fullToShort[$fullQid] ?? ''; + if ($shortId === '') { + continue; + } + $type = $fullToType[$fullQid] ?? ''; + + if ($type === 'glass_scale_question' && ($row['freeTextValue'] ?? '') !== '') { + $decoded = json_decode($row['freeTextValue'], true); + if (is_array($decoded)) { + foreach ($decoded as $symptomKey => $label) { + $sk = trim((string)$symptomKey); + $lab = trim((string)$label); + if ($sk !== '' && $lab !== '') { + $export[] = [ + 'questionID' => $sk, + 'answerOptionKey' => $lab, + ]; + } + } + } + continue; + } + + if ($type === 'multi_check_box_question') { + foreach (qdb_decode_multi_check_values($row['freeTextValue'] ?? null) as $key) { + $export[] = [ + 'questionID' => $shortId, + 'answerOptionKey' => $key, + ]; + } + if ($export === [] && !empty($row['answerOptionID']) && isset($optionIdToKey[$row['answerOptionID']])) { + $export[] = [ + 'questionID' => $shortId, + 'answerOptionKey' => $optionIdToKey[$row['answerOptionID']], + ]; + } + continue; + } + + $entry = ['questionID' => $shortId]; + if (!empty($row['answerOptionID']) && isset($optionIdToKey[$row['answerOptionID']])) { + $entry['answerOptionKey'] = $optionIdToKey[$row['answerOptionID']]; + } + if (isset($row['freeTextValue']) && $row['freeTextValue'] !== null && $row['freeTextValue'] !== '') { + $entry['freeTextValue'] = $row['freeTextValue']; + } + if (isset($row['numericValue']) && $row['numericValue'] !== null && $row['numericValue'] !== '') { + $entry['numericValue'] = (float)$row['numericValue']; + } + if (!empty($row['answeredAt'])) { + $entry['answeredAt'] = (int)$row['answeredAt']; + } + $export[] = $entry; + } + + $cq = $pdo->prepare( + "SELECT sumPoints, completedAt FROM completed_questionnaire + WHERE clientCode = :cc AND questionnaireID = :qn" + ); + $cq->execute([':cc' => $clientCode, ':qn' => $qnID]); + $meta = $cq->fetch(PDO::FETCH_ASSOC) ?: []; + + return [ + 'answers' => $export, + 'sumPoints' => isset($meta['sumPoints']) ? (int)$meta['sumPoints'] : 0, + 'completedAt' => isset($meta['completedAt']) ? (int)$meta['completedAt'] : null, + ]; +} + +/** + * Export all completed questionnaires for a client (coach RBAC must be applied by caller). + * + * @return list> + */ +function qdb_export_app_client_answers_bundle(PDO $pdo, string $clientCode): array { + $stmt = $pdo->prepare( + "SELECT questionnaireID FROM completed_questionnaire WHERE clientCode = :cc" + ); + $stmt->execute([':cc' => $clientCode]); + $items = []; + foreach ($stmt->fetchAll(PDO::FETCH_COLUMN) as $qnID) { + $block = qdb_export_app_questionnaire_answers($pdo, $clientCode, $qnID); + $items[] = [ + 'questionnaireID' => $qnID, + 'completedAt' => $block['completedAt'], + 'sumPoints' => $block['sumPoints'], + 'answers' => $block['answers'], + ]; + } + return $items; +} + +/** + * Bulk export: all coach-assigned clients with full answer bundles (caller applies RBAC). + * + * @return list>}> + */ +function qdb_export_app_bulk_answers_for_coach(PDO $pdo, array $tokenRec): array { + if (($tokenRec['role'] ?? '') !== 'coach') { + return []; + } + $coachID = trim((string)($tokenRec['entityID'] ?? '')); + if ($coachID === '') { + return []; + } + $stmt = $pdo->prepare( + 'SELECT clientCode FROM client + WHERE coachID = :cid AND COALESCE(archived, 0) = 0 + ORDER BY clientCode' + ); + $stmt->execute([':cid' => $coachID]); + $out = []; + foreach ($stmt->fetchAll(PDO::FETCH_COLUMN) as $clientCode) { + $out[] = [ + 'clientCode' => $clientCode, + 'questionnaires' => qdb_export_app_client_answers_bundle($pdo, (string)$clientCode), + ]; + } + return $out; +} diff --git a/lib/app_submit_validate.php b/lib/app_submit_validate.php new file mode 100644 index 0000000..84d46d5 --- /dev/null +++ b/lib/app_submit_validate.php @@ -0,0 +1,221 @@ +|null $manifest Structure manifest for legacy revision submits + * @return list + */ +function qdb_validate_app_submit_payload( + PDO $pdo, + string $qnID, + array $rawAnswers, + array $shortIdMap, + array $shortIdToType, + array $symptomParentMap, + array $optionMap, + ?array $manifest = null +): array { + $errors = []; + + if ($rawAnswers === []) { + $errors[] = [ + 'questionID' => '', + 'code' => 'ANSWERS_REQUIRED', + 'message' => 'At least one answer is required', + ]; + return $errors; + } + + foreach ($rawAnswers as $idx => $a) { + if (!is_array($a)) { + $errors[] = [ + 'questionID' => '', + 'code' => 'INVALID_ANSWER', + 'message' => 'Answer at index ' . $idx . ' must be an object', + ]; + continue; + } + $shortId = trim($a['questionID'] ?? ''); + if ($shortId === '') { + $errors[] = [ + 'questionID' => '', + 'code' => 'MISSING_QUESTION_ID', + 'message' => 'Each answer must include questionID', + ]; + continue; + } + + if (isset($symptomParentMap[$shortId])) { + $label = trim((string)($a['answerOptionKey'] ?? $a['freeTextValue'] ?? '')); + if ($label === '') { + $errors[] = [ + 'questionID' => $shortId, + 'code' => 'EMPTY_ANSWER', + 'message' => 'Symptom selection is required', + ]; + } + continue; + } + + if (!isset($shortIdMap[$shortId])) { + $errors[] = [ + 'questionID' => $shortId, + 'code' => 'UNKNOWN_QUESTION', + 'message' => 'Unknown question: ' . $shortId, + ]; + continue; + } + + $fullQID = $shortIdMap[$shortId]; + $type = $shortIdToType[$shortId] ?? ''; + $optKey = $a['answerOptionKey'] ?? null; + $free = isset($a['freeTextValue']) ? trim((string)$a['freeTextValue']) : ''; + $numeric = $a['numericValue'] ?? null; + $hasNumeric = $numeric !== null && $numeric !== ''; + + if ($type === 'multi_check_box_question') { + if ($optKey !== null && trim((string)$optKey) !== '') { + continue; + } + if ($free !== '') { + continue; + } + $errors[] = [ + 'questionID' => $shortId, + 'code' => 'EMPTY_ANSWER', + 'message' => 'Select at least one option', + ]; + continue; + } + + if ($optKey !== null && trim((string)$optKey) !== '') { + $key = (string)$optKey; + if (!isset($optionMap[$fullQID][$key])) { + $errors[] = [ + 'questionID' => $shortId, + 'code' => 'INVALID_OPTION', + 'message' => 'Unknown option key: ' . $key, + ]; + } + continue; + } + + if ($free !== '' || $hasNumeric) { + continue; + } + + $errors[] = [ + 'questionID' => $shortId, + 'code' => 'EMPTY_ANSWER', + 'message' => 'Answer is empty', + ]; + } + + if ($errors !== []) { + return $errors; + } + + require_once __DIR__ . '/app_answers.php'; + $grouped = qdb_group_app_submit_answers($rawAnswers, $shortIdToType, $symptomParentMap); + $answeredShort = []; + foreach ($grouped as $row) { + $sid = trim($row['questionID'] ?? ''); + if ($sid !== '') { + $answeredShort[$sid] = true; + } + } + + if ($manifest !== null) { + return array_merge($errors, qdb_validate_required_answers_from_manifest($manifest, $answeredShort)); + } + + $qStmt = $pdo->prepare( + 'SELECT questionID, type, isRequired, configJson FROM question + WHERE questionnaireID = :qn AND ' . qdb_active_questions_clause('question') + ); + $qStmt->execute([':qn' => $qnID]); + foreach ($qStmt->fetchAll(PDO::FETCH_ASSOC) as $qRow) { + if ((int)($qRow['isRequired'] ?? 0) !== 1) { + continue; + } + $fullId = $qRow['questionID']; + $parts = explode('__', $fullId); + $short = end($parts); + $type = $qRow['type'] ?? ''; + + if ($type === 'glass_scale_question') { + $cfg = json_decode($qRow['configJson'] ?? '{}', true) ?: []; + $symptoms = $cfg['symptoms'] ?? []; + if (is_array($symptoms) && $symptoms !== []) { + foreach ($symptoms as $symptomKey) { + $sk = trim((string)$symptomKey); + if ($sk === '') { + continue; + } + if (empty($answeredShort[$sk])) { + $errors[] = [ + 'questionID' => $sk, + 'code' => 'MISSING_ANSWER', + 'message' => 'Required symptom is not answered', + ]; + } + } + continue; + } + } + + if (empty($answeredShort[$short])) { + $errors[] = [ + 'questionID' => $short, + 'code' => 'MISSING_ANSWER', + 'message' => 'Required question is not answered', + ]; + } + } + + return $errors; +} + +/** + * @return list + */ +function qdb_validate_required_answers_from_manifest(array $manifest, array $answeredShort): array { + $errors = []; + foreach ($manifest['questions'] ?? [] as $qEntry) { + if (!is_array($qEntry) || empty($qEntry['isRequired'])) { + continue; + } + $shortId = (string)($qEntry['shortId'] ?? ''); + $type = (string)($qEntry['type'] ?? ''); + if ($shortId === '') { + continue; + } + if ($type === 'glass_scale_question') { + foreach ($qEntry['symptoms'] ?? [] as $symptomKey) { + $sk = trim((string)$symptomKey); + if ($sk === '') { + continue; + } + if (empty($answeredShort[$sk])) { + $errors[] = [ + 'questionID' => $sk, + 'code' => 'MISSING_ANSWER', + 'message' => 'Required symptom is not answered', + ]; + } + } + continue; + } + if (empty($answeredShort[$shortId])) { + $errors[] = [ + 'questionID' => $shortId, + 'code' => 'MISSING_ANSWER', + 'message' => 'Required question is not answered', + ]; + } + } + return $errors; +} diff --git a/lib/dev_fixture.php b/lib/dev_fixture.php new file mode 100644 index 0000000..7b2ac5b --- /dev/null +++ b/lib/dev_fixture.php @@ -0,0 +1,911 @@ + 0, + 'little_glass' => 1, + 'moderate_glass' => 2, + 'much_glass' => 3, + 'extreme_glass' => 4, +]; + +require_once __DIR__ . '/app_answers.php'; +require_once __DIR__ . '/submissions.php'; +require_once __DIR__ . '/scoring.php'; + +/** + * @return array{imported: array, skipped: array, errors: string[]} + */ +function qdb_import_dev_fixture(PDO $pdo, array $fixture): array +{ + $prefix = qdb_dev_validate_fixture($fixture); + $password = (string)($fixture['defaultPassword'] ?? 'socialvrlab'); + if (strlen($password) < 6) { + json_error('INVALID_FIELD', 'defaultPassword must be at least 6 characters', 400); + } + + $imported = [ + 'admins' => 0, + 'supervisors' => 0, + 'coaches' => 0, + 'clients' => 0, + 'completions' => 0, + 'submissions' => 0, + 'answers' => 0, + 'scoringRecomputed' => 0, + ]; + $skipped = [ + 'admins' => 0, + 'supervisors' => 0, + 'coaches' => 0, + 'clients' => 0, + 'completions' => 0, + ]; + $errors = []; + + $coachIdByUsername = []; + $supervisorIdByUsername = []; + $affectedClients = []; + + $hash = password_hash($password, PASSWORD_DEFAULT); + $now = time(); + + $pdo->beginTransaction(); + try { + foreach ($fixture['admins'] ?? [] as $row) { + $username = qdb_dev_norm_username($row, $prefix); + if (qdb_dev_user_exists($pdo, $username)) { + $skipped['admins']++; + continue; + } + $entityID = qdb_dev_entity_id('admin', $username); + $userID = qdb_dev_user_id($username); + $location = trim($row['location'] ?? 'Dev'); + $pdo->prepare('INSERT INTO admin (adminID, username, location) VALUES (:id, :u, :loc)') + ->execute([':id' => $entityID, ':u' => $username, ':loc' => $location]); + qdb_dev_insert_user($pdo, $userID, $username, $hash, 'admin', $entityID, $now); + $imported['admins']++; + } + + foreach ($fixture['supervisors'] ?? [] as $row) { + $username = qdb_dev_norm_username($row, $prefix); + if (qdb_dev_user_exists($pdo, $username)) { + $skipped['supervisors']++; + $supervisorIdByUsername[$username] = qdb_dev_lookup_entity_id($pdo, $username); + continue; + } + $entityID = qdb_dev_entity_id('supervisor', $username); + $userID = qdb_dev_user_id($username); + $location = trim($row['location'] ?? 'Dev'); + $pdo->prepare('INSERT INTO supervisor (supervisorID, username, location) VALUES (:id, :u, :loc)') + ->execute([':id' => $entityID, ':u' => $username, ':loc' => $location]); + qdb_dev_insert_user($pdo, $userID, $username, $hash, 'supervisor', $entityID, $now); + $supervisorIdByUsername[$username] = $entityID; + $imported['supervisors']++; + } + + foreach ($fixture['coaches'] ?? [] as $row) { + $username = qdb_dev_norm_username($row, $prefix); + $svUser = trim($row['supervisor'] ?? $row['supervisorUsername'] ?? ''); + if ($svUser === '' || !qdb_dev_has_prefix($svUser, $prefix)) { + $errors[] = "Counselor $username: missing or invalid supervisor"; + continue; + } + $supervisorID = $supervisorIdByUsername[$svUser] + ?? qdb_dev_lookup_entity_id($pdo, $svUser); + if ($supervisorID === '') { + $errors[] = "Counselor $username: supervisor $svUser not found"; + continue; + } + if (qdb_dev_user_exists($pdo, $username)) { + $skipped['coaches']++; + $coachIdByUsername[$username] = qdb_dev_lookup_entity_id($pdo, $username); + continue; + } + $entityID = qdb_dev_entity_id('coach', $username); + $userID = qdb_dev_user_id($username); + $pdo->prepare('INSERT INTO coach (coachID, supervisorID, username) VALUES (:id, :sid, :u)') + ->execute([':id' => $entityID, ':sid' => $supervisorID, ':u' => $username]); + qdb_dev_insert_user($pdo, $userID, $username, $hash, 'coach', $entityID, $now); + $coachIdByUsername[$username] = $entityID; + $imported['coaches']++; + } + + foreach ($fixture['clients'] ?? [] as $row) { + $clientCode = qdb_dev_norm_client_code($row, $prefix); + $coachUser = trim($row['coach'] ?? $row['coachUsername'] ?? ''); + if ($coachUser === '' || !qdb_dev_has_prefix($coachUser, $prefix)) { + $errors[] = "Client $clientCode: missing or invalid coach"; + continue; + } + $coachID = $coachIdByUsername[$coachUser] + ?? qdb_dev_lookup_entity_id($pdo, $coachUser); + if ($coachID === '') { + $errors[] = "Client $clientCode: coach $coachUser not found"; + continue; + } + $chk = $pdo->prepare('SELECT 1 FROM client WHERE clientCode = :cc'); + $chk->execute([':cc' => $clientCode]); + if ($chk->fetch()) { + $skipped['clients']++; + continue; + } + $pdo->prepare('INSERT INTO client (clientCode, coachID) VALUES (:cc, :cid)') + ->execute([':cc' => $clientCode, ':cid' => $coachID]); + $imported['clients']++; + } + + $variant = 0; + foreach ($fixture['completions'] ?? [] as $row) { + $clientCode = qdb_dev_norm_client_code($row, $prefix); + $qnID = trim($row['questionnaireID'] ?? ''); + if ($qnID === '') { + $errors[] = 'Completion missing questionnaireID'; + continue; + } + $qnChk = $pdo->prepare('SELECT 1 FROM questionnaire WHERE questionnaireID = :id'); + $qnChk->execute([':id' => $qnID]); + if (!$qnChk->fetch()) { + $errors[] = "Questionnaire not in DB: $qnID"; + continue; + } + $clChk = $pdo->prepare('SELECT coachID FROM client WHERE clientCode = :cc'); + $clChk->execute([':cc' => $clientCode]); + $coachID = $clChk->fetchColumn(); + if ($coachID === false) { + $errors[] = "Client not found: $clientCode"; + continue; + } + $doneChk = $pdo->prepare( + 'SELECT 1 FROM completed_questionnaire WHERE clientCode = :cc AND questionnaireID = :qn' + ); + $doneChk->execute([':cc' => $clientCode, ':qn' => $qnID]); + if ($doneChk->fetch()) { + $skipped['completions']++; + continue; + } + + $coachUserStmt = $pdo->prepare( + "SELECT u.userID FROM users u + WHERE u.role = 'coach' AND u.entityID = :cid LIMIT 1" + ); + $coachUserStmt->execute([':cid' => $coachID]); + $coachUserID = (string)($coachUserStmt->fetchColumn() ?: ''); + $tokenRec = ['userID' => $coachUserID, 'role' => 'coach']; + + $uploads = qdb_dev_normalize_uploads($row, $variant, $now); + $archiveSubmissions = qdb_table_exists($pdo, 'questionnaire_submission'); + + foreach ($uploads as $uploadIndex => $upload) { + $variantSeed = (int)($upload['variant'] ?? ($variant + $uploadIndex)); + $completedAt = (int)($upload['submittedAt'] ?? $upload['completedAt'] ?? $now); + $startedAt = (int)($upload['startedAt'] ?? ($completedAt - 600)); + + $answers = qdb_dev_build_answers_for_questionnaire($pdo, $qnID, $variantSeed); + $answerCount = qdb_dev_persist_submission( + $pdo, + $qnID, + $clientCode, + (string)$coachID, + $answers, + $startedAt, + $completedAt + ); + $imported['answers'] += $answerCount; + + if ($archiveSubmissions) { + $spStmt = $pdo->prepare( + 'SELECT sumPoints FROM completed_questionnaire + WHERE clientCode = :cc AND questionnaireID = :qn' + ); + $spStmt->execute([':cc' => $clientCode, ':qn' => $qnID]); + $sumPoints = (int)($spStmt->fetchColumn() ?: 0); + qdb_record_submission_after_submit( + $pdo, + $clientCode, + $qnID, + $tokenRec, + $startedAt, + $completedAt, + $sumPoints, + (string)$coachID, + $completedAt + ); + $imported['submissions']++; + } + } + + $imported['completions']++; + $affectedClients[$clientCode] = true; + $variant++; + } + + $upperPrefix = strtoupper(rtrim($prefix, '_')); + $devClientStmt = $pdo->prepare('SELECT clientCode FROM client WHERE clientCode LIKE :pfx'); + $devClientStmt->execute([':pfx' => $upperPrefix . '%']); + foreach ($devClientStmt->fetchAll(PDO::FETCH_COLUMN) as $cc) { + $affectedClients[(string)$cc] = true; + } + if ($affectedClients !== []) { + $imported['scoringRecomputed'] = qdb_recompute_scores_for_clients( + $pdo, + array_keys($affectedClients) + ); + } + + $pdo->commit(); + } catch (Throwable $e) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + throw $e; + } + + return ['imported' => $imported, 'skipped' => $skipped, 'errors' => $errors]; +} + +function qdb_dev_validate_fixture(array $fixture): string +{ + $prefix = trim((string)($fixture['prefix'] ?? 'dev_')); + if ($prefix === '' || !preg_match('/^dev[a-z0-9_]*$/i', $prefix)) { + json_error('INVALID_FIELD', 'fixture.prefix must start with "dev" (e.g. dev_)', 400); + } + $fixtureVersion = (int)($fixture['fixtureVersion'] ?? 0); + if ($fixtureVersion !== 1 && $fixtureVersion !== 2) { + json_error('INVALID_FIELD', 'fixtureVersion must be 1 or 2', 400); + } + return $prefix; +} + +/** + * @return list + */ +function qdb_dev_normalize_uploads(array $row, int $fallbackVariant, int $now): array +{ + if (!empty($row['uploads']) && is_array($row['uploads'])) { + $out = []; + foreach ($row['uploads'] as $i => $upload) { + if (!is_array($upload)) { + continue; + } + $submittedAt = (int)($upload['submittedAt'] ?? $upload['completedAt'] ?? $now); + $startedAt = (int)($upload['startedAt'] ?? ($submittedAt - 600)); + $out[] = [ + 'submittedAt' => $submittedAt, + 'startedAt' => $startedAt, + 'variant' => (int)($upload['variant'] ?? ($fallbackVariant + $i)), + ]; + } + if ($out !== []) { + usort($out, static fn($a, $b) => $a['submittedAt'] <=> $b['submittedAt']); + return $out; + } + } + + $versionCount = max(1, (int)($row['versions'] ?? $row['uploadCount'] ?? 1)); + $finalCompleted = isset($row['completedAt']) ? (int)$row['completedAt'] : ($now - ($fallbackVariant % 90) * 86400); + $uploads = []; + for ($v = 0; $v < $versionCount; $v++) { + $gapDays = ($versionCount - 1 - $v) * 14 + ($fallbackVariant % 5); + $completedAt = $finalCompleted - ($gapDays * 86400); + $uploads[] = [ + 'submittedAt' => $completedAt, + 'startedAt' => isset($row['startedAt']) && $v === $versionCount - 1 + ? (int)$row['startedAt'] + : ($completedAt - 600 - ($v * 120)), + 'variant' => $fallbackVariant + $v, + ]; + } + usort($uploads, static fn($a, $b) => $a['submittedAt'] <=> $b['submittedAt']); + return $uploads; +} + +function qdb_dev_has_prefix(string $value, string $prefix): bool +{ + return str_starts_with(strtolower($value), strtolower($prefix)) + || str_starts_with(strtoupper($value), strtoupper(rtrim($prefix, '_'))); +} + +function qdb_dev_norm_username(array $row, string $prefix): string +{ + $username = trim($row['username'] ?? ''); + if ($username === '' || !qdb_dev_has_prefix($username, $prefix)) { + json_error('INVALID_FIELD', "Username must use prefix $prefix", 400); + } + return $username; +} + +function qdb_dev_norm_client_code(array $row, string $prefix): string +{ + $code = trim($row['clientCode'] ?? ''); + $upperPrefix = strtoupper(rtrim($prefix, '_')); + if ($code === '' || !str_starts_with($code, $upperPrefix)) { + json_error('INVALID_FIELD', "clientCode must start with $upperPrefix", 400); + } + return $code; +} + +function qdb_dev_entity_id(string $role, string $username): string +{ + return 'dev_ent_' . $role . '_' . substr(hash('sha256', $username), 0, 24); +} + +function qdb_dev_user_id(string $username): string +{ + return 'dev_uid_' . substr(hash('sha256', 'user:' . $username), 0, 28); +} + +function qdb_dev_user_exists(PDO $pdo, string $username): bool +{ + $stmt = $pdo->prepare('SELECT 1 FROM users WHERE username = :u'); + $stmt->execute([':u' => $username]); + return (bool)$stmt->fetch(); +} + +function qdb_dev_lookup_entity_id(PDO $pdo, string $username): string +{ + $stmt = $pdo->prepare('SELECT entityID FROM users WHERE username = :u'); + $stmt->execute([':u' => $username]); + $id = $stmt->fetchColumn(); + return $id !== false ? (string)$id : ''; +} + +function qdb_dev_insert_user( + PDO $pdo, + string $userID, + string $username, + string $hash, + string $role, + string $entityID, + int $now +): void { + $mcp = 0; + $pdo->prepare( + 'INSERT INTO users (userID, username, passwordHash, role, entityID, mustChangePassword, createdAt) + VALUES (:uid, :u, :hash, :role, :eid, :mcp, :now)' + )->execute([ + ':uid' => $userID, + ':u' => $username, + ':hash' => $hash, + ':role' => $role, + ':eid' => $entityID, + ':mcp' => $mcp, + ':now' => $now, + ]); +} + +/** + * @return list + */ +function qdb_dev_build_answers_for_questionnaire(PDO $pdo, string $qnID, int $variant): array +{ + $stmt = $pdo->prepare( + 'SELECT questionID, type, configJson, orderIndex + FROM question WHERE questionnaireID = :qn ORDER BY orderIndex' + ); + $stmt->execute([':qn' => $qnID]); + $questions = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $aoStmt = $pdo->prepare( + 'SELECT ao.answerOptionID, ao.questionID, ao.defaultText, ao.points, ao.orderIndex + FROM answer_option ao + JOIN question q ON q.questionID = ao.questionID + WHERE q.questionnaireID = :qn + ORDER BY ao.orderIndex' + ); + $aoStmt->execute([':qn' => $qnID]); + $optionsByQuestion = []; + foreach ($aoStmt->fetchAll(PDO::FETCH_ASSOC) as $ao) { + $optionsByQuestion[$ao['questionID']][] = $ao; + } + + $answers = []; + $freeTextSamples = ['Testantwort', 'Dev-Eingabe', 'Beispieltext', 'N/A']; + + foreach ($questions as $q) { + $type = $q['type'] ?? ''; + $localId = qdb_question_local_id($q['questionID'], $qnID); + $config = json_decode($q['configJson'] ?? '{}', true) ?: []; + $v = $variant + (int)($q['orderIndex'] ?? 0); + + switch ($type) { + case 'last_page': + break; + + case 'glass_scale_question': + $symData = []; + foreach ($config['symptoms'] ?? [] as $si => $symptomKey) { + $sk = trim((string)$symptomKey); + if ($sk === '') { + continue; + } + $symData[$sk] = QDB_DEV_GLASS_LABELS[($v + $si) % count(QDB_DEV_GLASS_LABELS)]; + } + if ($symData !== []) { + $answers[] = [ + 'questionID' => $localId, + 'freeTextValue' => json_encode($symData, JSON_UNESCAPED_UNICODE), + ]; + } + break; + + case 'radio_question': + $opts = $optionsByQuestion[$q['questionID']] ?? []; + if ($opts) { + $pick = $opts[$v % count($opts)]; + $key = qdb_option_key($pick) ?: $pick['defaultText']; + $answers[] = ['questionID' => $localId, 'answerOptionKey' => $key]; + } + break; + + case 'multi_check_box_question': + $opts = $optionsByQuestion[$q['questionID']] ?? []; + if ($opts) { + $pickCount = min(5, max(2, count($opts) > 20 ? 4 : 2), count($opts)); + $keys = []; + for ($i = 0; $i < $pickCount; $i++) { + $pick = $opts[($v + $i) % count($opts)]; + $key = qdb_option_key($pick) ?: $pick['defaultText']; + if ($key !== '') { + $keys[] = $key; + } + } + if ($keys !== []) { + $answers[] = [ + 'questionID' => $localId, + 'freeTextValue' => qdb_encode_multi_check_values($keys), + ]; + } + } + break; + + case 'string_spinner': + $opts = $config['options'] ?? []; + if (is_array($opts) && $opts !== []) { + $answers[] = [ + 'questionID' => $localId, + 'freeTextValue' => (string)$opts[$v % count($opts)], + ]; + } + break; + + case 'value_spinner': + case 'slider_question': + $range = $config['range'] ?? ['min' => 0, 'max' => 10]; + $min = (int)($range['min'] ?? 0); + $max = (int)($range['max'] ?? 10); + if ($max < $min) { + $max = $min; + } + $span = max(1, $max - $min + 1); + $answers[] = [ + 'questionID' => $localId, + 'numericValue' => (float)($min + ($v % $span)), + ]; + break; + + case 'date_spinner': + $year = 2018 + ($v % 6); + $month = 1 + ($v % 12); + $day = 1 + ($v % 28); + $precision = $config['precision'] ?? 'full'; + $dateValue = match ($precision) { + 'year' => sprintf('%04d', $year), + 'year_month' => sprintf('%04d-%02d', $year, $month), + default => sprintf('%04d-%02d-%02d', $year, $month, $day), + }; + $answers[] = [ + 'questionID' => $localId, + 'freeTextValue' => $dateValue, + ]; + break; + + case 'free_text': + $answers[] = [ + 'questionID' => $localId, + 'freeTextValue' => $freeTextSamples[$v % count($freeTextSamples)], + ]; + break; + + case 'client_coach_code_question': + $answers[] = [ + 'questionID' => $localId, + 'freeTextValue' => 'DEV-IMPORT', + ]; + break; + + default: + break; + } + } + + return $answers; +} + +/** + * @param list $answers + */ +function qdb_dev_persist_submission( + PDO $pdo, + string $qnID, + string $clientCode, + string $assignedByCoach, + array $answers, + int $startedAt, + int $completedAt +): int { + $qRows = $pdo->prepare('SELECT questionID, type, configJson FROM question WHERE questionnaireID = :qn'); + $qRows->execute([':qn' => $qnID]); + $shortIdMap = []; + $typeByFullId = []; + $freeTextMaxLen = []; + foreach ($qRows->fetchAll(PDO::FETCH_ASSOC) as $qRow) { + $fullId = $qRow['questionID']; + $parts = explode('__', $fullId); + $shortIdMap[end($parts)] = $fullId; + $typeByFullId[$fullId] = $qRow['type'] ?? ''; + if (($qRow['type'] ?? '') === 'free_text') { + $cfg = json_decode($qRow['configJson'] ?? '{}', true) ?: []; + $freeTextMaxLen[$fullId] = max(1, min((int)($cfg['maxLength'] ?? 500), 10000)); + } + } + + $aoRows = $pdo->prepare( + 'SELECT ao.answerOptionID, ao.questionID, ao.defaultText, ao.points + FROM answer_option ao + JOIN question q ON q.questionID = ao.questionID + WHERE q.questionnaireID = :qn' + ); + $aoRows->execute([':qn' => $qnID]); + $optionMap = []; + foreach ($aoRows->fetchAll(PDO::FETCH_ASSOC) as $ao) { + $optionMap[$ao['questionID']][$ao['defaultText']] = [ + 'answerOptionID' => $ao['answerOptionID'], + 'points' => (int)$ao['points'], + ]; + } + + $sumPoints = 0; + $written = 0; + $answeredAt = $completedAt; + $answerInsert = $pdo->prepare( + 'INSERT INTO client_answer (clientCode, questionID, answerOptionID, freeTextValue, numericValue, answeredAt) + VALUES (:cc, :qid, :aoid, :ftv, :nv, :at) + ON CONFLICT(clientCode, questionID) DO UPDATE SET + answerOptionID = excluded.answerOptionID, + freeTextValue = excluded.freeTextValue, + numericValue = excluded.numericValue, + answeredAt = excluded.answeredAt' + ); + + foreach ($answers as $a) { + $shortId = trim($a['questionID'] ?? ''); + if ($shortId === '') { + continue; + } + + $fullQID = $shortIdMap[$shortId] ?? null; + if ($fullQID === null) { + continue; + } + + $freeTextValue = isset($a['freeTextValue']) ? (string)$a['freeTextValue'] : null; + $numericValue = isset($a['numericValue']) ? (float)$a['numericValue'] : null; + if ($freeTextValue !== null && $freeTextValue !== '') { + $maxLen = $freeTextMaxLen[$fullQID] ?? 2000; + $freeTextValue = sanitize_free_text($freeTextValue, $maxLen); + if ($freeTextValue === '') { + continue; + } + } + + $answerOptionID = null; + $qType = $typeByFullId[$fullQID] ?? ''; + + if ($qType === 'glass_scale_question' && $freeTextValue !== null && $freeTextValue !== '') { + $decoded = json_decode($freeTextValue, true); + if (is_array($decoded)) { + foreach ($decoded as $label) { + $label = trim((string)$label); + if ($label !== '') { + $sumPoints += QDB_DEV_GLASS_POINTS[$label] ?? 0; + } + } + } + } elseif ($qType === 'multi_check_box_question' && $freeTextValue !== null && $freeTextValue !== '') { + foreach (qdb_decode_multi_check_values($freeTextValue) as $mk) { + if (isset($optionMap[$fullQID][$mk])) { + $sumPoints += $optionMap[$fullQID][$mk]['points']; + } + } + } else { + $optionKey = $a['answerOptionKey'] ?? null; + if ($optionKey !== null && isset($optionMap[$fullQID][(string)$optionKey])) { + $opt = $optionMap[$fullQID][(string)$optionKey]; + $answerOptionID = $opt['answerOptionID']; + $sumPoints += $opt['points']; + } + } + + $answerInsert->execute([ + ':cc' => $clientCode, + ':qid' => $fullQID, + ':aoid' => $answerOptionID, + ':ftv' => $freeTextValue, + ':nv' => $numericValue, + ':at' => $answeredAt, + ]); + $written++; + } + + $pdo->prepare( + 'INSERT INTO completed_questionnaire (clientCode, questionnaireID, assignedByCoach, status, startedAt, completedAt, sumPoints) + VALUES (:cc, :qn, :abc, \'completed\', :sa, :ca, :sp) + ON CONFLICT(clientCode, questionnaireID) DO UPDATE SET + assignedByCoach = excluded.assignedByCoach, + status = \'completed\', + startedAt = COALESCE(excluded.startedAt, startedAt), + completedAt = excluded.completedAt, + sumPoints = excluded.sumPoints' + )->execute([ + ':cc' => $clientCode, + ':qn' => $qnID, + ':abc' => $assignedByCoach !== '' ? $assignedByCoach : null, + ':sa' => $startedAt, + ':ca' => $completedAt, + ':sp' => $sumPoints, + ]); + + return $written; +} + +/** @param list $ids */ +function qdb_dev_delete_by_ids(PDO $pdo, string $table, string $column, array $ids): int +{ + $ids = array_values(array_unique(array_filter($ids, static fn($id) => $id !== '' && $id !== null))); + if ($ids === []) { + return 0; + } + $total = 0; + foreach (array_chunk($ids, 200) as $chunk) { + $ph = implode(',', array_fill(0, count($chunk), '?')); + $stmt = $pdo->prepare("DELETE FROM $table WHERE $column IN ($ph)"); + $stmt->execute($chunk); + $total += $stmt->rowCount(); + } + return $total; +} + +/** + * Remove dev-prefixed test users, clients, and their answers (does not touch other accounts). + * + * @return array{deleted: array} + */ +function qdb_remove_dev_test_data(PDO $pdo, array $options = []): array +{ + $usernamePrefix = trim((string)($options['usernamePrefix'] ?? 'dev_')); + $clientPrefix = trim((string)($options['clientCodePrefix'] ?? 'DEV-CL-')); + if ($usernamePrefix === '' || stripos($usernamePrefix, 'dev') !== 0) { + json_error('INVALID_FIELD', 'usernamePrefix must start with "dev"', 400); + } + if ($clientPrefix === '' || stripos($clientPrefix, 'DEV') !== 0) { + json_error('INVALID_FIELD', 'clientCodePrefix must start with "DEV"', 400); + } + + $userLike = $usernamePrefix . '%'; + $clientLike = $clientPrefix . '%'; + + $deleted = [ + 'submissions' => 0, + 'followup_notes' => 0, + 'client_answers' => 0, + 'completed_questionnaires' => 0, + 'clients' => 0, + 'sessions' => 0, + 'users' => 0, + 'coaches' => 0, + 'supervisors' => 0, + 'admins' => 0, + ]; + + $coachIds = []; + $stmt = $pdo->prepare('SELECT coachID FROM coach WHERE username LIKE :pfx'); + $stmt->execute([':pfx' => $userLike]); + foreach ($stmt->fetchAll(PDO::FETCH_COLUMN) as $id) { + $coachIds[$id] = true; + } + + $supervisorIds = []; + $stmt = $pdo->prepare('SELECT supervisorID FROM supervisor WHERE username LIKE :pfx'); + $stmt->execute([':pfx' => $userLike]); + foreach ($stmt->fetchAll(PDO::FETCH_COLUMN) as $id) { + $supervisorIds[$id] = true; + } + + $adminIds = []; + $stmt = $pdo->prepare('SELECT adminID FROM admin WHERE username LIKE :pfx'); + $stmt->execute([':pfx' => $userLike]); + foreach ($stmt->fetchAll(PDO::FETCH_COLUMN) as $id) { + $adminIds[$id] = true; + } + + $userIds = []; + $userRows = $pdo->prepare('SELECT userID, role, entityID FROM users WHERE username LIKE :pfx'); + $userRows->execute([':pfx' => $userLike]); + foreach ($userRows->fetchAll(PDO::FETCH_ASSOC) as $u) { + $userIds[$u['userID']] = true; + switch ($u['role']) { + case 'coach': + $coachIds[$u['entityID']] = true; + break; + case 'supervisor': + $supervisorIds[$u['entityID']] = true; + break; + case 'admin': + $adminIds[$u['entityID']] = true; + break; + } + } + + $coachIdList = array_keys($coachIds); + $clientCodes = []; + $ccStmt = $pdo->prepare('SELECT clientCode FROM client WHERE clientCode LIKE :pfx'); + $ccStmt->execute([':pfx' => $clientLike]); + foreach ($ccStmt->fetchAll(PDO::FETCH_COLUMN) as $code) { + $clientCodes[$code] = true; + } + if ($coachIdList !== []) { + foreach (array_chunk($coachIdList, 200) as $chunk) { + $ph = implode(',', array_fill(0, count($chunk), '?')); + $byCoach = $pdo->prepare("SELECT clientCode FROM client WHERE coachID IN ($ph)"); + $byCoach->execute($chunk); + foreach ($byCoach->fetchAll(PDO::FETCH_COLUMN) as $code) { + $clientCodes[$code] = true; + } + } + } + $clientCodeList = array_keys($clientCodes); + + $pdo->beginTransaction(); + try { + if ($clientCodeList !== []) { + $responseDeleted = qdb_delete_client_response_data($pdo, $clientCodeList); + $deleted['submissions'] += $responseDeleted['submissions']; + $deleted['followup_notes'] += $responseDeleted['followup_notes']; + $deleted['client_answers'] += $responseDeleted['client_answers']; + $deleted['completed_questionnaires'] += $responseDeleted['completed_questionnaires']; + } + if ($coachIdList !== []) { + $deleted['submissions'] += qdb_delete_submissions_for_coaches($pdo, $coachIdList); + $deleted['completed_questionnaires'] += qdb_dev_delete_by_ids( + $pdo, + 'completed_questionnaire', + 'assignedByCoach', + $coachIdList + ); + } + + if ($clientCodeList !== []) { + $deleted['clients'] += qdb_dev_delete_by_ids($pdo, 'client', 'clientCode', $clientCodeList); + } + if ($coachIdList !== []) { + foreach (array_chunk($coachIdList, 200) as $chunk) { + $ph = implode(',', array_fill(0, count($chunk), '?')); + $delCl = $pdo->prepare("DELETE FROM client WHERE coachID IN ($ph)"); + $delCl->execute($chunk); + $deleted['clients'] += $delCl->rowCount(); + } + } + $delClPrefix = $pdo->prepare('DELETE FROM client WHERE clientCode LIKE :pfx'); + $delClPrefix->execute([':pfx' => $clientLike]); + $deleted['clients'] += $delClPrefix->rowCount(); + + $deleted['sessions'] = qdb_dev_delete_by_ids($pdo, 'session', 'userID', array_keys($userIds)); + + $deleted['users'] = qdb_dev_delete_by_ids($pdo, 'users', 'userID', array_keys($userIds)); + $delUsersLike = $pdo->prepare('DELETE FROM users WHERE username LIKE :pfx'); + $delUsersLike->execute([':pfx' => $userLike]); + $deleted['users'] += $delUsersLike->rowCount(); + + $deleted['coaches'] = qdb_dev_delete_by_ids($pdo, 'coach', 'coachID', $coachIdList); + $delCoachLike = $pdo->prepare('DELETE FROM coach WHERE username LIKE :pfx'); + $delCoachLike->execute([':pfx' => $userLike]); + $deleted['coaches'] += $delCoachLike->rowCount(); + + $deleted['supervisors'] = qdb_dev_delete_by_ids($pdo, 'supervisor', 'supervisorID', array_keys($supervisorIds)); + $delSvLike = $pdo->prepare('DELETE FROM supervisor WHERE username LIKE :pfx'); + $delSvLike->execute([':pfx' => $userLike]); + $deleted['supervisors'] += $delSvLike->rowCount(); + + $deleted['admins'] = qdb_dev_delete_by_ids($pdo, 'admin', 'adminID', array_keys($adminIds)); + $delAdLike = $pdo->prepare('DELETE FROM admin WHERE username LIKE :pfx'); + $delAdLike->execute([':pfx' => $userLike]); + $deleted['admins'] += $delAdLike->rowCount(); + + $pdo->commit(); + } catch (Throwable $e) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + throw $e; + } + + return ['deleted' => $deleted]; +} + +/** + * Remove all clients, completions, coaches, and supervisors. Keeps admin accounts + * and questionnaire definitions (questions, translations, etc.). + * + * @return array{deleted: array, kept: array} + */ +function qdb_wipe_all_data_except_admins(PDO $pdo): array +{ + $deleted = [ + 'client_answers' => 0, + 'completed_questionnaires' => 0, + 'clients' => 0, + 'sessions' => 0, + 'users' => 0, + 'coaches' => 0, + 'supervisors' => 0, + ]; + + $keptStmt = $pdo->query("SELECT COUNT(*) FROM users WHERE role = 'admin'"); + $keptAdmins = (int)$keptStmt->fetchColumn(); + + $pdo->beginTransaction(); + try { + // Interview data references clients/coaches; coach references supervisor. + // Disable FK checks for this bulk wipe (same pattern as questionnaire delete). + $pdo->exec('PRAGMA foreign_keys = OFF'); + + $deleted['client_answers'] = (int)$pdo->exec('DELETE FROM client_answer'); + if (qdb_table_exists($pdo, 'client_answer_submission')) { + $pdo->exec('DELETE FROM client_answer_submission'); + } + if (qdb_table_exists($pdo, 'questionnaire_submission')) { + $pdo->exec('DELETE FROM questionnaire_submission'); + } + if (qdb_table_exists($pdo, 'client_followup_note')) { + $pdo->exec('DELETE FROM client_followup_note'); + } + $deleted['completed_questionnaires'] = (int)$pdo->exec('DELETE FROM completed_questionnaire'); + $deleted['clients'] = (int)$pdo->exec('DELETE FROM client'); + + $deleted['coaches'] = (int)$pdo->exec('DELETE FROM coach'); + $deleted['supervisors'] = (int)$pdo->exec('DELETE FROM supervisor'); + + $sess = $pdo->prepare( + "DELETE FROM session WHERE userID IN (SELECT userID FROM users WHERE role != 'admin')" + ); + $sess->execute(); + $deleted['sessions'] = $sess->rowCount(); + + $users = $pdo->prepare("DELETE FROM users WHERE role != 'admin'"); + $users->execute(); + $deleted['users'] = $users->rowCount(); + + $pdo->exec('PRAGMA foreign_keys = ON'); + $pdo->commit(); + } catch (Throwable $e) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + try { + $pdo->exec('PRAGMA foreign_keys = ON'); + } catch (Throwable $ignored) { + } + throw $e; + } + + return [ + 'deleted' => $deleted, + 'kept' => ['admins' => $keptAdmins], + ]; +} diff --git a/lib/encrypted_payload.php b/lib/encrypted_payload.php new file mode 100644 index 0000000..f007235 --- /dev/null +++ b/lib/encrypted_payload.php @@ -0,0 +1,60 @@ + true, + 'alg' => 'A256GCM', + 'payload' => base64_encode(qdb_aes256_gcm_encrypt_bytes($plainJson, $key)), + ]; +} + +function qdb_decrypt_sensitive_envelope(array $envelope, string $tokenHex): string { + if (empty($envelope['encrypted']) || !isset($envelope['payload']) || !is_string($envelope['payload'])) { + throw new InvalidArgumentException('Invalid encrypted envelope'); + } + $raw = base64_decode($envelope['payload'], true); + if ($raw === false) { + throw new InvalidArgumentException('Invalid base64 payload'); + } + $key = hkdf_session_key_from_token($tokenHex); + $alg = isset($envelope['alg']) && is_string($envelope['alg']) ? $envelope['alg'] : 'A256CBC'; + return match ($alg) { + 'A256GCM' => qdb_aes256_gcm_decrypt_bytes($raw, $key), + 'A256CBC' => aes256_cbc_decrypt_bytes($raw, $key), + default => throw new InvalidArgumentException('Unsupported encrypted envelope algorithm'), + }; +} + +function qdb_aes256_gcm_encrypt_bytes(string $plain, string $key): string { + $key = str_pad(substr($key, 0, 32), 32, "\0"); + $iv = random_bytes(12); + $tag = ''; + $cipher = openssl_encrypt($plain, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag, ''); + if ($cipher === false || strlen($tag) !== 16) { + throw new RuntimeException('openssl_encrypt failed'); + } + return $iv . $cipher . $tag; +} + +function qdb_aes256_gcm_decrypt_bytes(string $data, string $key): string { + $ivLen = 12; + $tagLen = 16; + if (strlen($data) <= $ivLen + $tagLen) { + throw new InvalidArgumentException('cipher too short'); + } + $key = str_pad(substr($key, 0, 32), 32, "\0"); + $iv = substr($data, 0, $ivLen); + $tag = substr($data, -$tagLen); + $ct = substr($data, $ivLen, -$tagLen); + $plain = openssl_decrypt($ct, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $iv, $tag, ''); + if ($plain === false) { + throw new InvalidArgumentException('openssl_decrypt failed'); + } + return $plain; +} diff --git a/lib/errors.php b/lib/errors.php new file mode 100644 index 0000000..022e5e5 --- /dev/null +++ b/lib/errors.php @@ -0,0 +1,89 @@ +getMessage(); + return str_contains($msg, 'Could not acquire lock') + || str_contains($msg, 'Could not open lock file'); +} + +/** + * Map internal exceptions to safe, identifiable API error messages (details go to error_log). + */ +function qdb_public_error_message(Throwable $e, string $contextLabel = 'Operation'): string +{ + $msg = $e->getMessage(); + + if (qdb_is_lock_exception($e)) { + return 'Database is busy because another update is in progress. Please try again in a moment.'; + } + if (str_contains($msg, 'decrypt') || str_contains($msg, 'QDB_MASTER_KEY')) { + return 'Database configuration error. Contact the server administrator.'; + } + if (str_contains($msg, 'Could not read') || str_contains($msg, 'Could not write') + || str_contains($msg, 'Could not save')) { + return 'Database storage error. Contact the server administrator.'; + } + if ($e instanceof PDOException) { + return $contextLabel . ' failed due to a database error.'; + } + + return $contextLabel . ' failed. If this persists, contact support.'; +} + +/** + * @return array{0: string, 1: string, 2: int} code, message, http status + */ +function qdb_api_error_for_exception(Throwable $e, string $contextLabel = 'Operation'): array +{ + if (qdb_is_lock_exception($e)) { + return ['LOCKED', qdb_public_error_message($e, $contextLabel), 503]; + } + return ['SERVER_ERROR', qdb_public_error_message($e, $contextLabel), 500]; +} + +/** + * Standard handler catch: rollback, discard DB temp file, log, JSON error (never returns). + */ +function qdb_handler_fail( + Throwable $e, + string $contextLabel, + ?PDO $pdo = null, + ?string $tmpDb = null, + $lockFp = null +): never { + if (defined('QDB_TESTING') && qdb_test_is_control_flow_exception($e)) { + throw $e; + } + if ($pdo !== null && $pdo->inTransaction()) { + try { + $pdo->rollBack(); + } catch (Throwable) { + } + } + if ($tmpDb !== null && $lockFp !== null) { + require_once __DIR__ . '/../db_init.php'; + qdb_discard($tmpDb, $lockFp); + } + qdb_emit_api_error($e, $contextLabel); +} + +/** + * Log exception and exit with unified JSON error (never returns). + */ +function qdb_emit_api_error(Throwable $e, string $contextLabel): never +{ + if (defined('QDB_TESTING') && qdb_test_is_control_flow_exception($e)) { + throw $e; + } + if (function_exists('qdb_api_log_note_exception')) { + qdb_api_log_note_exception($e); + } + error_log($contextLabel . ': ' . $e->getMessage()); + [$code, $message, $status] = qdb_api_error_for_exception($e, $contextLabel); + json_error($code, $message, $status); +} diff --git a/lib/keycloak_auth.php b/lib/keycloak_auth.php new file mode 100644 index 0000000..276aeb8 --- /dev/null +++ b/lib/keycloak_auth.php @@ -0,0 +1,221 @@ + $issuer, + 'client_id' => $clientId, + 'client_secret' => $clientSecret, + 'redirect_uri' => (string)(qdb_env_get('QDB_KEYCLOAK_REDIRECT_URI') ?? qdb_keycloak_default_redirect_uri()), + 'username_claim' => (string)(qdb_env_get('QDB_KEYCLOAK_USERNAME_CLAIM') ?? 'preferred_username'), + 'display_name' => (string)(qdb_env_get('QDB_KEYCLOAK_DISPLAY_NAME') ?? 'University Konstanz'), + ]; +} + +function qdb_keycloak_is_configured(?array $config = null): bool +{ + $config ??= qdb_keycloak_config(); + return $config['issuer'] !== '' && $config['client_id'] !== '' && $config['client_secret'] !== ''; +} + +function qdb_keycloak_default_redirect_uri(): string +{ + $https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') + || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https'); + $scheme = $https ? 'https' : 'http'; + $host = $_SERVER['HTTP_HOST'] ?? 'localhost'; + $script = $_SERVER['SCRIPT_NAME'] ?? '/api/index.php'; + $base = rtrim(str_replace('\\', '/', dirname($script)), '/'); + return $scheme . '://' . $host . $base . '/auth/keycloak-callback'; +} + +function qdb_keycloak_frontend_url(): string +{ + $configured = (string)(qdb_env_get('QDB_KEYCLOAK_FRONTEND_URL') ?? ''); + if ($configured !== '') { + return $configured; + } + $https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') + || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https'); + $scheme = $https ? 'https' : 'http'; + $host = $_SERVER['HTTP_HOST'] ?? 'localhost'; + $script = $_SERVER['SCRIPT_NAME'] ?? '/api/index.php'; + $apiBase = rtrim(str_replace('\\', '/', dirname($script)), '/'); + $root = preg_replace('#/api$#', '', $apiBase) ?: ''; + return $scheme . '://' . $host . $root . '/website/index.html'; +} + +function qdb_keycloak_state_signing_key(): string +{ + $secret = qdb_env_get('QDB_KEYCLOAK_STATE_SECRET') + ?? qdb_env_get('QDB_MASTER_KEY') + ?? qdb_env_get('QDB_KEYCLOAK_CLIENT_SECRET') + ?? ''; + if ($secret === '') { + throw new RuntimeException('No signing secret available for Keycloak state'); + } + return $secret; +} + +function qdb_keycloak_base64url(string $bytes): string +{ + return rtrim(strtr(base64_encode($bytes), '+/', '-_'), '='); +} + +function qdb_keycloak_unbase64url(string $value): string +{ + $padded = strtr($value, '-_', '+/'); + $padded .= str_repeat('=', (4 - strlen($padded) % 4) % 4); + $decoded = base64_decode($padded, true); + if ($decoded === false) { + throw new InvalidArgumentException('Invalid base64url value'); + } + return $decoded; +} + +function qdb_keycloak_create_state(string $nonce): string +{ + $payload = json_encode([ + 'nonce' => $nonce, + 'exp' => time() + 600, + ], JSON_THROW_ON_ERROR); + $body = qdb_keycloak_base64url($payload); + $sig = qdb_keycloak_base64url(hash_hmac('sha256', $body, qdb_keycloak_state_signing_key(), true)); + return $body . '.' . $sig; +} + +function qdb_keycloak_verify_state(string $state): array +{ + $parts = explode('.', $state, 2); + if (count($parts) !== 2) { + json_error('INVALID_STATE', 'Invalid Keycloak state', 400); + } + [$body, $sig] = $parts; + $expected = qdb_keycloak_base64url(hash_hmac('sha256', $body, qdb_keycloak_state_signing_key(), true)); + if (!hash_equals($expected, $sig)) { + json_error('INVALID_STATE', 'Invalid Keycloak state', 400); + } + $payload = json_decode(qdb_keycloak_unbase64url($body), true); + if (!is_array($payload) || (int)($payload['exp'] ?? 0) < time()) { + json_error('INVALID_STATE', 'Expired Keycloak state', 400); + } + return $payload; +} + +function qdb_keycloak_discovery(array $config): array +{ + $url = $config['issuer'] . '/.well-known/openid-configuration'; + $json = qdb_keycloak_http_json($url); + foreach (['authorization_endpoint', 'token_endpoint', 'userinfo_endpoint'] as $key) { + if (empty($json[$key])) { + throw new RuntimeException("Keycloak discovery missing $key"); + } + } + return $json; +} + +function qdb_keycloak_http_json(string $url, array $options = []): array +{ + $context = stream_context_create(['http' => [ + 'method' => $options['method'] ?? 'GET', + 'header' => $options['headers'] ?? '', + 'content' => $options['content'] ?? '', + 'timeout' => 10, + 'ignore_errors' => true, + ]]); + $raw = file_get_contents($url, false, $context); + if ($raw === false) { + throw new RuntimeException('Keycloak request failed'); + } + $status = 200; + foreach (($http_response_header ?? []) as $header) { + if (preg_match('#^HTTP/\S+\s+(\d+)#', $header, $m)) { + $status = (int)$m[1]; + break; + } + } + $json = json_decode($raw, true); + if ($status < 200 || $status >= 300 || !is_array($json)) { + throw new RuntimeException('Keycloak returned an invalid response'); + } + return $json; +} + +function qdb_keycloak_exchange_code(array $config, array $discovery, string $code): array +{ + $body = http_build_query([ + 'grant_type' => 'authorization_code', + 'code' => $code, + 'redirect_uri' => $config['redirect_uri'], + 'client_id' => $config['client_id'], + 'client_secret' => $config['client_secret'], + ]); + return qdb_keycloak_http_json($discovery['token_endpoint'], [ + 'method' => 'POST', + 'headers' => "Content-Type: application/x-www-form-urlencoded\r\n", + 'content' => $body, + ]); +} + +function qdb_keycloak_userinfo(array $discovery, string $accessToken): array +{ + return qdb_keycloak_http_json($discovery['userinfo_endpoint'], [ + 'headers' => "Authorization: Bearer $accessToken\r\n", + ]); +} + +function qdb_keycloak_claim_username(array $config, array $claims): string +{ + $claim = $config['username_claim'] ?: 'preferred_username'; + $username = trim((string)($claims[$claim] ?? '')); + if ($username === '' && $claim !== 'email') { + $username = trim((string)($claims['email'] ?? '')); + } + if ($username === '') { + json_error('KEYCLOAK_NO_USERNAME', 'University login did not provide a usable username', 403); + } + return $username; +} + +function qdb_keycloak_redirect(string $url, int $status = 302): never +{ + if (defined('QDB_TESTING')) { + throw new \Tests\Support\RawHttpResponse('', ['Location' => $url], $status); + } + http_response_code($status); + header('Location: ' . $url, true, $status); + exit; +} + +function qdb_keycloak_finish_html(string $token, string $user, string $role): never +{ + $target = qdb_keycloak_frontend_url() . '#/'; + $payload = json_encode([ + 'token' => $token, + 'user' => $user, + 'role' => $role, + 'target' => $target, + ], JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_THROW_ON_ERROR); + qdb_http_download( + "", + ['Content-Type' => 'text/html; charset=UTF-8'] + ); +} + diff --git a/lib/login_rate_limit.php b/lib/login_rate_limit.php new file mode 100644 index 0000000..6533e56 --- /dev/null +++ b/lib/login_rate_limit.php @@ -0,0 +1,176 @@ + [], 'locked_until' => 0]; + $lockedUntil = (int)($entry['locked_until'] ?? 0); + if ($lockedUntil > $now) { + return [false, $lockedUntil - $now]; + } + $attempts = array_values(array_filter( + (array)($entry['attempts'] ?? []), + static fn($t) => is_int($t) && $t > $now - $window + )); + if (count($attempts) >= $max) { + $entry['locked_until'] = $now + $lockout; + $entry['attempts'] = $attempts; + $data[$key] = $entry; + ftruncate($fp, 0); + rewind($fp); + fwrite($fp, json_encode($data, JSON_UNESCAPED_UNICODE)); + fflush($fp); + return [false, $lockout]; + } + return [true, 0]; + } finally { + flock($fp, LOCK_UN); + fclose($fp); + } +} + +function qdb_login_rate_limit_record_failure(string $username, ?array $settings = null): int +{ + $settings ??= qdb_settings_get(); + $max = (int)$settings['login_max_attempts']; + $window = (int)$settings['login_window_seconds']; + $lockout = (int)$settings['login_lockout_seconds']; + $now = time(); + $key = qdb_login_rate_limit_key($username); + $path = qdb_login_rate_limit_path(); + $dir = dirname($path); + if (!is_dir($dir)) { + @mkdir($dir, 0755, true); + } + + $fp = @fopen($path, 'c+'); + if ($fp === false) { + return 0; + } + try { + if (!flock($fp, LOCK_EX)) { + return 0; + } + $raw = stream_get_contents($fp); + $data = is_string($raw) && $raw !== '' ? json_decode($raw, true) : []; + if (!is_array($data)) { + $data = []; + } + $entry = $data[$key] ?? ['attempts' => [], 'locked_until' => 0]; + $attempts = array_values(array_filter( + (array)($entry['attempts'] ?? []), + static fn($t) => is_int($t) && $t > $now - $window + )); + $attempts[] = $now; + $retry = 0; + if (count($attempts) >= $max) { + $entry['locked_until'] = $now + $lockout; + $retry = $lockout; + } + $entry['attempts'] = $attempts; + $data[$key] = $entry; + ftruncate($fp, 0); + rewind($fp); + fwrite($fp, json_encode($data, JSON_UNESCAPED_UNICODE)); + fflush($fp); + return $retry; + } finally { + flock($fp, LOCK_UN); + fclose($fp); + } +} + +function qdb_login_rate_limit_clear(string $username): void +{ + $key = qdb_login_rate_limit_key($username); + $path = qdb_login_rate_limit_path(); + if (!is_file($path)) { + return; + } + $fp = @fopen($path, 'c+'); + if ($fp === false) { + return; + } + try { + if (!flock($fp, LOCK_EX)) { + return; + } + $raw = stream_get_contents($fp); + $data = is_string($raw) && $raw !== '' ? json_decode($raw, true) : []; + if (!is_array($data) || !isset($data[$key])) { + return; + } + unset($data[$key]); + ftruncate($fp, 0); + rewind($fp); + fwrite($fp, json_encode($data, JSON_UNESCAPED_UNICODE)); + fflush($fp); + } finally { + flock($fp, LOCK_UN); + fclose($fp); + } +} + +function qdb_login_rate_limit_deny(int $retryAfterSeconds): never +{ + if ($retryAfterSeconds > 0) { + header('Retry-After: ' . (string)$retryAfterSeconds); + } + json_error( + 'RATE_LIMITED', + 'Too many login attempts. Please wait before trying again.', + 429 + ); +} diff --git a/lib/questionnaire_structure.php b/lib/questionnaire_structure.php new file mode 100644 index 0000000..a4e6e18 --- /dev/null +++ b/lib/questionnaire_structure.php @@ -0,0 +1,414 @@ +prepare( + 'SELECT COALESCE(structureRevision, 1) FROM questionnaire WHERE questionnaireID = :id' + ); + $stmt->execute([':id' => $qnID]); + $rev = $stmt->fetchColumn(); + return $rev !== false ? max(1, (int)$rev) : 1; +} + +/** + * @return array + */ +function qdb_build_structure_manifest(PDO $pdo, string $qnID, bool $includeRetired = false): array { + $where = 'questionnaireID = :qn'; + if (!$includeRetired) { + $where .= ' AND ' . qdb_active_questions_clause('question'); + } + $qStmt = $pdo->prepare( + "SELECT questionID, type, isRequired, configJson, defaultText, retiredAt + FROM question WHERE $where ORDER BY orderIndex" + ); + $qStmt->execute([':qn' => $qnID]); + $questions = []; + foreach ($qStmt->fetchAll(PDO::FETCH_ASSOC) as $qRow) { + $fullId = $qRow['questionID']; + $shortId = qdb_question_local_id($fullId, $qnID); + $config = json_decode($qRow['configJson'] ?? '{}', true) ?: []; + $qKey = qdb_question_key($config, $qRow['defaultText']); + + $optWhere = 'questionID = :qid'; + if (!$includeRetired) { + $optWhere .= ' AND ' . qdb_active_options_clause('answer_option'); + } + $aoStmt = $pdo->prepare( + "SELECT defaultText, points FROM answer_option WHERE $optWhere ORDER BY orderIndex" + ); + $aoStmt->execute([':qid' => $fullId]); + $options = []; + foreach ($aoStmt->fetchAll(PDO::FETCH_ASSOC) as $ao) { + $options[] = [ + 'key' => $ao['defaultText'], + 'points' => (int)$ao['points'], + ]; + } + + $entry = [ + 'questionID' => $fullId, + 'shortId' => $shortId, + 'questionKey'=> $qKey, + 'type' => $qRow['type'] ?? '', + 'isRequired' => (int)($qRow['isRequired'] ?? 0) === 1, + 'options' => $options, + ]; + if ($includeRetired && (int)($qRow['retiredAt'] ?? 0) > 0) { + $entry['retired'] = true; + } + if (($qRow['type'] ?? '') === 'glass_scale_question' && !empty($config['symptoms'])) { + $entry['symptoms'] = array_values(array_map('strval', (array)$config['symptoms'])); + } + $questions[] = $entry; + } + + return [ + 'questionnaireID' => $qnID, + 'structureRevision' => qdb_questionnaire_structure_revision($pdo, $qnID), + 'questions' => $questions, + ]; +} + +/** + * @return array|null + */ +function qdb_load_structure_manifest(PDO $pdo, string $qnID, int $revision): ?array { + $stmt = $pdo->prepare( + 'SELECT manifestJson FROM questionnaire_structure_snapshot + WHERE questionnaireID = :qn AND structureRevision = :rev' + ); + $stmt->execute([':qn' => $qnID, ':rev' => $revision]); + $json = $stmt->fetchColumn(); + if ($json === false || $json === '') { + return null; + } + $decoded = json_decode((string)$json, true); + return is_array($decoded) ? $decoded : null; +} + +function qdb_save_structure_snapshot(PDO $pdo, string $qnID, int $revision, array $manifest): void { + $json = json_encode($manifest, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + if ($json === false) { + $json = '{}'; + } + $pdo->prepare( + 'INSERT INTO questionnaire_structure_snapshot (questionnaireID, structureRevision, createdAt, manifestJson) + VALUES (:qn, :rev, :ts, :mj) + ON CONFLICT(questionnaireID, structureRevision) DO UPDATE SET + manifestJson = excluded.manifestJson, + createdAt = excluded.createdAt' + )->execute([ + ':qn' => $qnID, + ':rev' => $revision, + ':ts' => time(), + ':mj' => $json, + ]); +} + +/** + * Backfill revision-1 snapshots for questionnaires missing any snapshot row. + */ +function qdb_backfill_structure_snapshots(PDO $pdo): bool { + $ids = $pdo->query('SELECT questionnaireID FROM questionnaire')->fetchAll(PDO::FETCH_COLUMN); + $changed = false; + foreach ($ids as $qnID) { + $qnID = (string)$qnID; + $chk = $pdo->prepare( + 'SELECT 1 FROM questionnaire_structure_snapshot + WHERE questionnaireID = :qn LIMIT 1' + ); + $chk->execute([':qn' => $qnID]); + if ($chk->fetchColumn()) { + continue; + } + $rev = qdb_questionnaire_structure_revision($pdo, $qnID); + $manifest = qdb_build_structure_manifest($pdo, $qnID, true); + $manifest['structureRevision'] = $rev; + qdb_save_structure_snapshot($pdo, $qnID, $rev, $manifest); + $changed = true; + } + return $changed; +} + +function qdb_bump_structure_revision(PDO $pdo, string $qnID, string $reason = ''): int { + $current = qdb_questionnaire_structure_revision($pdo, $qnID); + $manifest = qdb_build_structure_manifest($pdo, $qnID, false); + $manifest['structureRevision'] = $current; + qdb_save_structure_snapshot($pdo, $qnID, $current, $manifest); + + $newRev = $current + 1; + $pdo->prepare( + 'UPDATE questionnaire SET structureRevision = :rev, structureChangedAt = :ts WHERE questionnaireID = :qn' + )->execute([':rev' => $newRev, ':ts' => time(), ':qn' => $qnID]); + + return $newRev; +} + +function qdb_questionnaire_id_for_question(PDO $pdo, string $questionID): ?string { + $stmt = $pdo->prepare('SELECT questionnaireID FROM question WHERE questionID = :qid'); + $stmt->execute([':qid' => $questionID]); + $id = $stmt->fetchColumn(); + return $id !== false ? (string)$id : null; +} + +function qdb_option_has_client_data(PDO $pdo, string $answerOptionID): bool { + $stmt = $pdo->prepare('SELECT 1 FROM client_answer WHERE answerOptionID = :id LIMIT 1'); + $stmt->execute([':id' => $answerOptionID]); + if ($stmt->fetchColumn()) { + return true; + } + $chk = $pdo->query( + "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'client_answer_submission' LIMIT 1" + ); + if (!$chk || !$chk->fetchColumn()) { + return false; + } + $stmt = $pdo->prepare( + 'SELECT 1 FROM client_answer_submission WHERE answerOptionID = :id LIMIT 1' + ); + $stmt->execute([':id' => $answerOptionID]); + return (bool)$stmt->fetchColumn(); +} + +function qdb_question_has_client_data(PDO $pdo, string $questionID): bool { + $stmt = $pdo->prepare('SELECT 1 FROM client_answer WHERE questionID = :qid LIMIT 1'); + $stmt->execute([':qid' => $questionID]); + if ($stmt->fetchColumn()) { + return true; + } + $chk = $pdo->query( + "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'client_answer_submission' LIMIT 1" + ); + if (!$chk || !$chk->fetchColumn()) { + return false; + } + $stmt = $pdo->prepare( + 'SELECT 1 FROM client_answer_submission WHERE questionID = :qid LIMIT 1' + ); + $stmt->execute([':qid' => $questionID]); + return (bool)$stmt->fetchColumn(); +} + +function qdb_retire_question(PDO $pdo, string $questionID, int $revision): void { + $pdo->prepare( + 'UPDATE question SET retiredAt = :ts, retiredInRevision = :rev + WHERE questionID = :qid AND COALESCE(retiredAt, 0) = 0' + )->execute([':ts' => time(), ':rev' => $revision, ':qid' => $questionID]); +} + +/** + * Build submit maps from a structure manifest (current or legacy). + * + * @return array{ + * shortIdMap: array, + * shortIdToType: array, + * optionMap: array>, + * symptomParentMap: array, + * freeTextMaxLen: array, + * manifestQuestionIds: list + * } + */ +function qdb_submit_maps_from_manifest(PDO $pdo, string $qnID, array $manifest): array { + $shortIdMap = []; + $shortIdToType = []; + $optionMap = []; + $symptomParentMap = []; + $freeTextMaxLen = []; + $manifestQuestionIds = []; + + foreach ($manifest['questions'] ?? [] as $qEntry) { + if (!is_array($qEntry)) { + continue; + } + $fullId = (string)($qEntry['questionID'] ?? ''); + $shortId = (string)($qEntry['shortId'] ?? ''); + if ($fullId === '' || $shortId === '') { + continue; + } + $type = (string)($qEntry['type'] ?? ''); + $shortIdMap[$shortId] = $fullId; + $shortIdToType[$shortId] = $type; + $manifestQuestionIds[] = $fullId; + + if ($type === 'free_text') { + $cfgStmt = $pdo->prepare('SELECT configJson FROM question WHERE questionID = :qid'); + $cfgStmt->execute([':qid' => $fullId]); + $cfgJson = $cfgStmt->fetchColumn(); + $cfg = json_decode($cfgJson ?: '{}', true) ?: []; + $freeTextMaxLen[$fullId] = max(1, min((int)($cfg['maxLength'] ?? 500), 10000)); + } + + $optionMap[$fullId] = []; + foreach ($qEntry['options'] ?? [] as $opt) { + if (!is_array($opt)) { + continue; + } + $key = (string)($opt['key'] ?? ''); + if ($key === '') { + continue; + } + $aoStmt = $pdo->prepare( + 'SELECT answerOptionID, points FROM answer_option + WHERE questionID = :qid AND defaultText = :k LIMIT 1' + ); + $aoStmt->execute([':qid' => $fullId, ':k' => $key]); + $aoRow = $aoStmt->fetch(PDO::FETCH_ASSOC); + $optionMap[$fullId][$key] = [ + 'answerOptionID' => $aoRow ? (string)$aoRow['answerOptionID'] : '', + 'points' => $aoRow ? (int)$aoRow['points'] : (int)($opt['points'] ?? 0), + ]; + } + + if ($type === 'glass_scale_question') { + foreach ($qEntry['symptoms'] ?? [] as $symptomKey) { + $sk = trim((string)$symptomKey); + if ($sk !== '') { + $symptomParentMap[$sk] = $fullId; + } + } + } + } + + if ($symptomParentMap === []) { + $symptomParentMap = qdb_glass_symptom_parent_map($pdo, $qnID); + } + + return [ + 'shortIdMap' => $shortIdMap, + 'shortIdToType' => $shortIdToType, + 'optionMap' => $optionMap, + 'symptomParentMap' => $symptomParentMap, + 'freeTextMaxLen' => $freeTextMaxLen, + 'manifestQuestionIds' => $manifestQuestionIds, + ]; +} + +/** + * Build submit maps from active DB questions (current revision). + * + * @return array + */ +function qdb_submit_maps_from_active_questions(PDO $pdo, string $qnID): array { + $manifest = qdb_build_structure_manifest($pdo, $qnID, false); + $manifest['structureRevision'] = qdb_questionnaire_structure_revision($pdo, $qnID); + return qdb_submit_maps_from_manifest($pdo, $qnID, $manifest); +} + +/** + * @return list + */ +function qdb_result_columns_from_manifest(array $manifest): array { + $cols = []; + foreach ($manifest['questions'] ?? [] as $qEntry) { + if (!is_array($qEntry)) { + continue; + } + $fullId = (string)($qEntry['questionID'] ?? ''); + $shortId = (string)($qEntry['shortId'] ?? ''); + $qKey = (string)($qEntry['questionKey'] ?? ''); + $type = (string)($qEntry['type'] ?? ''); + if ($fullId === '') { + continue; + } + $header = $qKey !== '' ? $qKey : ($shortId !== '' ? $shortId : $fullId); + $cols[] = [ + 'field' => $header, + 'header' => $header, + 'questionID' => $fullId, + 'type' => $type, + ]; + if ($type === 'glass_scale_question') { + foreach ($qEntry['symptoms'] ?? [] as $symptomKey) { + $sk = trim((string)$symptomKey); + if ($sk === '') { + continue; + } + $cols[] = [ + 'field' => $sk, + 'header' => $sk, + 'questionID' => $fullId, + 'type' => 'glass_symptom', + 'symptomKey' => $sk, + ]; + } + } + } + return $cols; +} + +function qdb_compute_questionnaire_score_from_manifest( + PDO $pdo, + string $clientCode, + array $manifest +): int { + require_once __DIR__ . '/scoring.php'; + $qnID = (string)($manifest['questionnaireID'] ?? ''); + if ($qnID === '') { + return 0; + } + + $questionIDs = []; + foreach ($manifest['questions'] ?? [] as $qEntry) { + if (is_array($qEntry) && !empty($qEntry['questionID'])) { + $questionIDs[] = (string)$qEntry['questionID']; + } + } + if ($questionIDs === []) { + return 0; + } + + $ph = implode(',', array_fill(0, count($questionIDs), '?')); + $aStmt = $pdo->prepare( + "SELECT ca.questionID, ca.answerOptionID, ca.freeTextValue, ca.numericValue, + ao.defaultText AS optionKey + FROM client_answer ca + LEFT JOIN answer_option ao ON ao.answerOptionID = ca.answerOptionID + WHERE ca.clientCode = ? AND ca.questionID IN ($ph)" + ); + $aStmt->execute(array_merge([$clientCode], $questionIDs)); + $answersByQuestion = []; + foreach ($aStmt->fetchAll(PDO::FETCH_ASSOC) as $a) { + $answersByQuestion[$a['questionID']] = $a; + } + + $total = 0; + foreach ($manifest['questions'] ?? [] as $qEntry) { + if (!is_array($qEntry)) { + continue; + } + $fullId = (string)($qEntry['questionID'] ?? ''); + $type = (string)($qEntry['type'] ?? ''); + $optionPoints = []; + foreach ($qEntry['options'] ?? [] as $opt) { + if (is_array($opt) && isset($opt['key'])) { + $optionPoints[(string)$opt['key']] = (int)($opt['points'] ?? 0); + } + } + $qRow = [ + 'questionID' => $fullId, + 'type' => $type, + 'configJson' => json_encode( + ['symptoms' => $qEntry['symptoms'] ?? []], + JSON_UNESCAPED_UNICODE + ), + ]; + $total += qdb_score_points_for_question( + $pdo, + $qRow, + $answersByQuestion[$fullId] ?? null, + $optionPoints + ); + } + return $total; +} diff --git a/lib/read_db_cache.php b/lib/read_db_cache.php new file mode 100644 index 0000000..6f58a99 --- /dev/null +++ b/lib/read_db_cache.php @@ -0,0 +1,299 @@ +getMessage()); + return null; + } + return $ffi; +} + +function qdb_close_fd(int $fd): void { + if ($fd < 0) { + return; + } + if (function_exists('posix_close')) { + @posix_close($fd); + return; + } + $ffi = qdb_linux_libc_ffi(); + if ($ffi !== null) { + $ffi->close($fd); + } +} + +/** + * Fill a raw fd (e.g. memfd). /proc/self/fd/N writes are blocked on some hosts; + * fall back to libc write() via FFI. + */ +function qdb_memfd_fill_fd(int $fd, string $bytes): bool { + $len = strlen($bytes); + if ($len === 0) { + return true; + } + $written = @file_put_contents('/proc/self/fd/' . $fd, $bytes); + if ($written === $len) { + return true; + } + $ffi = qdb_linux_libc_ffi(); + if ($ffi === null) { + return false; + } + $offset = 0; + while ($offset < $len) { + $chunkLen = min(1024 * 1024, $len - $offset); + $chunk = substr($bytes, $offset, $chunkLen); + $buf = FFI::new('char[' . $chunkLen . ']'); + FFI::memcpy($buf, $chunk, $chunkLen); + $n = (int)$ffi->write($fd, $buf, $chunkLen); + if ($n <= 0) { + return false; + } + $offset += $n; + } + return true; +} + +/** + * @return array{0: int, 1: string}|null [fd, /proc/self/fd/N] + */ +function qdb_memfd_write(string $bytes): ?array { + $ffi = qdb_linux_libc_ffi(); + if ($ffi === null) { + return null; + } + try { + $MFD_CLOEXEC = 1; + $fd = (int)$ffi->memfd_create('qdb_read', $MFD_CLOEXEC); + if ($fd < 0) { + return null; + } + if (!qdb_memfd_fill_fd($fd, $bytes)) { + qdb_close_fd($fd); + return null; + } + return [$fd, '/proc/self/fd/' . $fd]; + } catch (Throwable $e) { + error_log('qdb_memfd_write: ' . $e->getMessage()); + return null; + } +} + +function qdb_read_open_sqlite_on_path(string $sqlitePath, string $schemaSql, bool $fresh): PDO { + if ($fresh) { + $pdo = new PDO('sqlite:' . $sqlitePath); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $pdo->exec($schemaSql); + $pdo->exec('PRAGMA user_version = ' . QDB_VERSION . ';'); + } else { + $pdo = new PDO('sqlite:' . $sqlitePath); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + $pdo->exec('PRAGMA foreign_keys = ON;'); + // DELETE (not WAL) so a migration persist via file_get_contents + qdb_save is complete. + $pdo->exec('PRAGMA journal_mode = DELETE;'); + + $schemaChanged = qdb_apply_migrations($pdo, $schemaSql); + if ($schemaChanged) { + $migrateLock = qdb_acquire_lock(); + try { + if ($sqlitePath === ':memory:') { + throw new Exception('Cannot persist migration from in-memory read snapshot'); + } + $savePath = $sqlitePath; + if (str_starts_with($sqlitePath, '/proc/')) { + $savePath = tempnam(sys_get_temp_dir(), 'qdb_migrate_'); + $bytes = file_get_contents($sqlitePath); + if ($bytes === false || file_put_contents($savePath, $bytes) === false) { + throw new Exception('Could not copy memfd DB for migration save'); + } + } elseif (!is_file($savePath)) { + throw new Exception('Cannot persist migration from missing read snapshot'); + } + $pdo->exec('PRAGMA wal_checkpoint(FULL);'); + qdb_save($savePath, $migrateLock); + if ($savePath !== $sqlitePath && is_file($savePath)) { + @unlink($savePath); + } + } catch (Throwable $e) { + flock($migrateLock, LOCK_UN); + fclose($migrateLock); + throw $e; + } + return qdb_read_db_open()[0]; + } + + return $pdo; +} + +/** + * @return PDO|null + */ +function qdb_read_cache_get(string $signature) { + $state = &qdb_read_cache_state(); + if ($state['signature'] === $signature && $state['pdo'] instanceof PDO) { + return $state['pdo']; + } + return null; +} + +function qdb_read_cache_store(string $signature, PDO $pdo, string $memfdPath, int $memfdFd): void { + qdb_read_cache_reset(); + $state = &qdb_read_cache_state(); + $state['signature'] = $signature; + $state['pdo'] = $pdo; + $state['memfdPath'] = $memfdPath; + $state['memfdFd'] = $memfdFd; +} + +function qdb_read_cache_reset(): void { + $state = &qdb_read_cache_state(); + $state['signature'] = ''; + $state['pdo'] = null; + if ($state['memfdFd'] >= 0) { + qdb_close_fd($state['memfdFd']); + } + $state['memfdFd'] = -1; + if ($state['memfdPath'] !== '' && is_file($state['memfdPath'])) { + @unlink($state['memfdPath']); + } + $state['memfdPath'] = ''; +} + +/** + * @return array{signature: string, pdo: ?PDO, memfdPath: string, memfdFd: int} + */ +function &qdb_read_cache_state(): array { + static $state = [ + 'signature' => '', + 'pdo' => null, + 'memfdPath' => '', + 'memfdFd' => -1, + ]; + return $state; +} diff --git a/lib/response.php b/lib/response.php new file mode 100644 index 0000000..c6d0ffe --- /dev/null +++ b/lib/response.php @@ -0,0 +1,122 @@ + true, "data" => $data]); + exit; +} + +/** + * @param mixed $details Optional structured payload (e.g. validation error list). + */ +/** + * Send a non-JSON download response (CSV, ZIP, etc.). + * + * @param array $headers + */ +function qdb_http_download(string $body, array $headers, int $status = 200): never +{ + if (defined('QDB_TESTING')) { + throw new \Tests\Support\RawHttpResponse($body, $headers, $status); + } + http_response_code($status); + foreach ($headers as $name => $value) { + header($name . ': ' . $value); + } + echo $body; + exit; +} + +function json_error(string $code, string $message, int $status = 400, mixed $details = null): never { + if (defined('QDB_TESTING')) { + throw new \Tests\Support\JsonErrorResponse($code, $message, $status, $details); + } + if (function_exists('qdb_api_log_note_api_error')) { + qdb_api_log_note_api_error($code, $message, $status); + } + http_response_code($status); + header('Content-Type: application/json; charset=UTF-8'); + $error = ["code" => $code, "message" => $message]; + if ($details !== null) { + $error['details'] = $details; + } + echo json_encode(["ok" => false, "error" => $error]); + exit; +} + +/** Raw request body (cached; php://input is single-read). */ +function qdb_raw_request_body(): string { + if (defined('QDB_TESTING') && array_key_exists('qdb_test_request_body', $GLOBALS)) { + return (string)$GLOBALS['qdb_test_request_body']; + } + static $raw = null; + if ($raw === null) { + $read = file_get_contents('php://input'); + $raw = $read === false ? '' : $read; + } + return $raw; +} + +function read_json_body(): array { + $raw = qdb_raw_request_body(); + $data = json_decode($raw, true); + if (!is_array($data)) { + json_error('INVALID_BODY', 'Request body must be valid JSON', 400); + } + return $data; +} + +function read_encrypted_json_body(string $tokenHex): array { + $outer = read_json_body(); + if (empty($outer['encrypted'])) { + json_error('ENCRYPTION_REQUIRED', 'Sensitive requests must send an encrypted payload', 400); + } + try { + $plain = qdb_decrypt_sensitive_envelope($outer, $tokenHex); + } catch (Throwable $e) { + error_log('decrypt body failed: ' . $e->getMessage()); + json_error('DECRYPT_FAILED', 'Could not decrypt request payload', 400); + } + $data = json_decode($plain, true); + if (!is_array($data)) { + json_error('INVALID_BODY', 'Decrypted body must be valid JSON object', 400); + } + return $data; +} + +function json_success_sensitive(mixed $data, string $tokenHex, int $status = 200): never { + $plain = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); + json_success(qdb_sensitive_envelope($plain, $tokenHex), $status); +} diff --git a/lib/scoring.php b/lib/scoring.php new file mode 100644 index 0000000..9923f12 --- /dev/null +++ b/lib/scoring.php @@ -0,0 +1,784 @@ + 0, + 'little_glass' => 1, + 'moderate_glass' => 2, + 'much_glass' => 3, + 'extreme_glass' => 4, + ]; +} + +function qdb_make_score_rule_id(string $questionID, string $scopeKey, string $levelKey): string { + return substr(hash('sha256', $questionID . "\0" . $scopeKey . "\0" . $levelKey), 0, 32); +} + +/** + * @return list + */ +function qdb_get_score_rules_for_question(PDO $pdo, string $questionID): array { + $stmt = $pdo->prepare( + 'SELECT scopeKey, levelKey, points FROM question_score_rule WHERE questionID = :qid ORDER BY scopeKey, levelKey' + ); + $stmt->execute([':qid' => $questionID]); + $rows = []; + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) { + $rows[] = [ + 'scopeKey' => (string)($r['scopeKey'] ?? ''), + 'levelKey' => (string)($r['levelKey'] ?? ''), + 'points' => (int)($r['points'] ?? 0), + ]; + } + return $rows; +} + +/** + * @param list $rules + */ +function qdb_sync_question_score_rules(PDO $pdo, string $questionID, array $rules): void { + $pdo->prepare('DELETE FROM question_score_rule WHERE questionID = :qid') + ->execute([':qid' => $questionID]); + if ($rules === []) { + return; + } + $ins = $pdo->prepare( + 'INSERT INTO question_score_rule (ruleID, questionID, scopeKey, levelKey, points) + VALUES (:rid, :qid, :sk, :lk, :pts)' + ); + foreach ($rules as $rule) { + $scopeKey = trim((string)($rule['scopeKey'] ?? '')); + $levelKey = trim((string)($rule['levelKey'] ?? '')); + if ($levelKey === '') { + continue; + } + $ins->execute([ + ':rid' => qdb_make_score_rule_id($questionID, $scopeKey, $levelKey), + ':qid' => $questionID, + ':sk' => $scopeKey, + ':lk' => $levelKey, + ':pts' => (int)($rule['points'] ?? 0), + ]); + } +} + +/** + * Build nested map: scopeKey -> levelKey -> points. + * + * @return array> + */ +function qdb_score_rule_map_for_question(PDO $pdo, string $questionID): array { + $map = []; + foreach (qdb_get_score_rules_for_question($pdo, $questionID) as $rule) { + $map[$rule['scopeKey']][$rule['levelKey']] = $rule['points']; + } + return $map; +} + +/** + * Attach scoreRules to glass symptom rows for editor API. + * + * @return list}> + */ +function qdb_glass_symptoms_with_score_rules(PDO $pdo, string $questionID, array $config): array { + $ruleMap = $questionID !== '' ? qdb_score_rule_map_for_question($pdo, $questionID) : []; + $defaults = qdb_default_glass_level_points(); + $rows = []; + foreach (qdb_glass_symptoms_with_labels($pdo, $config) as $row) { + $key = $row['key']; + $levels = []; + foreach (qdb_glass_scale_level_keys() as $levelKey) { + $levels[$levelKey] = $ruleMap[$key][$levelKey] + ?? $ruleMap[''][$levelKey] + ?? $defaults[$levelKey] + ?? 0; + } + $rows[] = [ + 'key' => $key, + 'labelDe' => $row['labelDe'], + 'scoreLevels' => $levels, + ]; + } + return $rows; +} + +/** + * Parse scoreRules from question save body. + * + * @return list + */ +function qdb_parse_score_rules_body(array $body, string $questionType, array $config): array { + if (!isset($body['scoreRules']) || !is_array($body['scoreRules'])) { + if ($questionType === 'glass_scale_question' && isset($body['glassSymptoms']) && is_array($body['glassSymptoms'])) { + return qdb_score_rules_from_glass_symptoms($body['glassSymptoms']); + } + return []; + } + $rules = []; + foreach ($body['scoreRules'] as $row) { + if (!is_array($row)) { + continue; + } + $levelKey = trim((string)($row['levelKey'] ?? '')); + if ($levelKey === '') { + continue; + } + $rules[] = [ + 'scopeKey' => trim((string)($row['scopeKey'] ?? '')), + 'levelKey' => $levelKey, + 'points' => (int)($row['points'] ?? 0), + ]; + } + return $rules; +} + +/** + * @param list}> $glassSymptoms + * @return list + */ +function qdb_score_rules_from_glass_symptoms(array $glassSymptoms): array { + $rules = []; + $defaults = qdb_default_glass_level_points(); + foreach ($glassSymptoms as $row) { + if (!is_array($row)) { + continue; + } + $scopeKey = trim((string)($row['key'] ?? '')); + if ($scopeKey === '') { + continue; + } + $levels = is_array($row['scoreLevels'] ?? null) ? $row['scoreLevels'] : $defaults; + foreach (qdb_glass_scale_level_keys() as $levelKey) { + $rules[] = [ + 'scopeKey' => $scopeKey, + 'levelKey' => $levelKey, + 'points' => (int)($levels[$levelKey] ?? $defaults[$levelKey] ?? 0), + ]; + } + } + return $rules; +} + +function qdb_normalize_scoring_bands(array $row): array { + $greenMax = (int)($row['greenMax'] ?? 12); + $yellowMax = (int)($row['yellowMax'] ?? 36); + return [ + 'greenMin' => (int)($row['greenMin'] ?? 0), + 'greenMax' => $greenMax, + 'yellowMin' => (int)($row['yellowMin'] ?? ($greenMax + 1)), + 'yellowMax' => $yellowMax, + 'redMin' => (int)($row['redMin'] ?? ($yellowMax + 1)), + ]; +} + +/** + * @return string|null Error message, or null if valid. + */ +function qdb_validate_scoring_bands(array $bands): ?string { + if ($bands['greenMin'] > $bands['greenMax']) { + return 'Green min must not exceed green max'; + } + if ($bands['yellowMin'] > $bands['yellowMax']) { + return 'Yellow min must not exceed yellow max'; + } + if ($bands['greenMax'] >= $bands['yellowMin']) { + return 'Green range must end before yellow range starts'; + } + if ($bands['yellowMax'] >= $bands['redMin']) { + return 'Yellow range must end before red range starts'; + } + return null; +} + +/** + * @param array|int $bandsOrGreenMax Band map or legacy greenMax + */ +function qdb_band_for_total(float $total, array|int $bandsOrGreenMax, ?int $yellowMax = null): string { + $bands = is_array($bandsOrGreenMax) + ? qdb_normalize_scoring_bands($bandsOrGreenMax) + : qdb_normalize_scoring_bands(['greenMax' => $bandsOrGreenMax, 'yellowMax' => $yellowMax ?? 36]); + + if ($total >= $bands['greenMin'] && $total <= $bands['greenMax']) { + return 'green'; + } + if ($total >= $bands['yellowMin'] && $total <= $bands['yellowMax']) { + return 'yellow'; + } + if ($total >= $bands['redMin']) { + return 'red'; + } + if ($total < $bands['greenMin']) { + return 'green'; + } + if ($total < $bands['yellowMin']) { + return 'yellow'; + } + return 'red'; +} + +function qdb_parse_scoring_bands_body(array $body): array { + $greenMax = (int)($body['greenMax'] ?? 12); + $yellowMax = (int)($body['yellowMax'] ?? 36); + return qdb_normalize_scoring_bands([ + 'greenMin' => (int)($body['greenMin'] ?? 0), + 'greenMax' => $greenMax, + 'yellowMin' => (int)($body['yellowMin'] ?? ($greenMax + 1)), + 'yellowMax' => $yellowMax, + 'redMin' => (int)($body['redMin'] ?? ($yellowMax + 1)), + ]); +} + +function qdb_scoring_bands_to_api(array $row): array { + $bands = qdb_normalize_scoring_bands($row); + return $bands; +} + +function qdb_compute_questionnaire_score(PDO $pdo, string $clientCode, string $questionnaireID): int { + $qStmt = $pdo->prepare( + 'SELECT questionID, type, configJson FROM question WHERE questionnaireID = :qn ORDER BY orderIndex' + ); + $qStmt->execute([':qn' => $questionnaireID]); + $questions = $qStmt->fetchAll(PDO::FETCH_ASSOC); + + $aoStmt = $pdo->prepare( + 'SELECT ao.questionID, ao.defaultText, ao.points + FROM answer_option ao + JOIN question q ON q.questionID = ao.questionID + WHERE q.questionnaireID = :qn' + ); + $aoStmt->execute([':qn' => $questionnaireID]); + $optionPoints = []; + foreach ($aoStmt->fetchAll(PDO::FETCH_ASSOC) as $ao) { + $optionPoints[$ao['questionID']][$ao['defaultText']] = (int)$ao['points']; + } + + $aStmt = $pdo->prepare( + 'SELECT ca.questionID, ca.answerOptionID, ca.freeTextValue, ca.numericValue, + ao.defaultText AS optionKey + FROM client_answer ca + LEFT JOIN answer_option ao ON ao.answerOptionID = ca.answerOptionID + JOIN question q ON q.questionID = ca.questionID + WHERE ca.clientCode = :cc AND q.questionnaireID = :qn' + ); + $aStmt->execute([':cc' => $clientCode, ':qn' => $questionnaireID]); + $answersByQuestion = []; + foreach ($aStmt->fetchAll(PDO::FETCH_ASSOC) as $a) { + $answersByQuestion[$a['questionID']] = $a; + } + + $total = 0; + foreach ($questions as $qRow) { + $total += qdb_score_points_for_question( + $pdo, + $qRow, + $answersByQuestion[$qRow['questionID']] ?? null, + $optionPoints[$qRow['questionID']] ?? [] + ); + } + return $total; +} + +function qdb_score_points_for_question( + PDO $pdo, + array $questionRow, + ?array $answerRow, + array $optionPointsMap, +): int { + if ($answerRow === null) { + return 0; + } + $type = (string)($questionRow['type'] ?? ''); + $questionID = (string)($questionRow['questionID'] ?? ''); + $config = json_decode($questionRow['configJson'] ?? '{}', true) ?: []; + $ruleMap = qdb_score_rule_map_for_question($pdo, $questionID); + + switch ($type) { + case 'radio_question': + case 'string_spinner': + $key = trim((string)($answerRow['optionKey'] ?? $answerRow['freeTextValue'] ?? '')); + return $key !== '' ? (int)($optionPointsMap[$key] ?? 0) : 0; + + case 'multi_check_box_question': + $raw = trim((string)($answerRow['freeTextValue'] ?? '')); + if ($raw === '') { + return 0; + } + $sum = 0; + foreach (qdb_decode_multi_check_values($raw) as $mk) { + $sum += (int)($optionPointsMap[$mk] ?? 0); + } + return $sum; + + case 'glass_scale_question': + $json = trim((string)($answerRow['freeTextValue'] ?? '')); + if ($json === '') { + return 0; + } + $decoded = json_decode($json, true); + if (!is_array($decoded)) { + return 0; + } + $defaults = qdb_default_glass_level_points(); + $sum = 0; + foreach ($decoded as $symptomKey => $levelKey) { + $sk = trim((string)$symptomKey); + $lk = trim((string)$levelKey); + if ($sk === '' || $lk === '') { + continue; + } + $sum += (int)($ruleMap[$sk][$lk] + ?? $ruleMap[''][$lk] + ?? $defaults[$lk] + ?? 0); + } + return $sum; + + case 'slider_question': + case 'value_spinner': + $value = $answerRow['numericValue']; + if ($value === null || $value === '') { + $value = $answerRow['freeTextValue']; + } + if ($value === null || $value === '') { + return 0; + } + $levelKey = is_numeric($value) + ? (string)(int)round((float)$value) + : trim((string)$value); + return (int)($ruleMap[''][$levelKey] ?? 0); + + default: + return 0; + } +} + +function qdb_recompute_profile_scores_for_client(PDO $pdo, string $clientCode, ?string $triggerQuestionnaireID = null): void { + $profileSql = 'SELECT profileID, name, greenMin, greenMax, yellowMin, yellowMax, redMin FROM scoring_profile WHERE isActive = 1'; + $params = []; + if ($triggerQuestionnaireID !== null && $triggerQuestionnaireID !== '') { + $profileSql = ' + SELECT sp.profileID, sp.name, sp.greenMin, sp.greenMax, sp.yellowMin, sp.yellowMax, sp.redMin + FROM scoring_profile sp + JOIN scoring_profile_questionnaire spq ON spq.profileID = sp.profileID + WHERE sp.isActive = 1 AND spq.questionnaireID = :qn'; + $params[':qn'] = $triggerQuestionnaireID; + } + $pStmt = $pdo->prepare($profileSql); + $pStmt->execute($params); + $profiles = $pStmt->fetchAll(PDO::FETCH_ASSOC); + + $deleteOrphan = $pdo->prepare( + 'DELETE FROM client_scoring_profile_result WHERE clientCode = :cc AND profileID = :pid' + ); + $upsert = $pdo->prepare( + 'INSERT INTO client_scoring_profile_result + (clientCode, profileID, weightedTotal, band, computedAt, questionnaireSnapshot) + VALUES (:cc, :pid, :wt, :band, :at, :snap) + ON CONFLICT(clientCode, profileID) DO UPDATE SET + weightedTotal = excluded.weightedTotal, + band = excluded.band, + computedAt = excluded.computedAt, + questionnaireSnapshot = excluded.questionnaireSnapshot' + ); + + foreach ($profiles as $profile) { + $profileID = $profile['profileID']; + $members = qdb_scoring_profile_members($pdo, $profileID); + if ($members === []) { + continue; + } + + $snapshot = []; + $weightedTotal = 0.0; + $allComplete = true; + + foreach ($members as $member) { + $qnID = $member['questionnaireID']; + $weight = (float)$member['weight']; + $cq = $pdo->prepare( + "SELECT status, sumPoints FROM completed_questionnaire + WHERE clientCode = :cc AND questionnaireID = :qn" + ); + $cq->execute([':cc' => $clientCode, ':qn' => $qnID]); + $row = $cq->fetch(PDO::FETCH_ASSOC); + if (!$row || ($row['status'] ?? '') !== 'completed') { + $allComplete = false; + break; + } + $sumPoints = (int)($row['sumPoints'] ?? 0); + $contribution = $sumPoints * $weight; + $weightedTotal += $contribution; + $snapshot[$qnID] = [ + 'sumPoints' => $sumPoints, + 'weight' => $weight, + 'contribution' => round($contribution, 4), + ]; + } + + if (!$allComplete) { + $deleteOrphan->execute([':cc' => $clientCode, ':pid' => $profileID]); + continue; + } + + $band = qdb_band_for_total($weightedTotal, qdb_normalize_scoring_bands($profile)); + $upsert->execute([ + ':cc' => $clientCode, + ':pid' => $profileID, + ':wt' => round($weightedTotal, 4), + ':band' => $band, + ':at' => time(), + ':snap' => json_encode($snapshot, JSON_UNESCAPED_UNICODE), + ]); + } +} + +/** + * @return list + */ +function qdb_scoring_profile_members(PDO $pdo, string $profileID): array { + $stmt = $pdo->prepare( + 'SELECT spq.questionnaireID, spq.weight, spq.orderIndex, q.name + FROM scoring_profile_questionnaire spq + JOIN questionnaire q ON q.questionnaireID = spq.questionnaireID + WHERE spq.profileID = :pid + ORDER BY spq.orderIndex, spq.questionnaireID' + ); + $stmt->execute([':pid' => $profileID]); + $rows = []; + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) { + $rows[] = [ + 'questionnaireID' => $r['questionnaireID'], + 'weight' => (float)$r['weight'], + 'orderIndex' => (int)$r['orderIndex'], + 'name' => $r['name'] ?? '', + ]; + } + return $rows; +} + +/** + * @return list> + */ +function qdb_list_scoring_profiles(PDO $pdo): array { + $stmt = $pdo->query( + 'SELECT profileID, name, description, isActive, + greenMin, greenMax, yellowMin, yellowMax, redMin, + createdAt, updatedAt + FROM scoring_profile ORDER BY name' + ); + $profiles = []; + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) { + $row['isActive'] = (int)$row['isActive']; + $bands = qdb_normalize_scoring_bands($row); + $row = array_merge($row, $bands); + $row['questionnaires'] = qdb_scoring_profile_members($pdo, $row['profileID']); + $profiles[] = $row; + } + return $profiles; +} + +function qdb_get_scoring_profile(PDO $pdo, string $profileID): ?array { + $stmt = $pdo->prepare( + 'SELECT profileID, name, description, isActive, + greenMin, greenMax, yellowMin, yellowMax, redMin, + createdAt, updatedAt + FROM scoring_profile WHERE profileID = :pid' + ); + $stmt->execute([':pid' => $profileID]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + if (!$row) { + return null; + } + $row['isActive'] = (int)$row['isActive']; + $row = array_merge($row, qdb_normalize_scoring_bands($row)); + $row['questionnaires'] = qdb_scoring_profile_members($pdo, $profileID); + return $row; +} + +/** + * Backfill default glass score rules (0–4) for all glass questions missing rules. + */ +function qdb_backfill_glass_score_rules(PDO $pdo): int { + $stmt = $pdo->query("SELECT questionID, configJson FROM question WHERE type = 'glass_scale_question'"); + $count = 0; + $defaults = qdb_default_glass_level_points(); + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) { + $existing = qdb_get_score_rules_for_question($pdo, $row['questionID']); + if ($existing !== []) { + continue; + } + $config = json_decode($row['configJson'] ?? '{}', true) ?: []; + $rules = []; + foreach ($config['symptoms'] ?? [] as $symptomKey) { + $sk = trim((string)$symptomKey); + if ($sk === '') { + continue; + } + foreach ($defaults as $levelKey => $pts) { + $rules[] = ['scopeKey' => $sk, 'levelKey' => $levelKey, 'points' => $pts]; + } + } + if ($rules !== []) { + qdb_sync_question_score_rules($pdo, $row['questionID'], $rules); + $count++; + } + } + return $count; +} + +/** + * Recompute questionnaire sumPoints and scoring-profile results for specific clients. + * + * @param list $clientCodes + */ +function qdb_recompute_scores_for_clients(PDO $pdo, array $clientCodes): int { + $clientCodes = array_values(array_unique(array_filter( + $clientCodes, + static fn($cc) => is_string($cc) && trim($cc) !== '' + ))); + if ($clientCodes === []) { + return 0; + } + + $n = 0; + $qnStmt = $pdo->prepare( + "SELECT questionnaireID FROM completed_questionnaire + WHERE clientCode = :cc AND status = 'completed'" + ); + $upd = $pdo->prepare( + 'UPDATE completed_questionnaire SET sumPoints = :sp WHERE clientCode = :cc AND questionnaireID = :qn' + ); + foreach ($clientCodes as $clientCode) { + $qnStmt->execute([':cc' => $clientCode]); + foreach ($qnStmt->fetchAll(PDO::FETCH_COLUMN) as $qnID) { + $score = qdb_compute_questionnaire_score($pdo, $clientCode, (string)$qnID); + $upd->execute([ + ':sp' => $score, + ':cc' => $clientCode, + ':qn' => $qnID, + ]); + $n++; + } + qdb_recompute_profile_scores_for_client($pdo, $clientCode); + } + return $n; +} + +function qdb_recompute_all_questionnaire_scores(PDO $pdo): int { + $stmt = $pdo->query( + "SELECT DISTINCT clientCode FROM completed_questionnaire WHERE status = 'completed'" + ); + $clientCodes = $stmt->fetchAll(PDO::FETCH_COLUMN); + return qdb_recompute_scores_for_clients($pdo, $clientCodes); +} + +function qdb_seed_default_rhs_scoring_profile(PDO $pdo): ?string { + foreach (qdb_list_scoring_profiles($pdo) as $p) { + if ($p['name'] === 'RHS Index') { + return $p['profileID']; + } + } + $rhsId = 'questionnaire_2_rhs'; + $chk = $pdo->prepare('SELECT 1 FROM questionnaire WHERE questionnaireID = :id'); + $chk->execute([':id' => $rhsId]); + if (!$chk->fetch()) { + return null; + } + $profileID = bin2hex(random_bytes(16)); + $now = time(); + $pdo->prepare( + 'INSERT INTO scoring_profile (profileID, name, description, isActive, + greenMin, greenMax, yellowMin, yellowMax, redMin, createdAt, updatedAt) + VALUES (:id, :name, :desc, 1, 0, 12, 13, 36, 37, :ca, :ua)' + )->execute([ + ':id' => $profileID, + ':name' => 'RHS Index', + ':desc' => 'Legacy RHS thresholds (green 0–12, yellow 13–36, red 37+)', + ':ca' => $now, + ':ua' => $now, + ]); + $pdo->prepare( + 'INSERT INTO scoring_profile_questionnaire (profileID, questionnaireID, weight, orderIndex) + VALUES (:pid, :qn, 1.0, 0)' + )->execute([':pid' => $profileID, ':qn' => $rhsId]); + return $profileID; +} + +function qdb_is_valid_scoring_band(?string $band): bool { + return in_array($band, ['green', 'yellow', 'red'], true); +} + +/** + * Coach decision when set; otherwise the server-computed band. + */ +function qdb_effective_scoring_band(array $row): string { + $coach = trim((string)($row['coachBand'] ?? '')); + if ($coach !== '' && qdb_is_valid_scoring_band($coach)) { + return $coach; + } + return (string)($row['band'] ?? ''); +} + +function qdb_scoring_review_row_to_api(array $row, bool $includeComputed = true): array { + $coachBand = trim((string)($row['coachBand'] ?? '')); + $out = [ + 'profileID' => (string)($row['profileID'] ?? ''), + 'name' => (string)($row['name'] ?? ''), + 'coachBand' => $coachBand !== '' ? $coachBand : null, + 'pendingReview' => $coachBand === '', + ]; + if ($includeComputed) { + $out['weightedTotal'] = (float)($row['weightedTotal'] ?? 0); + $out['calculatedBand'] = (string)($row['band'] ?? ''); + $out['computedAt'] = !empty($row['computedAt']) ? date('Y-m-d H:i', (int)$row['computedAt']) : ''; + } + return $out; +} + +/** + * @param list $clientCodes + * @return list>}> + */ +function qdb_app_scoring_review_for_clients(PDO $pdo, array $clientCodes): array { + if ($clientCodes === []) { + return []; + } + $placeholders = implode(',', array_fill(0, count($clientCodes), '?')); + $stmt = $pdo->prepare( + "SELECT r.clientCode, r.profileID, r.coachBand, sp.name + FROM client_scoring_profile_result r + JOIN scoring_profile sp ON sp.profileID = r.profileID AND sp.isActive = 1 + WHERE r.clientCode IN ($placeholders) + ORDER BY r.clientCode, sp.name" + ); + $stmt->execute(array_values($clientCodes)); + $byClient = []; + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) { + $cc = (string)$row['clientCode']; + $byClient[$cc][] = qdb_scoring_review_row_to_api($row, false); + } + $out = []; + foreach ($clientCodes as $code) { + $out[] = [ + 'clientCode' => $code, + 'profiles' => $byClient[$code] ?? [], + ]; + } + return $out; +} + +/** + * @return array + */ +/** + * Store coach band; create a result row from app-local computed values when none exists yet. + * + * @return array + */ +function qdb_save_coach_scoring_review( + PDO $pdo, + string $clientCode, + string $profileID, + string $coachBand, + string $userID, + ?float $weightedTotal = null, + ?string $calculatedBand = null, +): array { + if (!qdb_is_valid_scoring_band($coachBand)) { + throw new InvalidArgumentException('coachBand must be green, yellow, or red'); + } + + $stmt = $pdo->prepare( + 'SELECT r.band, r.coachBand, r.weightedTotal, r.computedAt, sp.name + FROM client_scoring_profile_result r + JOIN scoring_profile sp ON sp.profileID = r.profileID + WHERE r.clientCode = :cc AND r.profileID = :pid' + ); + $stmt->execute([':cc' => $clientCode, ':pid' => $profileID]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$row) { + if ($weightedTotal === null || $calculatedBand === null || !qdb_is_valid_scoring_band($calculatedBand)) { + throw new RuntimeException('Scoring profile result not found; calculatedBand and weightedTotal required'); + } + $profile = qdb_get_scoring_profile($pdo, $profileID); + if (!$profile || (int)($profile['isActive'] ?? 0) !== 1) { + throw new RuntimeException('Scoring profile not found'); + } + $now = time(); + $pdo->prepare( + 'INSERT INTO client_scoring_profile_result + (clientCode, profileID, weightedTotal, band, computedAt, questionnaireSnapshot, + coachBand, coachReviewedAt, coachReviewedByUserID) + VALUES (:cc, :pid, :wt, :band, :at, :snap, :cb, :rat, :uid)' + )->execute([ + ':cc' => $clientCode, + ':pid' => $profileID, + ':wt' => round($weightedTotal, 4), + ':band' => $calculatedBand, + ':at' => $now, + ':snap' => '{}', + ':cb' => $coachBand, + ':rat' => $now, + ':uid' => $userID, + ]); + return qdb_scoring_review_row_to_api([ + 'profileID' => $profileID, + 'name' => $profile['name'], + 'weightedTotal' => $weightedTotal, + 'band' => $calculatedBand, + 'coachBand' => $coachBand, + 'computedAt' => $now, + ]); + } + + return qdb_set_coach_scoring_band($pdo, $clientCode, $profileID, $coachBand, $userID); +} + +function qdb_set_coach_scoring_band( + PDO $pdo, + string $clientCode, + string $profileID, + string $coachBand, + string $userID +): array { + if (!qdb_is_valid_scoring_band($coachBand)) { + throw new InvalidArgumentException('coachBand must be green, yellow, or red'); + } + $stmt = $pdo->prepare( + 'SELECT r.band, r.coachBand, r.weightedTotal, r.computedAt, sp.name + FROM client_scoring_profile_result r + JOIN scoring_profile sp ON sp.profileID = r.profileID + WHERE r.clientCode = :cc AND r.profileID = :pid' + ); + $stmt->execute([':cc' => $clientCode, ':pid' => $profileID]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + if (!$row) { + throw new RuntimeException('Scoring profile result not found or profile incomplete'); + } + $now = time(); + $pdo->prepare( + 'UPDATE client_scoring_profile_result + SET coachBand = :cb, coachReviewedAt = :at, coachReviewedByUserID = :uid + WHERE clientCode = :cc AND profileID = :pid' + )->execute([ + ':cb' => $coachBand, + ':at' => $now, + ':uid' => $userID, + ':cc' => $clientCode, + ':pid' => $profileID, + ]); + $row['coachBand'] = $coachBand; + $row['profileID'] = $profileID; + return qdb_scoring_review_row_to_api($row); +} diff --git a/lib/settings.php b/lib/settings.php new file mode 100644 index 0000000..6d644d6 --- /dev/null +++ b/lib/settings.php @@ -0,0 +1,114 @@ + 10, + 'login_window_seconds' => 900, + 'login_lockout_seconds' => 900, + 'session_ttl_seconds' => 30 * 24 * 60 * 60, + 'temp_session_ttl_seconds' => 10 * 60, + ]; +} + +function qdb_settings_keys(): array +{ + return array_keys(qdb_settings_defaults()); +} + +function qdb_ensure_system_setting_table(PDO $pdo): void +{ + if (!qdb_table_exists($pdo, 'system_setting')) { + $pdo->exec(" + CREATE TABLE system_setting ( + settingKey TEXT NOT NULL PRIMARY KEY, + settingValue TEXT NOT NULL DEFAULT '' + ) + "); + } +} + +function qdb_settings_get_on_pdo(PDO $pdo): array +{ + qdb_ensure_system_setting_table($pdo); + $out = qdb_settings_defaults(); + $rows = $pdo->query('SELECT settingKey, settingValue FROM system_setting')->fetchAll(PDO::FETCH_ASSOC); + foreach ($rows as $row) { + $key = (string)($row['settingKey'] ?? ''); + if ($key === '' || !array_key_exists($key, $out)) { + continue; + } + $out[$key] = (int)$row['settingValue']; + } + return $out; +} + +function qdb_settings_get(): array +{ + require_once __DIR__ . '/../db_init.php'; + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + try { + return qdb_settings_get_on_pdo($pdo); + } finally { + qdb_discard($tmpDb, $lockFp); + } +} + +/** + * @param array $partial + * @param array|null $base existing settings; defaults used when null + * @return array saved settings + */ +function qdb_settings_validate_and_merge(array $partial, ?array $base = null): array +{ + $merged = $base ?? qdb_settings_defaults(); + $limits = [ + 'login_max_attempts' => [1, 100], + 'login_window_seconds' => [60, 86400], + 'login_lockout_seconds' => [60, 86400], + 'session_ttl_seconds' => [3600, 365 * 24 * 60 * 60], + 'temp_session_ttl_seconds' => [300, 24 * 60 * 60], + ]; + foreach (qdb_settings_keys() as $key) { + if (!array_key_exists($key, $partial)) { + continue; + } + $val = (int)$partial[$key]; + [$min, $max] = $limits[$key]; + if ($val < $min || $val > $max) { + throw new InvalidArgumentException( + "$key must be between $min and $max" + ); + } + $merged[$key] = $val; + } + return $merged; +} + +/** + * @param array $settings full merged settings to persist + */ +function qdb_settings_save_on_pdo(PDO $pdo, array $settings): void +{ + qdb_ensure_system_setting_table($pdo); + $stmt = $pdo->prepare( + 'INSERT INTO system_setting (settingKey, settingValue) + VALUES (:k, :v) + ON CONFLICT(settingKey) DO UPDATE SET settingValue = excluded.settingValue' + ); + foreach (qdb_settings_keys() as $key) { + $stmt->execute([':k' => $key, ':v' => (string)(int)($settings[$key] ?? 0)]); + } +} + +function qdb_session_ttl_seconds(?array $settings = null, bool $temp = false): int +{ + $settings ??= qdb_settings_get(); + return $temp + ? (int)$settings['temp_session_ttl_seconds'] + : (int)$settings['session_ttl_seconds']; +} diff --git a/lib/submissions.php b/lib/submissions.php new file mode 100644 index 0000000..49ddd08 --- /dev/null +++ b/lib/submissions.php @@ -0,0 +1,999 @@ +prepare( + 'SELECT questionID, defaultText, type, orderIndex, configJson FROM question WHERE questionID = :id' + ); + $qStmt->execute([':id' => $fullId]); + $qRow = $qStmt->fetch(PDO::FETCH_ASSOC); + if (!$qRow) { + $symptoms = $qEntry['symptoms'] ?? []; + $qRow = [ + 'questionID' => $fullId, + 'defaultText' => (string)($qEntry['questionKey'] ?? $qEntry['shortId'] ?? $fullId), + 'type' => (string)($qEntry['type'] ?? ''), + 'orderIndex' => 0, + 'configJson' => json_encode( + ['symptoms' => is_array($symptoms) ? $symptoms : []], + JSON_UNESCAPED_UNICODE + ), + ]; + } + $questions[] = $qRow; + + $cfg = json_decode($qRow['configJson'] ?? '{}', true) ?: []; + $type = $qRow['type'] ?? ''; + if ($type === 'glass_scale_question') { + $symptoms = $cfg['symptoms'] ?? $qEntry['symptoms'] ?? []; + if (is_array($symptoms) && $symptoms !== []) { + foreach ($symptoms as $symptomKey) { + $sk = trim((string)$symptomKey); + if ($sk === '') { + continue; + } + $resultColumns[] = [ + 'header' => qdb_string_german_label($pdo, $sk, $stringLabelCache), + 'questionID' => $fullId, + 'symptomKey' => $sk, + 'kind' => 'glass_symptom', + 'question' => $qRow, + ]; + } + continue; + } + } + + $resultColumns[] = [ + 'header' => qdb_question_german_label($pdo, $qRow), + 'questionID' => $fullId, + 'symptomKey' => null, + 'kind' => 'question', + 'question' => $qRow, + ]; + + $aoStmt = $pdo->prepare('SELECT answerOptionID, defaultText FROM answer_option WHERE questionID = :qid'); + $aoStmt->execute([':qid' => $fullId]); + $dbOptions = $aoStmt->fetchAll(PDO::FETCH_ASSOC); + if ($dbOptions !== []) { + foreach ($dbOptions as $ao) { + $optionTextMap[$ao['answerOptionID']] = qdb_option_german_label( + $pdo, + $ao['answerOptionID'], + $ao['defaultText'] + ); + } + } else { + foreach ($qEntry['options'] ?? [] as $opt) { + if (!is_array($opt)) { + continue; + } + $key = (string)($opt['key'] ?? ''); + if ($key !== '') { + $optionTextMap[$key] = $key; + } + } + } + } + + return [ + 'questions' => $questions, + 'resultColumns' => $resultColumns, + 'optionTextMap' => $optionTextMap, + 'stringLabelCache' => $stringLabelCache, + ]; +} + +function qdb_next_submission_version(PDO $pdo, string $clientCode, string $questionnaireID): int { + $stmt = $pdo->prepare( + 'SELECT COALESCE(MAX(version), 0) + 1 FROM questionnaire_submission + WHERE clientCode = :cc AND questionnaireID = :qn' + ); + $stmt->execute([':cc' => $clientCode, ':qn' => $questionnaireID]); + return (int)$stmt->fetchColumn(); +} + +/** + * Snapshot live answers + completion metadata after a successful submit. + */ +function qdb_record_submission_after_submit( + PDO $pdo, + string $clientCode, + string $questionnaireID, + array $tokenRec, + ?int $startedAt, + ?int $completedAt, + int $sumPoints, + string $assignedByCoach, + int $structureRevision = 1, + ?array $structureManifest = null, + ?array $questionIdsToCopy = null, + ?int $submittedAt = null +): string { + $version = qdb_next_submission_version($pdo, $clientCode, $questionnaireID); + $submissionID = bin2hex(random_bytes(16)); + $submittedAtValue = $submittedAt ?? $completedAt ?? time(); + $snapshotJson = '{}'; + if ($structureManifest !== null) { + $encoded = json_encode($structureManifest, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + if ($encoded !== false) { + $snapshotJson = $encoded; + } + } + + $cq = $pdo->prepare( + 'SELECT status FROM completed_questionnaire WHERE clientCode = :cc AND questionnaireID = :qn' + ); + $cq->execute([':cc' => $clientCode, ':qn' => $questionnaireID]); + $status = $cq->fetchColumn() ?: 'completed'; + + $pdo->prepare( + 'INSERT INTO questionnaire_submission ( + submissionID, clientCode, questionnaireID, version, submittedAt, + submittedByUserID, submittedByRole, assignedByCoach, + status, startedAt, completedAt, sumPoints, + structureRevision, structureSnapshotJson + ) VALUES ( + :sid, :cc, :qn, :ver, :sat, + :uid, :role, :abc, + :st, :sa, :ca, :sp, + :srev, :snap + )' + )->execute([ + ':sid' => $submissionID, + ':cc' => $clientCode, + ':qn' => $questionnaireID, + ':ver' => $version, + ':sat' => $submittedAtValue, + ':uid' => $tokenRec['userID'] ?? '', + ':role' => $tokenRec['role'] ?? '', + ':abc' => $assignedByCoach !== '' ? $assignedByCoach : null, + ':st' => $status, + ':sa' => $startedAt, + ':ca' => $completedAt, + ':sp' => $sumPoints, + ':srev' => max(1, $structureRevision), + ':snap' => $snapshotJson, + ]); + + if ($questionIdsToCopy !== null && $questionIdsToCopy !== []) { + $questionIdsToCopy = array_values(array_unique(array_filter($questionIdsToCopy, 'is_string'))); + $ph = implode(',', array_fill(0, count($questionIdsToCopy), '?')); + $copy = $pdo->prepare( + "INSERT INTO client_answer_submission (submissionID, questionID, answerOptionID, freeTextValue, numericValue, answeredAt) + SELECT ?, questionID, answerOptionID, freeTextValue, numericValue, answeredAt + FROM client_answer + WHERE clientCode = ? AND questionID IN ($ph)" + ); + $copy->execute(array_merge([$submissionID, $clientCode], $questionIdsToCopy)); + } else { + $copy = $pdo->prepare( + 'INSERT INTO client_answer_submission (submissionID, questionID, answerOptionID, freeTextValue, numericValue, answeredAt) + SELECT :sid, questionID, answerOptionID, freeTextValue, numericValue, answeredAt + FROM client_answer + WHERE clientCode = :cc AND questionID IN ( + SELECT questionID FROM question WHERE questionnaireID = :qn + )' + ); + $copy->execute([':sid' => $submissionID, ':cc' => $clientCode, ':qn' => $questionnaireID]); + } + + return $submissionID; +} + +/** + * One-time: create version 1 submissions for existing completions without archive rows. + */ +function qdb_backfill_submissions_from_live(PDO $pdo): bool { + $count = (int)$pdo->query('SELECT COUNT(*) FROM questionnaire_submission')->fetchColumn(); + if ($count > 0) { + return false; + } + + $rows = $pdo->query( + 'SELECT clientCode, questionnaireID, assignedByCoach, status, startedAt, completedAt, sumPoints + FROM completed_questionnaire' + )->fetchAll(PDO::FETCH_ASSOC); + + if (!$rows) { + return false; + } + + $pdo->beginTransaction(); + try { + foreach ($rows as $cq) { + $clientCode = $cq['clientCode']; + $qnID = $cq['questionnaireID']; + $chk = $pdo->prepare( + 'SELECT 1 FROM client_answer ca + JOIN question q ON q.questionID = ca.questionID + WHERE ca.clientCode = :cc AND q.questionnaireID = :qn LIMIT 1' + ); + $chk->execute([':cc' => $clientCode, ':qn' => $qnID]); + if (!$chk->fetchColumn()) { + continue; + } + + $submissionID = bin2hex(random_bytes(16)); + $completedAt = $cq['completedAt'] ?? time(); + $pdo->prepare( + 'INSERT INTO questionnaire_submission ( + submissionID, clientCode, questionnaireID, version, submittedAt, + submittedByUserID, submittedByRole, assignedByCoach, + status, startedAt, completedAt, sumPoints + ) VALUES ( + :sid, :cc, :qn, 1, :sat, + \'\', \'\', :abc, + :st, :sa, :ca, :sp + )' + )->execute([ + ':sid' => $submissionID, + ':cc' => $clientCode, + ':qn' => $qnID, + ':sat' => $completedAt, + ':abc' => $cq['assignedByCoach'], + ':st' => $cq['status'] ?: 'completed', + ':sa' => $cq['startedAt'], + ':ca' => $cq['completedAt'], + ':sp' => $cq['sumPoints'], + ]); + + $pdo->prepare( + 'INSERT INTO client_answer_submission (submissionID, questionID, answerOptionID, freeTextValue, numericValue, answeredAt) + SELECT :sid, questionID, answerOptionID, freeTextValue, numericValue, answeredAt + FROM client_answer + WHERE clientCode = :cc AND questionID IN ( + SELECT questionID FROM question WHERE questionnaireID = :qn + )' + )->execute([':sid' => $submissionID, ':cc' => $clientCode, ':qn' => $qnID]); + } + $pdo->commit(); + return true; + } catch (Throwable $e) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + throw $e; + } +} + +/** + * Build CSV rows for all submission versions of one questionnaire (RBAC-scoped). + * + * @return list> + */ +function qdb_export_all_versions_rows( + PDO $pdo, + string $questionnaireID, + array $questions, + array $resultColumns, + array $optionTextMap, + array $tokenRec +): array { + [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl'); + + $sql = " + SELECT qs.submissionID, qs.version, qs.submittedAt, qs.submittedByUserID, qs.submittedByRole, + qs.completedAt AS submissionCompletedAt, qs.sumPoints AS submissionSumPoints, + qs.startedAt AS submissionStartedAt, qs.status AS submissionStatus, + qs.structureRevision, qs.structureSnapshotJson, + cl.clientCode, cl.coachID, + co.username AS coachUsername, + sv.username AS supervisorUsername + FROM questionnaire_submission qs + INNER JOIN client cl ON cl.clientCode = qs.clientCode + LEFT JOIN coach co ON co.coachID = cl.coachID + LEFT JOIN supervisor sv ON sv.supervisorID = co.supervisorID + WHERE qs.questionnaireID = :qnid AND ($rbacClause) + ORDER BY cl.clientCode ASC, qs.version ASC + "; + $params = array_merge([':qnid' => $questionnaireID], $rbacParams); + $stmt = $pdo->prepare($sql); + $stmt->execute($params); + $submissions = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $defaultQuestionIDs = array_column($questions, 'questionID'); + + $userMap = []; + $userStmt = $pdo->query('SELECT userID, username FROM users'); + foreach ($userStmt->fetchAll(PDO::FETCH_ASSOC) as $u) { + $userMap[$u['userID']] = $u['username']; + } + + $rows = []; + foreach ($submissions as $s) { + $snap = json_decode($s['structureSnapshotJson'] ?? '{}', true) ?: []; + if (!empty($snap['questions'])) { + $snapCtx = qdb_display_context_from_manifest($pdo, $snap); + $submissionColumns = $snapCtx['resultColumns']; + $submissionOptionMap = $snapCtx['optionTextMap']; + $submissionQuestionIDs = array_column($snapCtx['questions'], 'questionID'); + } else { + $submissionColumns = $resultColumns; + $submissionOptionMap = $optionTextMap; + $submissionQuestionIDs = $defaultQuestionIDs; + } + + $row = [ + 'submissionID' => $s['submissionID'], + 'version' => (string)(int)$s['version'], + 'structureRevision' => (string)max(1, (int)($s['structureRevision'] ?? 1)), + 'submittedAt' => $s['submittedAt'] ? date('Y-m-d H:i', (int)$s['submittedAt']) : '', + 'submittedByRole' => $s['submittedByRole'] ?? '', + 'submittedBy' => $userMap[$s['submittedByUserID'] ?? ''] ?? ($s['submittedByUserID'] ?? ''), + 'clientCode' => $s['clientCode'], + 'coach' => $s['coachUsername'] ?? $s['coachID'], + 'supervisor' => $s['supervisorUsername'] ?? '', + 'status' => $s['submissionStatus'] ?? '', + 'sumPoints' => $s['submissionSumPoints'] ?? '', + 'startedAt' => $s['submissionStartedAt'] ? date('Y-m-d H:i', (int)$s['submissionStartedAt']) : '', + 'completedAt' => $s['submissionCompletedAt'] ? date('Y-m-d H:i', (int)$s['submissionCompletedAt']) : '', + ]; + + $answerMap = qdb_load_client_answer_map( + $pdo, + $s['clientCode'], + $submissionQuestionIDs, + 'client_answer_submission', + $s['submissionID'] + ); + + foreach ($submissionColumns as $col) { + $qid = $col['questionID']; + $a = $answerMap[$qid] ?? null; + $row[$col['header']] = qdb_results_column_cell_value($col, $a, $submissionOptionMap); + } + $rows[] = $row; + } + + return $rows; +} + +/** + * @return array{questions: array, resultColumns: array, optionTextMap: array} + */ +function qdb_export_questionnaire_context(PDO $pdo, string $questionnaireID): array { + $qStmt = $pdo->prepare( + 'SELECT questionID, defaultText, type, orderIndex, configJson FROM question WHERE questionnaireID = :id ORDER BY orderIndex' + ); + $qStmt->execute([':id' => $questionnaireID]); + $questions = $qStmt->fetchAll(PDO::FETCH_ASSOC); + $questionIDs = array_column($questions, 'questionID'); + $resultColumns = qdb_results_export_columns($questions, $questionnaireID); + + $optionTextMap = []; + foreach ($questionIDs as $qid) { + $aoStmt = $pdo->prepare('SELECT answerOptionID, defaultText FROM answer_option WHERE questionID = :qid'); + $aoStmt->execute([':qid' => $qid]); + foreach ($aoStmt->fetchAll(PDO::FETCH_ASSOC) as $ao) { + $optionTextMap[$ao['answerOptionID']] = $ao['defaultText']; + } + } + + return [ + 'questions' => $questions, + 'resultColumns' => $resultColumns, + 'optionTextMap' => $optionTextMap, + ]; +} + +/** + * Current (live) response rows for one questionnaire, RBAC-scoped. + * + * @return list> + */ +function qdb_export_current_rows( + PDO $pdo, + string $questionnaireID, + array $questions, + array $resultColumns, + array $optionTextMap, + array $tokenRec +): array { + [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl'); + $questionIDs = array_column($questions, 'questionID'); + + $sql = " + SELECT cl.clientCode, cl.coachID, + co.username AS coachUsername, + sv.username AS supervisorUsername, + cq.status, cq.sumPoints, cq.startedAt, cq.completedAt + FROM client cl + INNER JOIN completed_questionnaire cq ON cq.clientCode = cl.clientCode AND cq.questionnaireID = :qnid + LEFT JOIN coach co ON co.coachID = cl.coachID + LEFT JOIN supervisor sv ON sv.supervisorID = co.supervisorID + WHERE $rbacClause + ORDER BY cl.clientCode + "; + $params = array_merge([':qnid' => $questionnaireID], $rbacParams); + $cStmt = $pdo->prepare($sql); + $cStmt->execute($params); + $clients = $cStmt->fetchAll(PDO::FETCH_ASSOC); + + $qPlaceholders = !empty($questionIDs) ? implode(',', array_fill(0, count($questionIDs), '?')) : "'__none__'"; + $answerStmt = $pdo->prepare(" + SELECT questionID, answerOptionID, freeTextValue, numericValue + FROM client_answer + WHERE clientCode = ? AND questionID IN ($qPlaceholders) + "); + + $rows = []; + foreach ($clients as $c) { + $row = [ + 'clientCode' => $c['clientCode'], + 'coach' => $c['coachUsername'] ?? $c['coachID'], + 'supervisor' => $c['supervisorUsername'] ?? '', + 'status' => $c['status'], + 'sumPoints' => $c['sumPoints'], + 'startedAt' => $c['startedAt'] ? date('Y-m-d H:i', (int)$c['startedAt']) : '', + 'completedAt' => $c['completedAt'] ? date('Y-m-d H:i', (int)$c['completedAt']) : '', + ]; + + $bindParams = array_merge([$c['clientCode']], $questionIDs); + $answerStmt->execute($bindParams); + $answerMap = []; + foreach ($answerStmt->fetchAll(PDO::FETCH_ASSOC) as $a) { + $answerMap[$a['questionID']] = $a; + } + + foreach ($resultColumns as $col) { + $qid = $col['questionID']; + $a = $answerMap[$qid] ?? null; + $row[$col['header']] = qdb_results_column_cell_value($col, $a, $optionTextMap); + } + $rows[] = $row; + } + + return $rows; +} + +function qdb_export_safe_basename(string $name): string { + $safe = preg_replace('/[^a-zA-Z0-9_-]+/', '_', $name); + return $safe !== '' ? $safe : 'questionnaire'; +} + +/** @param array $used */ +function qdb_export_unique_zip_name(array &$used, string $base): string { + $name = $base; + $n = 2; + while (isset($used[$name])) { + $name = preg_replace('/(\.csv)$/i', "_{$n}$1", $base, 1, $count); + if (!$count) { + $name = $base . '_' . $n; + } + $n++; + } + $used[$name] = true; + return $name; +} + +/** + * @param list> $rows + * @param list $fallbackHeader + */ +function qdb_export_rows_to_csv_string(array $rows, array $fallbackHeader = []): string { + $fp = fopen('php://temp', 'r+'); + if ($fp === false) { + return ''; + } + fwrite($fp, "\xEF\xBB\xBF"); + if (!empty($rows)) { + fputcsv($fp, array_keys($rows[0]), ',', '"', '\\'); + foreach ($rows as $row) { + fputcsv($fp, array_values($row), ',', '"', '\\'); + } + } elseif ($fallbackHeader !== []) { + fputcsv($fp, $fallbackHeader, ',', '"', '\\'); + } + rewind($fp); + $csv = stream_get_contents($fp); + fclose($fp); + return $csv !== false ? $csv : ''; +} + +function qdb_export_current_csv_fallback_header(array $resultColumns): array { + $header = ['clientCode', 'coach', 'supervisor', 'status', 'sumPoints', 'startedAt', 'completedAt']; + foreach ($resultColumns as $col) { + $header[] = $col['header']; + } + return $header; +} + +function qdb_export_all_versions_csv_fallback_header(array $resultColumns): array { + $header = [ + 'submissionID', 'version', 'submittedAt', 'submittedByRole', 'submittedBy', + 'clientCode', 'coach', 'supervisor', 'status', 'sumPoints', 'startedAt', 'completedAt', + ]; + foreach ($resultColumns as $col) { + $header[] = $col['header']; + } + return $header; +} + +/** + * Build a ZIP of CSV exports for every questionnaire. Returns path to a temp file (caller must unlink). + */ +function qdb_build_server_export_zip(PDO $pdo, array $tokenRec, bool $allVersions): string { + if (!class_exists('ZipArchive')) { + json_error('SERVER_ERROR', 'ZIP export is not available on this server', 500); + } + + $qnRows = $pdo->query( + 'SELECT questionnaireID, name, version FROM questionnaire ORDER BY orderIndex, name' + )->fetchAll(PDO::FETCH_ASSOC); + + $tmpZip = tempnam(sys_get_temp_dir(), 'qdb_export_'); + if ($tmpZip === false) { + json_error('SERVER_ERROR', 'Could not create export file', 500); + } + + $zip = new ZipArchive(); + if ($zip->open($tmpZip, ZipArchive::OVERWRITE) !== true) { + @unlink($tmpZip); + json_error('SERVER_ERROR', 'Could not create ZIP archive', 500); + } + + $usedNames = []; + foreach ($qnRows as $qn) { + $qnID = $qn['questionnaireID']; + $ctx = qdb_export_questionnaire_context($pdo, $qnID); + $questions = $ctx['questions']; + $resultColumns = $ctx['resultColumns']; + $optionTextMap = $ctx['optionTextMap']; + + if ($allVersions) { + $rows = qdb_export_all_versions_rows($pdo, $qnID, $questions, $resultColumns, $optionTextMap, $tokenRec); + $fallback = qdb_export_all_versions_csv_fallback_header($resultColumns); + $base = qdb_export_safe_basename($qn['name']) . '_all_versions.csv'; + } else { + $rows = qdb_export_current_rows($pdo, $qnID, $questions, $resultColumns, $optionTextMap, $tokenRec); + $fallback = qdb_export_current_csv_fallback_header($resultColumns); + $ver = preg_replace('/[^a-zA-Z0-9_.-]+/', '_', (string)($qn['version'] ?? '')); + $base = qdb_export_safe_basename($qn['name']) . ($ver !== '' ? "_v{$ver}" : '') . '.csv'; + } + + $entryName = qdb_export_unique_zip_name($usedNames, $base); + $csv = qdb_export_rows_to_csv_string($rows, $fallback); + $zip->addFromString($entryName, $csv); + } + + $readme = $allVersions + ? "NAT-AS Ressourcenbarometer — all questionnaire response exports (every upload version).\nOne CSV per questionnaire.\n" + : "NAT-AS Ressourcenbarometer — all questionnaire response exports (current data).\nOne CSV per questionnaire.\n"; + $zip->addFromString('README.txt', $readme); + $zip->close(); + + return $tmpZip; +} + +/** + * Delete uploads, answers, and completions for client codes (does not delete client rows). + * + * @param list $clientCodes + * @return array{submissions: int, followup_notes: int, client_answers: int, completed_questionnaires: int} + */ +function qdb_delete_client_response_data(PDO $pdo, array $clientCodes): array { + $clientCodes = array_values(array_unique(array_filter( + $clientCodes, + static fn($c) => is_string($c) && $c !== '' + ))); + $deleted = [ + 'submissions' => 0, + 'followup_notes' => 0, + 'client_answers' => 0, + 'completed_questionnaires' => 0, + ]; + if ($clientCodes === []) { + return $deleted; + } + + foreach (array_chunk($clientCodes, 200) as $chunk) { + $ph = implode(',', array_fill(0, count($chunk), '?')); + if (qdb_table_exists($pdo, 'questionnaire_submission')) { + $stmt = $pdo->prepare("DELETE FROM questionnaire_submission WHERE clientCode IN ($ph)"); + $stmt->execute($chunk); + $deleted['submissions'] += $stmt->rowCount(); + } + if (qdb_table_exists($pdo, 'client_followup_note')) { + $stmt = $pdo->prepare("DELETE FROM client_followup_note WHERE clientCode IN ($ph)"); + $stmt->execute($chunk); + $deleted['followup_notes'] += $stmt->rowCount(); + } + $stmt = $pdo->prepare("DELETE FROM client_answer WHERE clientCode IN ($ph)"); + $stmt->execute($chunk); + $deleted['client_answers'] += $stmt->rowCount(); + $stmt = $pdo->prepare("DELETE FROM completed_questionnaire WHERE clientCode IN ($ph)"); + $stmt->execute($chunk); + $deleted['completed_questionnaires'] += $stmt->rowCount(); + } + + return $deleted; +} + +/** + * Remove submission rows assigned to coaches (FK before coach delete). + * + * @param list $coachIDs + */ +function qdb_delete_submissions_for_coaches(PDO $pdo, array $coachIDs): int { + if (!qdb_table_exists($pdo, 'questionnaire_submission')) { + return 0; + } + $coachIDs = array_values(array_unique(array_filter( + $coachIDs, + static fn($id) => is_string($id) && $id !== '' + ))); + if ($coachIDs === []) { + return 0; + } + $total = 0; + foreach (array_chunk($coachIDs, 200) as $chunk) { + $ph = implode(',', array_fill(0, count($chunk), '?')); + $stmt = $pdo->prepare("DELETE FROM questionnaire_submission WHERE assignedByCoach IN ($ph)"); + $stmt->execute($chunk); + $total += $stmt->rowCount(); + } + return $total; +} + +/** + * @param array> $answerMap questionID => answer row + * @param array>|null $compareMap live answers to diff against + * @return list + */ +function qdb_answers_display_rows( + PDO $pdo, + array $resultColumns, + array $answerMap, + array $optionTextMap, + array &$stringLabelCache, + ?array $compareMap = null +): array { + $rows = []; + foreach ($resultColumns as $col) { + $qid = $col['questionID']; + $a = $answerMap[$qid] ?? null; + $value = qdb_display_column_cell_value($pdo, $col, $a, $optionTextMap, $stringLabelCache); + $changedFromLive = false; + if ($compareMap !== null) { + $liveValue = qdb_display_column_cell_value( + $pdo, + $col, + $compareMap[$qid] ?? null, + $optionTextMap, + $stringLabelCache + ); + $changedFromLive = $value !== $liveValue; + } + $rows[] = [ + 'label' => $col['header'], + 'value' => $value, + 'changedFromLive' => $changedFromLive, + ]; + } + return $rows; +} + +/** + * @param list $questionIDs + * @return array> + */ +function qdb_load_client_answer_map( + PDO $pdo, + string $clientCode, + array $questionIDs, + string $table = 'client_answer', + ?string $submissionID = null +): array { + if ($questionIDs === []) { + return []; + } + $allowed = ['client_answer', 'client_answer_submission']; + if (!in_array($table, $allowed, true)) { + return []; + } + + $ph = implode(',', array_fill(0, count($questionIDs), '?')); + if ($table === 'client_answer_submission') { + if ($submissionID === null || $submissionID === '') { + return []; + } + $sql = "SELECT questionID, answerOptionID, freeTextValue, numericValue + FROM client_answer_submission + WHERE submissionID = ? AND questionID IN ($ph)"; + $params = array_merge([$submissionID], $questionIDs); + } else { + $sql = "SELECT questionID, answerOptionID, freeTextValue, numericValue + FROM client_answer + WHERE clientCode = ? AND questionID IN ($ph)"; + $params = array_merge([$clientCode], $questionIDs); + } + + $stmt = $pdo->prepare($sql); + $stmt->execute($params); + $map = []; + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) { + $map[$row['questionID']] = $row; + } + return $map; +} + +/** + * Full client response detail for the web clients list (RBAC-scoped). + */ +function qdb_client_detail(PDO $pdo, array $tokenRec, string $clientCode): array { + $clientCode = trim($clientCode); + if ($clientCode === '') { + json_error('INVALID_FIELD', 'clientCode is required', 400); + } + + [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl', 'all'); + $stmt = $pdo->prepare( + "SELECT cl.clientCode, cl.coachID, cl.archived, cl.archivedAt, + co.username AS coachUsername, + sv.username AS supervisorUsername + FROM client cl + LEFT JOIN coach co ON co.coachID = cl.coachID + LEFT JOIN supervisor sv ON sv.supervisorID = co.supervisorID + WHERE cl.clientCode = :cc AND $rbacClause" + ); + $stmt->execute(array_merge([':cc' => $clientCode], $rbacParams)); + $client = $stmt->fetch(PDO::FETCH_ASSOC); + if (!$client) { + json_error('NOT_FOUND', 'Client not found', 404); + } + + $qnStmt = $pdo->prepare( + "SELECT q.questionnaireID, q.name, q.version AS questionnaireVersion, q.orderIndex + FROM questionnaire q + WHERE q.questionnaireID IN ( + SELECT questionnaireID FROM completed_questionnaire WHERE clientCode = :cc + UNION + SELECT questionnaireID FROM questionnaire_submission WHERE clientCode = :cc2 + ) + ORDER BY q.orderIndex, q.name" + ); + $qnStmt->execute([':cc' => $clientCode, ':cc2' => $clientCode]); + $qnRows = $qnStmt->fetchAll(PDO::FETCH_ASSOC); + + $questionnaires = []; + foreach ($qnRows as $qn) { + $qnID = $qn['questionnaireID']; + $ctx = qdb_display_questionnaire_context($pdo, $qnID); + $questionIDs = array_column($ctx['questions'], 'questionID'); + $resultColumns = $ctx['resultColumns']; + $optionTextMap = $ctx['optionTextMap']; + $stringLabelCache = $ctx['stringLabelCache']; + + $cqStmt = $pdo->prepare( + 'SELECT status, sumPoints, startedAt, completedAt + FROM completed_questionnaire + WHERE clientCode = :cc AND questionnaireID = :qn' + ); + $cqStmt->execute([':cc' => $clientCode, ':qn' => $qnID]); + $cq = $cqStmt->fetch(PDO::FETCH_ASSOC) ?: []; + + $liveMap = qdb_load_client_answer_map($pdo, $clientCode, $questionIDs); + $currentAnswers = qdb_answers_display_rows( + $pdo, + $resultColumns, + $liveMap, + $optionTextMap, + $stringLabelCache, + null + ); + + $subStmt = $pdo->prepare( + 'SELECT submissionID, version, submittedAt, status, sumPoints, completedAt, + structureRevision, structureSnapshotJson + FROM questionnaire_submission + WHERE clientCode = :cc AND questionnaireID = :qn + ORDER BY version DESC' + ); + $subStmt->execute([':cc' => $clientCode, ':qn' => $qnID]); + $submissions = []; + foreach ($subStmt->fetchAll(PDO::FETCH_ASSOC) as $s) { + $snap = json_decode($s['structureSnapshotJson'] ?? '{}', true) ?: []; + if (!empty($snap['questions'])) { + $snapCtx = qdb_display_context_from_manifest($pdo, $snap); + $versionColumns = $snapCtx['resultColumns']; + $versionOptionMap = $snapCtx['optionTextMap']; + $versionStringCache = $snapCtx['stringLabelCache']; + $versionQuestionIDs = array_column($snapCtx['questions'], 'questionID'); + } else { + $versionColumns = $resultColumns; + $versionOptionMap = $optionTextMap; + $versionStringCache = $stringLabelCache; + $versionQuestionIDs = $questionIDs; + } + + $subMap = qdb_load_client_answer_map( + $pdo, + $clientCode, + $versionQuestionIDs, + 'client_answer_submission', + $s['submissionID'] + ); + $versionAnswers = qdb_answers_display_rows( + $pdo, + $versionColumns, + $subMap, + $versionOptionMap, + $versionStringCache, + $liveMap + ); + $changedCount = count(array_filter( + $versionAnswers, + static fn($r) => !empty($r['changedFromLive']) + )); + $submissions[] = [ + 'submissionID' => $s['submissionID'], + 'version' => (int)$s['version'], + 'structureRevision' => max(1, (int)($s['structureRevision'] ?? 1)), + 'submittedAt' => $s['submittedAt'] ? date('Y-m-d H:i', (int)$s['submittedAt']) : '', + 'status' => $s['status'] ?? '', + 'sumPoints' => $s['sumPoints'] !== null ? (int)$s['sumPoints'] : null, + 'completedAt' => $s['completedAt'] ? date('Y-m-d H:i', (int)$s['completedAt']) : '', + 'changedCount' => $changedCount, + 'answers' => $versionAnswers, + ]; + } + + $questionnaires[] = [ + 'questionnaireID' => $qnID, + 'name' => $qn['name'], + 'questionnaireVersion' => $qn['questionnaireVersion'] ?? '', + 'status' => $cq['status'] ?? '', + 'sumPoints' => isset($cq['sumPoints']) ? (int)$cq['sumPoints'] : null, + 'startedAt' => !empty($cq['startedAt']) ? date('Y-m-d H:i', (int)$cq['startedAt']) : '', + 'completedAt' => !empty($cq['completedAt']) ? date('Y-m-d H:i', (int)$cq['completedAt']) : '', + 'submissionCount' => count($submissions), + 'currentAnswers' => $currentAnswers, + 'submissions' => $submissions, + ]; + } + + return [ + 'client' => [ + 'clientCode' => $client['clientCode'], + 'coachID' => $client['coachID'] ?? '', + 'coachUsername' => $client['coachUsername'] ?? '', + 'supervisorUsername' => $client['supervisorUsername'] ?? '', + ], + 'questionnaires' => $questionnaires, + 'scoringProfiles' => qdb_client_scoring_profile_results($pdo, $clientCode), + ]; +} + +function qdb_client_scoring_profile_results(PDO $pdo, string $clientCode): array { + require_once __DIR__ . '/scoring.php'; + $stmt = $pdo->prepare( + 'SELECT r.profileID, r.weightedTotal, r.band, r.computedAt, r.questionnaireSnapshot, + r.coachBand, r.coachReviewedAt, r.coachReviewedByUserID, + sp.name, sp.greenMin, sp.greenMax, sp.yellowMin, sp.yellowMax, sp.redMin + FROM client_scoring_profile_result r + JOIN scoring_profile sp ON sp.profileID = r.profileID + WHERE r.clientCode = :cc + ORDER BY sp.name' + ); + $stmt->execute([':cc' => $clientCode]); + $out = []; + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) { + $bands = qdb_normalize_scoring_bands($row); + $coachBand = trim((string)($row['coachBand'] ?? '')); + $out[] = [ + 'profileID' => $row['profileID'], + 'name' => $row['name'], + 'weightedTotal' => (float)$row['weightedTotal'], + 'band' => $row['band'], + 'calculatedBand' => $row['band'], + 'coachBand' => $coachBand !== '' ? $coachBand : null, + 'effectiveBand' => qdb_effective_scoring_band($row), + 'pendingReview' => $coachBand === '', + 'greenMin' => $bands['greenMin'], + 'greenMax' => $bands['greenMax'], + 'yellowMin' => $bands['yellowMin'], + 'yellowMax' => $bands['yellowMax'], + 'redMin' => $bands['redMin'], + 'computedAt' => $row['computedAt'] ? date('Y-m-d H:i', (int)$row['computedAt']) : '', + 'coachReviewedAt' => !empty($row['coachReviewedAt']) + ? date('Y-m-d H:i', (int)$row['coachReviewedAt']) : '', + 'snapshot' => json_decode($row['questionnaireSnapshot'] ?? '{}', true) ?: [], + ]; + } + return $out; +} + +/** + * Active scoring profiles + per-client results for the clients list (RBAC-scoped). + * + * @return array{profiles: list, byClient: array>} + */ +function qdb_clients_scoring_summary_for_list(PDO $pdo, array $tokenRec): array { + require_once __DIR__ . '/scoring.php'; + [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl'); + + $profiles = $pdo->query( + "SELECT profileID, name FROM scoring_profile WHERE isActive = 1 ORDER BY name" + )->fetchAll(PDO::FETCH_ASSOC); + + if ($profiles === []) { + return ['profiles' => [], 'byClient' => []]; + } + + $stmt = $pdo->prepare( + "SELECT r.clientCode, r.profileID, r.band, r.coachBand, r.weightedTotal, sp.name + FROM client_scoring_profile_result r + INNER JOIN scoring_profile sp ON sp.profileID = r.profileID AND sp.isActive = 1 + INNER JOIN client cl ON cl.clientCode = r.clientCode + WHERE ($rbacClause)" + ); + $stmt->execute($rbacParams); + + $byClient = []; + foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) { + $cc = (string)$row['clientCode']; + $coachBand = trim((string)($row['coachBand'] ?? '')); + $byClient[$cc][$row['profileID']] = [ + 'band' => (string)$row['band'], + 'calculatedBand' => (string)$row['band'], + 'coachBand' => $coachBand !== '' ? $coachBand : null, + 'effectiveBand' => qdb_effective_scoring_band($row), + 'pendingReview' => $coachBand === '', + 'weightedTotal' => (float)$row['weightedTotal'], + 'name' => (string)$row['name'], + ]; + } + + return [ + 'profiles' => array_map(static fn(array $p) => [ + 'profileID' => (string)$p['profileID'], + 'name' => (string)$p['name'], + ], $profiles), + 'byClient' => $byClient, + ]; +} + +/** + * @return list + */ +function qdb_client_scoring_dots_for_client(string $clientCode, array $summary): array { + $out = []; + foreach ($summary['profiles'] as $profile) { + $result = $summary['byClient'][$clientCode][$profile['profileID']] ?? null; + $out[] = [ + 'profileID' => $profile['profileID'], + 'name' => $profile['name'], + 'band' => $result['calculatedBand'] ?? null, + 'calculatedBand' => $result['calculatedBand'] ?? null, + 'coachBand' => $result['coachBand'] ?? null, + 'effectiveBand' => $result['effectiveBand'] ?? null, + 'pendingReview' => $result['pendingReview'] ?? false, + 'weightedTotal' => $result !== null ? $result['weightedTotal'] : null, + ]; + } + return $out; +} diff --git a/lib/text_sanitize.php b/lib/text_sanitize.php new file mode 100644 index 0000000..009859c --- /dev/null +++ b/lib/text_sanitize.php @@ -0,0 +1,61 @@ + 0 && strlen($s) < $min) { + json_error('INVALID_FIELD', "$fieldName must be at least $min characters", 400); + } + if ($max > 0 && strlen($s) > $max) { + json_error('INVALID_FIELD', "$fieldName must be at most $max characters", 400); + } + return $s; +} + +/** + * Validate that a value is one of the allowed options. + * Returns the value or calls json_error. + */ +function validate_enum(mixed $value, string $fieldName, array $allowed): string { + $s = trim((string)$value); + if (!in_array($s, $allowed, true)) { + json_error('INVALID_FIELD', "$fieldName must be one of: " . implode(', ', $allowed), 400); + } + return $s; +} + +/** + * Validate and return an integer from body, with optional min/max bounds. + */ +function validate_int(mixed $value, string $fieldName, ?int $min = null, ?int $max = null): int { + $i = (int)$value; + if ($min !== null && $i < $min) { + json_error('INVALID_FIELD', "$fieldName must be at least $min", 400); + } + if ($max !== null && $i > $max) { + json_error('INVALID_FIELD', "$fieldName must be at most $max", 400); + } + return $i; +} + +/** + * Require the request method to match, or exit with 405. + */ +function require_method(string ...$allowed): void { + $method = $_SERVER['REQUEST_METHOD']; + if (!in_array($method, $allowed, true)) { + json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405); + } +} diff --git a/logs/api/.gitkeep b/logs/api/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..3792903 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,30 @@ + + + + + tests/Unit + + + tests/Integration + + + + + + lib + handlers + db_init.php + common.php + + + tests + vendor + + + diff --git a/schema.sql b/schema.sql new file mode 100644 index 0000000..859d397 --- /dev/null +++ b/schema.sql @@ -0,0 +1,259 @@ +PRAGMA foreign_keys = ON; + +CREATE TABLE IF NOT EXISTS users ( + userID TEXT NOT NULL PRIMARY KEY, + username TEXT NOT NULL UNIQUE, + passwordHash TEXT NOT NULL, + role TEXT NOT NULL CHECK(role IN ('admin','supervisor','coach')), + entityID TEXT NOT NULL, + mustChangePassword INTEGER NOT NULL DEFAULT 1, + createdAt INTEGER NOT NULL +); + +CREATE TABLE IF NOT EXISTS admin ( + adminID TEXT NOT NULL PRIMARY KEY, + username TEXT NOT NULL, + location TEXT NOT NULL DEFAULT '' +); + +CREATE TABLE IF NOT EXISTS supervisor ( + supervisorID TEXT NOT NULL PRIMARY KEY, + username TEXT NOT NULL, + location TEXT NOT NULL DEFAULT '' +); + +CREATE TABLE IF NOT EXISTS coach ( + coachID TEXT NOT NULL PRIMARY KEY, + supervisorID TEXT NOT NULL, + username TEXT NOT NULL, + FOREIGN KEY(supervisorID) REFERENCES supervisor(supervisorID) +); + +CREATE TABLE IF NOT EXISTS client ( + clientCode TEXT NOT NULL PRIMARY KEY, + coachID TEXT NOT NULL, + archived INTEGER NOT NULL DEFAULT 0, + archivedAt INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY(coachID) REFERENCES coach(coachID) +); + +CREATE TABLE IF NOT EXISTS questionnaire_category ( + categoryKey TEXT NOT NULL PRIMARY KEY, + label TEXT NOT NULL DEFAULT '', + color TEXT NOT NULL DEFAULT '#64748b', + orderIndex INTEGER NOT NULL DEFAULT 0 +); + +CREATE TABLE IF NOT EXISTS questionnaire ( + questionnaireID TEXT NOT NULL PRIMARY KEY, + name TEXT NOT NULL DEFAULT '', + version TEXT NOT NULL DEFAULT '', + state TEXT NOT NULL DEFAULT '', + orderIndex INTEGER NOT NULL DEFAULT 0, + showPoints INTEGER NOT NULL DEFAULT 0, + conditionJson TEXT NOT NULL DEFAULT '{}', + categoryKey TEXT NOT NULL DEFAULT '', + structureRevision INTEGER NOT NULL DEFAULT 1, + structureChangedAt INTEGER NOT NULL DEFAULT 0 +); + +CREATE TABLE IF NOT EXISTS questionnaire_structure_snapshot ( + questionnaireID TEXT NOT NULL, + structureRevision INTEGER NOT NULL, + createdAt INTEGER NOT NULL, + manifestJson TEXT NOT NULL DEFAULT '{}', + PRIMARY KEY (questionnaireID, structureRevision), + FOREIGN KEY(questionnaireID) REFERENCES questionnaire(questionnaireID) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS question ( + questionID TEXT NOT NULL PRIMARY KEY, + questionnaireID TEXT NOT NULL, + defaultText TEXT NOT NULL DEFAULT '', + type TEXT NOT NULL DEFAULT '', + orderIndex INTEGER NOT NULL DEFAULT 0, + isRequired INTEGER NOT NULL DEFAULT 0, + configJson TEXT NOT NULL DEFAULT '{}', + retiredAt INTEGER NOT NULL DEFAULT 0, + retiredInRevision INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY(questionnaireID) REFERENCES questionnaire(questionnaireID) +); + +CREATE TABLE IF NOT EXISTS answer_option ( + answerOptionID TEXT NOT NULL PRIMARY KEY, + questionID TEXT NOT NULL, + defaultText TEXT NOT NULL DEFAULT '', + points INTEGER NOT NULL DEFAULT 0, + orderIndex INTEGER NOT NULL DEFAULT 0, + nextQuestionId TEXT NOT NULL DEFAULT '', + retiredAt INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY(questionID) REFERENCES question(questionID) +); + +CREATE TABLE IF NOT EXISTS client_answer ( + clientCode TEXT NOT NULL, + questionID TEXT NOT NULL, + answerOptionID TEXT, + freeTextValue TEXT, + numericValue REAL, + answeredAt INTEGER, + PRIMARY KEY (clientCode, questionID), + FOREIGN KEY(clientCode) REFERENCES client(clientCode), + FOREIGN KEY(questionID) REFERENCES question(questionID), + FOREIGN KEY(answerOptionID) REFERENCES answer_option(answerOptionID) +); + +CREATE TABLE IF NOT EXISTS completed_questionnaire ( + clientCode TEXT NOT NULL, + questionnaireID TEXT NOT NULL, + assignedByCoach TEXT, + status TEXT NOT NULL DEFAULT '', + startedAt INTEGER, + completedAt INTEGER, + sumPoints INTEGER, + PRIMARY KEY (clientCode, questionnaireID), + FOREIGN KEY(clientCode) REFERENCES client(clientCode), + FOREIGN KEY(questionnaireID) REFERENCES questionnaire(questionnaireID), + FOREIGN KEY(assignedByCoach) REFERENCES coach(coachID) +); + +CREATE TABLE IF NOT EXISTS question_translation ( + questionID TEXT NOT NULL, + languageCode TEXT NOT NULL, + text TEXT NOT NULL DEFAULT '', + PRIMARY KEY (questionID, languageCode), + FOREIGN KEY(questionID) REFERENCES question(questionID) +); + +CREATE TABLE IF NOT EXISTS answer_option_translation ( + answerOptionID TEXT NOT NULL, + languageCode TEXT NOT NULL, + text TEXT NOT NULL DEFAULT '', + PRIMARY KEY (answerOptionID, languageCode), + FOREIGN KEY(answerOptionID) REFERENCES answer_option(answerOptionID) +); + +CREATE TABLE IF NOT EXISTS string_translation ( + stringKey TEXT NOT NULL, + languageCode TEXT NOT NULL, + text TEXT NOT NULL DEFAULT '', + PRIMARY KEY (stringKey, languageCode) +); + +CREATE TABLE IF NOT EXISTS language ( + languageCode TEXT NOT NULL PRIMARY KEY, + name TEXT NOT NULL DEFAULT '' +); + +CREATE TABLE IF NOT EXISTS session ( + token TEXT NOT NULL PRIMARY KEY, + userID TEXT NOT NULL, + role TEXT NOT NULL, + entityID TEXT NOT NULL DEFAULT '', + createdAt INTEGER NOT NULL, + expiresAt INTEGER NOT NULL, + temp INTEGER NOT NULL DEFAULT 0 +); + +CREATE TABLE IF NOT EXISTS questionnaire_submission ( + submissionID TEXT NOT NULL PRIMARY KEY, + clientCode TEXT NOT NULL, + questionnaireID TEXT NOT NULL, + version INTEGER NOT NULL, + submittedAt INTEGER NOT NULL, + submittedByUserID TEXT NOT NULL DEFAULT '', + submittedByRole TEXT NOT NULL DEFAULT '', + assignedByCoach TEXT, + status TEXT NOT NULL DEFAULT '', + startedAt INTEGER, + completedAt INTEGER, + sumPoints INTEGER, + structureRevision INTEGER NOT NULL DEFAULT 1, + structureSnapshotJson TEXT NOT NULL DEFAULT '{}', + FOREIGN KEY(clientCode) REFERENCES client(clientCode), + FOREIGN KEY(questionnaireID) REFERENCES questionnaire(questionnaireID), + FOREIGN KEY(assignedByCoach) REFERENCES coach(coachID), + UNIQUE(clientCode, questionnaireID, version) +); + +CREATE INDEX IF NOT EXISTS idx_submission_client_qn + ON questionnaire_submission(clientCode, questionnaireID); +CREATE INDEX IF NOT EXISTS idx_submission_client_time + ON questionnaire_submission(clientCode, submittedAt); + +CREATE TABLE IF NOT EXISTS client_answer_submission ( + submissionID TEXT NOT NULL, + questionID TEXT NOT NULL, + answerOptionID TEXT, + freeTextValue TEXT, + numericValue REAL, + answeredAt INTEGER, + PRIMARY KEY (submissionID, questionID), + FOREIGN KEY(submissionID) REFERENCES questionnaire_submission(submissionID) ON DELETE CASCADE, + FOREIGN KEY(questionID) REFERENCES question(questionID), + FOREIGN KEY(answerOptionID) REFERENCES answer_option(answerOptionID) +); + +CREATE TABLE IF NOT EXISTS client_followup_note ( + clientCode TEXT NOT NULL PRIMARY KEY, + note TEXT NOT NULL DEFAULT '', + updatedByUserID TEXT NOT NULL DEFAULT '', + updatedAt INTEGER NOT NULL, + FOREIGN KEY(clientCode) REFERENCES client(clientCode) +); + +CREATE TABLE IF NOT EXISTS question_score_rule ( + ruleID TEXT NOT NULL PRIMARY KEY, + questionID TEXT NOT NULL, + scopeKey TEXT NOT NULL DEFAULT '', + levelKey TEXT NOT NULL, + points INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY(questionID) REFERENCES question(questionID) ON DELETE CASCADE, + UNIQUE(questionID, scopeKey, levelKey) +); + +CREATE INDEX IF NOT EXISTS idx_score_rule_question + ON question_score_rule(questionID); + +CREATE TABLE IF NOT EXISTS scoring_profile ( + profileID TEXT NOT NULL PRIMARY KEY, + name TEXT NOT NULL DEFAULT '', + description TEXT NOT NULL DEFAULT '', + isActive INTEGER NOT NULL DEFAULT 1, + greenMin INTEGER NOT NULL DEFAULT 0, + greenMax INTEGER NOT NULL DEFAULT 12, + yellowMin INTEGER NOT NULL DEFAULT 13, + yellowMax INTEGER NOT NULL DEFAULT 36, + redMin INTEGER NOT NULL DEFAULT 37, + createdAt INTEGER NOT NULL DEFAULT 0, + updatedAt INTEGER NOT NULL DEFAULT 0 +); + +CREATE TABLE IF NOT EXISTS scoring_profile_questionnaire ( + profileID TEXT NOT NULL, + questionnaireID TEXT NOT NULL, + weight REAL NOT NULL DEFAULT 1.0, + orderIndex INTEGER NOT NULL DEFAULT 0, + PRIMARY KEY (profileID, questionnaireID), + FOREIGN KEY(profileID) REFERENCES scoring_profile(profileID) ON DELETE CASCADE, + FOREIGN KEY(questionnaireID) REFERENCES questionnaire(questionnaireID) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS client_scoring_profile_result ( + clientCode TEXT NOT NULL, + profileID TEXT NOT NULL, + weightedTotal REAL NOT NULL DEFAULT 0, + band TEXT NOT NULL DEFAULT '', + computedAt INTEGER NOT NULL DEFAULT 0, + questionnaireSnapshot TEXT NOT NULL DEFAULT '{}', + coachBand TEXT NOT NULL DEFAULT '', + coachReviewedAt INTEGER NOT NULL DEFAULT 0, + coachReviewedByUserID TEXT NOT NULL DEFAULT '', + PRIMARY KEY (clientCode, profileID), + FOREIGN KEY(clientCode) REFERENCES client(clientCode) ON DELETE CASCADE, + FOREIGN KEY(profileID) REFERENCES scoring_profile(profileID) ON DELETE CASCADE +); + +CREATE INDEX IF NOT EXISTS idx_client_profile_result_profile + ON client_scoring_profile_result(profileID); + diff --git a/scripts/generate_dev_fixture.py b/scripts/generate_dev_fixture.py new file mode 100644 index 0000000..4235f8e --- /dev/null +++ b/scripts/generate_dev_fixture.py @@ -0,0 +1,237 @@ +#!/usr/bin/env python3 +"""Generate dev-test-fixture.json for admin Dev tab import.""" +import json +import random +from datetime import datetime, timedelta +from pathlib import Path + +PREFIX = "dev_" +PASSWORD = "socialvrlab" +CLIENT_PREFIX = "DEV-CL-" +SCALE = 5 + +# Fixed RNG + anchor time so the file is reproducible but not grid-like. +RNG = random.Random(42) +ANCHOR = datetime(2026, 5, 27, 15, 30, 0) + +REPO_ROOT = Path(__file__).resolve().parents[2] + +FALLBACK_QUESTIONNAIRE_IDS = [ + "questionnaire_1_demographic_information", + "questionnaire_2_rhs", + "questionnaire_3_integration_index", + "questionnaire_4_consultation_results", + "questionnaire_5_final_interview", + "questionnaire_6_follow_up_survey", +] + +NUM_ADMINS = 2 * SCALE +NUM_SUPERVISORS = 3 * SCALE +NUM_COACHES = 20 * SCALE +NUM_CLIENTS = 100 * SCALE +CLIENTS_WITHOUT_COMPLETIONS = 15 * SCALE + +# Roughly how many clients (with a coach) get at least one questionnaire. +COMPLETION_PARTICIPATION = 0.82 + + +def resolve_bundle_path() -> Path | None: + """Use the newest questionnaires_bundle_*.json in the repo root.""" + candidates = sorted( + REPO_ROOT.glob("questionnaires_bundle_*.json"), + key=lambda p: p.stat().st_mtime, + reverse=True, + ) + return candidates[0] if candidates else None + + +def load_questionnaire_ids(bundle_path: Path | None) -> list[str]: + if bundle_path is None or not bundle_path.is_file(): + return FALLBACK_QUESTIONNAIRE_IDS + bundle = json.loads(bundle_path.read_text(encoding="utf-8")) + ids = [ + item["questionnaire"]["questionnaireID"] + for item in bundle.get("questionnaires", []) + if item.get("questionnaire", {}).get("questionnaireID") + ] + return ids or FALLBACK_QUESTIONNAIRE_IDS + + +def ts_days_ago(min_days: float, max_days: float) -> int: + days = RNG.uniform(min_days, max_days) + return int((ANCHOR - timedelta(days=days)).timestamp()) + + +def variant_seed(client_code: str, qn_id: str, version_index: int) -> int: + raw = f"{client_code}:{qn_id}:v{version_index}" + return int.from_bytes(raw.encode(), "big") % 100_000 + + +def assign_coaches(clients: list[dict], coaches: list[dict]) -> None: + """Uneven coach load: a few busy coaches, several light ones.""" + coach_names = [c["username"] for c in coaches] + weights = [] + for i, _ in enumerate(coach_names): + # Coaches 1–4 and 7, 12 get more clients; tail is lighter. + base = RNG.uniform(0.4, 1.0) + if (i % 7) in (0, 1, 2): + base *= RNG.uniform(2.0, 4.5) + if i % 11 == 0: + base *= RNG.uniform(0.15, 0.45) + weights.append(base) + + for row in clients: + row["coach"] = RNG.choices(coach_names, weights=weights, k=1)[0] + + +def build_completions( + questionnaire_ids: list[str], + clients_with_data: list[str], +) -> list[dict]: + """ + Natural-ish completion patterns: + - Sequential questionnaire funnel (not random qn per slot) + - Irregular timestamps and gaps between uploads + - ~20% of client/qn pairs have 2–4 archived versions + """ + completions: list[dict] = [] + qn_count = len(questionnaire_ids) + + # How many questionnaires a client completes (skewed toward mid funnel). + qn_count_weights = [8, 14, 22, 24, 18, 10, 4][:qn_count] + if len(qn_count_weights) < qn_count: + qn_count_weights += [2] * (qn_count - len(qn_count_weights)) + + for client_code in clients_with_data: + if RNG.random() > COMPLETION_PARTICIPATION: + continue + + num_qn = RNG.choices( + list(range(1, qn_count + 1)), + weights=qn_count_weights[:qn_count], + k=1, + )[0] + selected = questionnaire_ids[:num_qn] + + # First activity somewhere in the last ~6 months; some clients are very recent. + cursor = ts_days_ago(3, 185) + if RNG.random() < 0.12: + cursor = ts_days_ago(0.5, 14) + + for qn_id in selected: + version_count = 1 + roll = RNG.random() + if roll < 0.08 and num_qn >= 2: + version_count = 4 + elif roll < 0.20: + version_count = 3 + elif roll < 0.38: + version_count = 2 + + uploads = [] + for v in range(version_count): + if v > 0: + # Re-upload after days or weeks (sometimes same day correction). + if RNG.random() < 0.18: + gap_sec = RNG.randint(2 * 3600, 36 * 3600) + else: + gap_sec = RNG.randint(2 * 86400, 55 * 86400) + cursor += gap_sec + else: + # First upload for this qn: often a few days after previous qn. + cursor += RNG.randint(0, 12 * 86400) + + duration = RNG.randint(4 * 60, 55 * 60) + started = cursor - duration + completed = cursor + RNG.randint(0, 120) + + uploads.append({ + "submittedAt": completed, + "startedAt": started, + "variant": variant_seed(client_code, qn_id, v), + }) + + completions.append({ + "clientCode": client_code, + "questionnaireID": qn_id, + "versions": version_count, + "uploads": uploads, + }) + + RNG.shuffle(completions) + return completions + + +def main(): + bundle_path = resolve_bundle_path() + questionnaire_ids = load_questionnaire_ids(bundle_path) + + admins = [ + {"username": f"{PREFIX}admin_{i}", "location": f"Dev Admin Standort {i}"} + for i in range(1, NUM_ADMINS + 1) + ] + supervisors = [ + {"username": f"{PREFIX}supervisor_{i}", "location": f"Dev Supervisor Region {i}"} + for i in range(1, NUM_SUPERVISORS + 1) + ] + coaches = [] + for i in range(1, NUM_COACHES + 1): + sv = (i - 1) % NUM_SUPERVISORS + 1 + coaches.append({ + "username": f"{PREFIX}coach_{i:02d}", + "supervisor": f"{PREFIX}supervisor_{sv}", + }) + + clients = [ + {"clientCode": f"{CLIENT_PREFIX}{i:04d}"} + for i in range(1, NUM_CLIENTS + 1) + ] + assign_coaches(clients, coaches) + + all_codes = [c["clientCode"] for c in clients] + no_data_codes = set(RNG.sample(all_codes, min(CLIENTS_WITHOUT_COMPLETIONS, len(all_codes)))) + clients_with_data = [c for c in all_codes if c not in no_data_codes] + + completions = build_completions(questionnaire_ids, clients_with_data) + total_uploads = sum(c["versions"] for c in completions) + multi_version_pairs = sum(1 for c in completions if c["versions"] > 1) + + bundle_note = bundle_path.name if bundle_path and bundle_path.is_file() else "fallback questionnaire list" + + fixture = { + "fixtureVersion": 2, + "description": ( + f"Dev/test users, clients, completions with uneven coach load and upload history " + f"({SCALE}x scale). Questionnaires from {bundle_note}. Password: socialvrlab." + ), + "prefix": PREFIX, + "defaultPassword": PASSWORD, + "sourceBundle": bundle_note, + "questionnaireIDs": questionnaire_ids, + "admins": admins, + "supervisors": supervisors, + "coaches": coaches, + "clients": clients, + "completions": completions, + "stats": { + "scale": SCALE, + "admins": len(admins), + "supervisors": len(supervisors), + "coaches": len(coaches), + "clients": len(clients), + "clientsWithoutCompletions": CLIENTS_WITHOUT_COMPLETIONS, + "completionRecords": len(completions), + "totalUploads": total_uploads, + "multiVersionPairs": multi_version_pairs, + "questionnaires": len(questionnaire_ids), + }, + } + + out = REPO_ROOT / "dev-test-fixture.json" + out.write_text(json.dumps(fixture, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + print(f"Wrote {out}") + print(json.dumps(fixture["stats"], indent=2)) + + +if __name__ == "__main__": + main() diff --git a/seed_admin.php b/seed_admin.php new file mode 100644 index 0000000..e3981bf --- /dev/null +++ b/seed_admin.php @@ -0,0 +1,66 @@ + [location] +// Creates an initial admin user in the encrypted database. +// If the DB doesn't exist yet, it is created from schema.sql. + +if (php_sapi_name() !== 'cli') { + http_response_code(403); + echo "CLI only\n"; + exit(1); +} + +require_once __DIR__ . '/db_init.php'; + +$usage = "Usage: php seed_admin.php [location]\n"; + +$username = $argv[1] ?? ''; +$password = $argv[2] ?? ''; +$location = $argv[3] ?? ''; + +if ($username === '' || $password === '') { + fwrite(STDERR, $usage); + exit(1); +} + +if (strlen($password) < 6) { + fwrite(STDERR, "Error: Password must be at least 6 characters.\n"); + exit(1); +} + +try { + [$pdo, $tmpDb, $lockFp] = qdb_open(true); + + $existing = $pdo->prepare("SELECT userID FROM users WHERE username = :u"); + $existing->execute([':u' => $username]); + if ($existing->fetch()) { + qdb_discard($tmpDb, $lockFp); + fwrite(STDERR, "Error: User '$username' already exists.\n"); + exit(1); + } + + $adminID = bin2hex(random_bytes(16)); + $userID = bin2hex(random_bytes(16)); + $hash = password_hash($password, PASSWORD_DEFAULT); + $now = time(); + + $pdo->beginTransaction(); + + $pdo->prepare("INSERT INTO admin (adminID, username, location) VALUES (:id, :u, :loc)") + ->execute([':id' => $adminID, ':u' => $username, ':loc' => $location]); + + $pdo->prepare( + "INSERT INTO users (userID, username, passwordHash, role, entityID, mustChangePassword, createdAt) + VALUES (:uid, :u, :hash, 'admin', :eid, 1, :now)" + )->execute([':uid' => $userID, ':u' => $username, ':hash' => $hash, ':eid' => $adminID, ':now' => $now]); + + $pdo->commit(); + $pdo = null; + + qdb_save($tmpDb, $lockFp); + echo "Admin user '$username' created successfully (must change password on first login).\n"; + +} catch (Throwable $e) { + if (isset($tmpDb, $lockFp)) qdb_discard($tmpDb, $lockFp); + fwrite(STDERR, "Error: " . $e->getMessage() . "\n"); + exit(1); +} diff --git a/tests/Integration/ApiRoutingTest.php b/tests/Integration/ApiRoutingTest.php new file mode 100644 index 0000000..17a9184 --- /dev/null +++ b/tests/Integration/ApiRoutingTest.php @@ -0,0 +1,22 @@ +api()->request('GET', 'does-not-exist'); + $this->assertApiError($res, 'NOT_FOUND', 404); + } + + public function testUnauthorizedWithoutToken(): void + { + $res = $this->api()->request('GET', 'questionnaires'); + $this->assertApiError($res, 'UNAUTHORIZED', 401); + } +} diff --git a/tests/Integration/AppQuestionnairesBulkTest.php b/tests/Integration/AppQuestionnairesBulkTest.php new file mode 100644 index 0000000..d69691a --- /dev/null +++ b/tests/Integration/AppQuestionnairesBulkTest.php @@ -0,0 +1,51 @@ +submitFixtureQuestionnaire(); + $login = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $res = $this->api()->withMobileToken($login['token'], 'GET', 'app_questionnaires', null, [ + 'answersBulk' => '1', + ]); + $this->assertApiOk($res); + $this->assertTrue($res['data']['encrypted'] ?? false); + $plain = qdb_decrypt_sensitive_envelope($res['data'], $login['token']); + $payload = json_decode($plain, true, 512, JSON_THROW_ON_ERROR); + $this->assertIsArray($payload['clients'] ?? null); + $found = null; + foreach ($payload['clients'] as $row) { + if (($row['clientCode'] ?? '') === $this->fixture()->clientCode) { + $found = $row; + break; + } + } + $this->assertNotNull($found, 'fixture client missing from bulk payload'); + $this->assertNotEmpty($found['questionnaires'] ?? []); + $qn = $found['questionnaires'][0]; + $this->assertSame($this->fixture()->questionnaireId, $qn['questionnaireID'] ?? null); + $this->assertNotEmpty($qn['answers'] ?? []); + } + + public function testReadDbOpenUsesCacheOnSecondCall(): void + { + [$pdo1, $tmp1] = qdb_open(false); + [$pdo2, $tmp2] = qdb_open(false); + $this->assertSame(QDB_READONLY_CACHE_HANDLE, $tmp1); + $this->assertSame(QDB_READONLY_CACHE_HANDLE, $tmp2); + $this->assertSame($pdo1, $pdo2); + qdb_discard($tmp1, null); + qdb_discard($tmp2, null); + } +} diff --git a/tests/Integration/AppQuestionnairesTest.php b/tests/Integration/AppQuestionnairesTest.php new file mode 100644 index 0000000..664c9d3 --- /dev/null +++ b/tests/Integration/AppQuestionnairesTest.php @@ -0,0 +1,61 @@ +submitFixtureQuestionnaire(); + $this->assertApiOk($res); + $this->assertTrue($res['data']['submitted'] ?? false); + } + + public function testCoachLoadsQuestionnaireDefinition(): void + { + $login = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $res = $this->api()->withMobileToken($login['token'], 'GET', 'app_questionnaires', null, [ + 'id' => $this->fixture()->questionnaireId, + ]); + $this->assertApiOk($res); + $this->assertNotEmpty($res['data']['questions']); + + $glass = null; + foreach ($res['data']['questions'] as $q) { + if (($q['layout'] ?? '') === 'glass_scale_question') { + $glass = $q; + break; + } + } + $this->assertNotNull($glass); + $this->assertIsArray($glass['glassSymptoms'] ?? null); + $symptomKeys = array_column($glass['glassSymptoms'], 'key'); + $this->assertContains($this->fixture()->glassSymptomKey, $symptomKeys); + } + + public function testCoachListsAssignedClients(): void + { + $this->submitFixtureQuestionnaire(); + $login = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $res = $this->api()->withMobileToken($login['token'], 'GET', 'app_questionnaires', null, [ + 'clients' => '1', + ]); + $this->assertApiOk($res); + $this->assertTrue($res['data']['encrypted'] ?? false); + $plain = qdb_decrypt_sensitive_envelope($res['data'], $login['token']); + $payload = json_decode($plain, true, 512, JSON_THROW_ON_ERROR); + $codes = array_column($payload['clients'], 'clientCode'); + $this->assertContains($this->fixture()->clientCode, $codes); + } +} diff --git a/tests/Integration/AppSubmitExtendedTest.php b/tests/Integration/AppSubmitExtendedTest.php new file mode 100644 index 0000000..7c21459 --- /dev/null +++ b/tests/Integration/AppSubmitExtendedTest.php @@ -0,0 +1,271 @@ +api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $f = $this->fixture(); + + $this->assertApiOk($this->submitQuestionnaireAs($login['token'], $f->questionnaireId, $f->clientCode, [ + ['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes'], + ['questionID' => $f->freeTextShortId, 'freeTextValue' => 'Old notes'], + ['questionID' => $f->glassSymptomKey, 'freeTextValue' => 'moderate'], + ['questionID' => 'fatigue', 'freeTextValue' => 'severe'], + ])); + + $res = $this->submitQuestionnaireAs($login['token'], $f->questionnaireId, $f->clientCode, [ + ['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes'], + ]); + $this->assertApiOk($res); + + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + + $ft = $pdo->prepare( + 'SELECT freeTextValue FROM client_answer WHERE clientCode = :cc AND questionID = :qid' + ); + $ft->execute([':cc' => $f->clientCode, ':qid' => $f->freeTextQuestionId]); + $this->assertFalse($ft->fetchColumn()); + + $glass = $pdo->prepare( + 'SELECT freeTextValue FROM client_answer WHERE clientCode = :cc AND questionID = :qid' + ); + $glass->execute([':cc' => $f->clientCode, ':qid' => $f->glassQuestionId]); + $this->assertFalse($glass->fetchColumn()); + + $sub = $pdo->prepare( + 'SELECT submissionID FROM questionnaire_submission + WHERE clientCode = :cc AND questionnaireID = :qn ORDER BY version DESC LIMIT 1' + ); + $sub->execute([':cc' => $f->clientCode, ':qn' => $f->questionnaireId]); + $submissionId = (string)$sub->fetchColumn(); + + $archived = $pdo->prepare( + 'SELECT freeTextValue FROM client_answer_submission + WHERE submissionID = :sid AND questionID = :qid' + ); + $archived->execute([':sid' => $submissionId, ':qid' => $f->freeTextQuestionId]); + $this->assertFalse($archived->fetchColumn()); + + $archivedGlass = $pdo->prepare( + 'SELECT freeTextValue FROM client_answer_submission + WHERE submissionID = :sid AND questionID = :qid' + ); + $archivedGlass->execute([':sid' => $submissionId, ':qid' => $f->glassQuestionId]); + $this->assertFalse($archivedGlass->fetchColumn()); + + qdb_discard($tmpDb, $lockFp); + } + + public function testResubmitReplacesGlassSymptomsInsteadOfMerging(): void + { + $login = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $f = $this->fixture(); + + $this->assertApiOk($this->submitQuestionnaireAs($login['token'], $f->questionnaireId, $f->clientCode, [ + ['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes'], + ['questionID' => $f->glassSymptomKey, 'freeTextValue' => 'moderate'], + ['questionID' => 'fatigue', 'freeTextValue' => 'severe'], + ])); + + $res = $this->submitQuestionnaireAs($login['token'], $f->questionnaireId, $f->clientCode, [ + ['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes'], + ['questionID' => $f->glassSymptomKey, 'freeTextValue' => 'mild'], + ]); + $this->assertApiOk($res); + + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $glass = $pdo->prepare( + 'SELECT freeTextValue FROM client_answer WHERE clientCode = :cc AND questionID = :qid' + ); + $glass->execute([':cc' => $f->clientCode, ':qid' => $f->glassQuestionId]); + $decoded = json_decode((string)$glass->fetchColumn(), true); + $this->assertSame(['pain' => 'mild'], $decoded); + qdb_discard($tmpDb, $lockFp); + } + + public function testResubmitCreatesSecondSubmissionVersion(): void + { + $login = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $f = $this->fixture(); + + $this->assertApiOk($this->submitFixtureQuestionnaire()); + $this->assertSame(1, $this->submissionVersionCount($f->clientCode, $f->questionnaireId)); + + $res = $this->submitQuestionnaireAs($login['token'], $f->questionnaireId, $f->clientCode, [ + ['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes'], + ]); + $this->assertApiOk($res); + $this->assertTrue($res['data']['submitted'] ?? false); + $this->assertSame(2, $this->submissionVersionCount($f->clientCode, $f->questionnaireId)); + + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $stmt = $pdo->prepare( + 'SELECT COUNT(*) FROM client_answer_submission cas + INNER JOIN questionnaire_submission qs ON qs.submissionID = cas.submissionID + WHERE qs.clientCode = :cc AND qs.questionnaireID = :qn' + ); + $stmt->execute([':cc' => $f->clientCode, ':qn' => $f->questionnaireId]); + $this->assertGreaterThanOrEqual(2, (int)$stmt->fetchColumn()); + qdb_discard($tmpDb, $lockFp); + } + + public function testSupervisorCanSubmitForClient(): void + { + $login = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::SUPERVISOR_USERNAME, + DatabaseSeeder::PASSWORD + ); + $f = $this->fixture(); + $res = $this->submitQuestionnaireAs($login['token'], $f->questionnaireId, $f->clientCode, [ + ['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes'], + ]); + $this->assertApiOk($res); + + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $stmt = $pdo->prepare( + 'SELECT submittedByRole FROM questionnaire_submission + WHERE clientCode = :cc AND questionnaireID = :qn ORDER BY version DESC LIMIT 1' + ); + $stmt->execute([':cc' => $f->clientCode, ':qn' => $f->questionnaireId]); + $this->assertSame('supervisor', $stmt->fetchColumn()); + qdb_discard($tmpDb, $lockFp); + } + + public function testSubmitWithFreeTextAndGlassSymptom(): void + { + $login = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $f = $this->fixture(); + $res = $this->submitQuestionnaireAs($login['token'], $f->questionnaireId, $f->clientCode, [ + ['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes'], + ['questionID' => $f->freeTextShortId, 'freeTextValue' => 'Feeling better'], + ['questionID' => $f->glassSymptomKey, 'freeTextValue' => 'moderate'], + ]); + $this->assertApiOk($res); + + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $ft = $pdo->prepare( + 'SELECT freeTextValue FROM client_answer WHERE clientCode = :cc AND questionID = :qid' + ); + $ft->execute([':cc' => $f->clientCode, ':qid' => $f->freeTextQuestionId]); + $this->assertSame('Feeling better', $ft->fetchColumn()); + + $glass = $pdo->prepare( + 'SELECT freeTextValue FROM client_answer WHERE clientCode = :cc AND questionID = :qid' + ); + $glass->execute([':cc' => $f->clientCode, ':qid' => $f->glassQuestionId]); + $json = (string)$glass->fetchColumn(); + $decoded = json_decode($json, true); + $this->assertIsArray($decoded); + $this->assertSame('moderate', $decoded[$f->glassSymptomKey] ?? null); + qdb_discard($tmpDb, $lockFp); + } + + public function testCoachLoadsEncryptedAnswersBundle(): void + { + $this->submitFixtureQuestionnaire(); + $login = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $f = $this->fixture(); + $res = $this->api()->withMobileToken($login['token'], 'GET', 'app_questionnaires', null, [ + 'clientCode' => $f->clientCode, + 'answers' => '1', + ]); + $this->assertApiOk($res); + $this->assertTrue($res['data']['encrypted'] ?? false); + $plain = qdb_decrypt_sensitive_envelope($res['data'], $login['token']); + $payload = json_decode($plain, true, 512, JSON_THROW_ON_ERROR); + $this->assertSame($f->clientCode, $payload['clientCode']); + $this->assertNotEmpty($payload['questionnaires']); + } + + public function testMultiCheckSubmitAccumulatesPoints(): void + { + $login = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $f = $this->fixture(); + $res = $this->submitQuestionnaireAs($login['token'], $f->questionnaireId, $f->clientCode, [ + ['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes'], + ['questionID' => $f->multiCheckShortId, 'answerOptionKey' => 'opt_a'], + ['questionID' => $f->multiCheckShortId, 'answerOptionKey' => 'opt_b'], + ]); + $this->assertApiOk($res); + $this->assertSame(3, $res['data']['sumPoints'] ?? 0); + } + + public function testRequiredQuestionMissingFailsValidation(): void + { + $login = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $f = $this->fixture(); + $res = $this->submitQuestionnaireAs($login['token'], $f->questionnaireId, $f->clientCode, [ + ['questionID' => $f->freeTextShortId, 'freeTextValue' => 'skipped consent'], + ]); + $this->assertApiError($res, 'VALIDATION_FAILED', 400); + $codes = array_column($res['error']['details']['errors'] ?? [], 'code'); + $this->assertContains('MISSING_ANSWER', $codes); + } + + public function testActiveListExcludesDraftQuestionnaire(): void + { + $login = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $f = $this->fixture(); + $res = $this->api()->withMobileToken($login['token'], 'GET', 'app_questionnaires'); + $this->assertApiOk($res); + $ids = array_column($res['data'], 'id'); + $this->assertContains($f->questionnaireId, $ids); + $this->assertNotContains($f->draftQuestionnaireId, $ids); + } + + public function testTempTokenCannotSubmitQuestionnaire(): void + { + [$pdo, $tmpDb, $lockFp] = qdb_open(true); + $f = $this->fixture(); + $pdo->prepare('UPDATE users SET mustChangePassword = 1 WHERE userID = :uid') + ->execute([':uid' => $f->supervisorUserId]); + qdb_save($tmpDb, $lockFp); + + $login = $this->api()->loginMobile( + DatabaseSeeder::SUPERVISOR_USERNAME, + DatabaseSeeder::PASSWORD + ); + $this->assertApiOk($login); + $tempToken = $login['data']['token']; + $res = $this->api()->encryptedPost($tempToken, 'app_questionnaires', [ + 'questionnaireID' => $f->questionnaireId, + 'clientCode' => $f->clientCode, + 'answers' => [ + ['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes'], + ], + ]); + $this->assertApiError($res, 'PASSWORD_CHANGE_REQUIRED', 403); + } +} diff --git a/tests/Integration/AppSubmitLegacyTest.php b/tests/Integration/AppSubmitLegacyTest.php new file mode 100644 index 0000000..3f8eeca --- /dev/null +++ b/tests/Integration/AppSubmitLegacyTest.php @@ -0,0 +1,78 @@ +api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $f = $this->fixture(); + + $this->assertApiOk($this->submitQuestionnaireAs( + $login['token'], + $f->questionnaireId, + $f->clientCode, + [['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes']], + null, + null, + 1 + )); + + require_once dirname(__DIR__, 2) . '/lib/questionnaire_structure.php'; + [$pdo, $tmpDb, $lockFp] = qdb_open(true); + $rev = qdb_bump_structure_revision($pdo, $f->questionnaireId, 'legacy_test_retire'); + qdb_retire_question($pdo, $f->questionId, $rev); + $currentRev = qdb_questionnaire_structure_revision($pdo, $f->questionnaireId); + qdb_save($tmpDb, $lockFp); + + $this->assertGreaterThan(1, $currentRev); + + $res = $this->submitQuestionnaireAs( + $login['token'], + $f->questionnaireId, + $f->clientCode, + [['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes']], + null, + null, + 1 + ); + $this->assertApiOk($res); + $this->assertTrue($res['data']['submitted'] ?? false); + $this->assertTrue($res['data']['legacySubmit'] ?? false); + $this->assertSame(1, $res['data']['structureRevision'] ?? null); + + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $sub = $pdo->prepare( + 'SELECT structureRevision, structureSnapshotJson FROM questionnaire_submission + WHERE clientCode = :cc AND questionnaireID = :qn ORDER BY version DESC LIMIT 1' + ); + $sub->execute([':cc' => $f->clientCode, ':qn' => $f->questionnaireId]); + $row = $sub->fetch(\PDO::FETCH_ASSOC); + $this->assertSame(1, (int)($row['structureRevision'] ?? 0)); + $snap = json_decode($row['structureSnapshotJson'] ?? '{}', true) ?: []; + $this->assertNotEmpty($snap['questions'] ?? []); + + $archived = $pdo->prepare( + 'SELECT COUNT(*) FROM client_answer_submission cas + INNER JOIN questionnaire_submission qs ON qs.submissionID = cas.submissionID + WHERE qs.clientCode = :cc AND qs.questionnaireID = :qn AND cas.questionID = :qid' + ); + $archived->execute([ + ':cc' => $f->clientCode, + ':qn' => $f->questionnaireId, + ':qid' => $f->questionId, + ]); + $this->assertGreaterThan(0, (int)$archived->fetchColumn()); + + qdb_discard($tmpDb, $lockFp); + } +} diff --git a/tests/Integration/AssignmentsActivityLogTest.php b/tests/Integration/AssignmentsActivityLogTest.php new file mode 100644 index 0000000..2f57454 --- /dev/null +++ b/tests/Integration/AssignmentsActivityLogTest.php @@ -0,0 +1,38 @@ +api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'assignments'); + $this->assertApiOk($res); + $this->assertArrayHasKey('coaches', $res['data']); + $this->assertArrayHasKey('clients', $res['data']); + } + + public function testActivityLogReadableByAdmin(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $today = gmdate('Y-m-d'); + $res = $this->api()->withToken($token, 'GET', 'activity-log', null, [ + 'date' => $today, + 'limit' => '50', + ]); + $this->assertApiOk($res); + $this->assertArrayHasKey('entries', $res['data']); + } +} diff --git a/tests/Integration/AuthTest.php b/tests/Integration/AuthTest.php new file mode 100644 index 0000000..0eba9d8 --- /dev/null +++ b/tests/Integration/AuthTest.php @@ -0,0 +1,68 @@ +api()->loginWeb(DatabaseSeeder::ADMIN_USERNAME, DatabaseSeeder::PASSWORD); + $this->assertApiOk($res); + $this->assertNotEmpty($res['data']['token']); + $this->assertSame('admin', $res['data']['role']); + } + + public function testInvalidCredentials(): void + { + $res = $this->api()->loginWeb(DatabaseSeeder::ADMIN_USERNAME, 'wrong'); + $this->assertApiError($res, 'INVALID_CREDENTIALS', 401); + } + + public function testCoachBlockedOnWebClient(): void + { + $res = $this->api()->loginWeb(DatabaseSeeder::COACH_USERNAME, DatabaseSeeder::PASSWORD); + $this->assertApiError($res, 'FORBIDDEN', 403); + } + + public function testRateLimitAfterFailures(): void + { + require_once dirname(__DIR__, 2) . '/lib/settings.php'; + [$pdo, $tmpDb, $lockFp] = qdb_open(true); + qdb_settings_save_on_pdo($pdo, qdb_settings_validate_and_merge([ + 'login_max_attempts' => 2, + 'login_window_seconds' => 600, + 'login_lockout_seconds' => 600, + ], qdb_settings_get_on_pdo($pdo))); + qdb_save($tmpDb, $lockFp); + + $this->api()->loginWeb('rate_user', 'bad1'); + $this->api()->loginWeb('rate_user', 'bad2'); + $res = $this->api()->loginWeb('rate_user', 'bad3'); + $this->assertApiError($res, 'RATE_LIMITED', 429); + } + + public function testMissingFields(): void + { + $res = $this->api()->request('POST', 'auth/login', ['username' => '']); + $this->assertApiError($res, 'MISSING_FIELDS', 400); + } + + public function testKeycloakConfigDisabledByDefault(): void + { + $res = $this->api()->request('GET', 'auth/keycloak-config'); + $this->assertApiOk($res); + $this->assertFalse($res['data']['enabled']); + $this->assertSame('', $res['data']['loginUrl']); + } + + public function testKeycloakLoginRequiresConfiguration(): void + { + $res = $this->api()->request('GET', 'auth/keycloak-login'); + $this->assertApiError($res, 'KEYCLOAK_NOT_CONFIGURED', 503); + } +} diff --git a/tests/Integration/BackupDevOpsTest.php b/tests/Integration/BackupDevOpsTest.php new file mode 100644 index 0000000..d2f05f0 --- /dev/null +++ b/tests/Integration/BackupDevOpsTest.php @@ -0,0 +1,80 @@ +api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'POST', 'backup'); + $this->assertApiOk($res); + $filename = $res['data']['filename'] ?? ''; + $this->assertNotSame('', $filename); + + $path = QDB_UPLOADS_DIR . '/backups/' . $filename; + $this->assertFileExists($path); + $this->assertGreaterThan(0, filesize($path)); + $this->assertSame(filesize(QDB_PATH), filesize($path)); + } + + public function testDevImportEmptyFixtureSucceeds(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'POST', 'dev', [ + 'fixture' => [ + 'fixtureVersion' => 2, + 'prefix' => 'dev_', + 'defaultPassword' => 'DevPass1!', + 'admins' => [], + 'supervisors' => [], + 'coaches' => [], + 'clients' => [], + ], + ]); + $this->assertApiOk($res); + $this->assertSame(0, $res['data']['imported']['clients'] ?? -1); + } + + public function testDevRejectsInvalidFixtureVersion(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'POST', 'dev', [ + 'fixture' => [ + 'fixtureVersion' => 99, + 'prefix' => 'dev_', + 'defaultPassword' => 'DevPass1!', + ], + ]); + $this->assertApiError($res, 'INVALID_FIELD', 400); + } + + public function testMigrationAddsCategoryKeyColumn(): void + { + [$pdo, $tmpDb, $lockFp] = qdb_open(true); + if (!qdb_column_exists($pdo, 'questionnaire', 'categoryKey')) { + $this->markTestSkipped('categoryKey already absent in fresh schema'); + } + $pdo->exec('ALTER TABLE questionnaire DROP COLUMN categoryKey'); + qdb_save($tmpDb, $lockFp); + + [$pdo2, $tmp2, $lock2] = qdb_open(false); + $this->assertTrue(qdb_column_exists($pdo2, 'questionnaire', 'categoryKey')); + qdb_discard($tmp2, $lock2); + } + +} diff --git a/tests/Integration/CatalogApiTest.php b/tests/Integration/CatalogApiTest.php new file mode 100644 index 0000000..75ea251 --- /dev/null +++ b/tests/Integration/CatalogApiTest.php @@ -0,0 +1,94 @@ +api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'translations', null, ['languages' => '1']); + $this->assertApiOk($res); + $this->assertIsArray($res['data']['languages']); + } + + public function testListQuestionsForQuestionnaire(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + $res = $this->api()->withToken($token, 'GET', 'questions', null, [ + 'questionnaireID' => $f->questionnaireId, + ]); + $this->assertApiOk($res); + $ids = array_column($res['data']['questions'], 'questionID'); + $this->assertContains($f->questionId, $ids); + } + + public function testListAnswerOptions(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'answer_options', null, [ + 'questionID' => $this->fixture()->questionId, + ]); + $this->assertApiOk($res); + $this->assertNotEmpty($res['data']['answerOptions']); + } + + public function testListCoachesForSupervisor(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::SUPERVISOR_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'coaches'); + $this->assertApiOk($res); + $coachIds = array_column($res['data']['coaches'], 'coachID'); + $this->assertContains($this->fixture()->coachId, $coachIds); + } + + public function testTranslationsAllDashboard(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'translations', null, ['all' => '1']); + $this->assertApiOk($res); + $this->assertArrayHasKey('questionnaires', $res['data']); + $this->assertArrayHasKey('entries', $res['data']); + } + + public function testTranslationsForQuestionnaire(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'translations', null, [ + 'questionnaireID' => $this->fixture()->questionnaireId, + ]); + $this->assertApiOk($res); + $this->assertArrayHasKey('entries', $res['data']); + } + + public function testAppTranslationsPublicNoAuth(): void + { + $res = $this->api()->request('GET', 'app_questionnaires', null, [], ['translations' => '1']); + $this->assertApiOk($res); + $this->assertArrayHasKey('translations', $res['data']); + } +} diff --git a/tests/Integration/ChangePasswordTest.php b/tests/Integration/ChangePasswordTest.php new file mode 100644 index 0000000..b7e66af --- /dev/null +++ b/tests/Integration/ChangePasswordTest.php @@ -0,0 +1,39 @@ +fixture(); + $pdo->prepare('UPDATE users SET mustChangePassword = 1 WHERE userID = :uid') + ->execute([':uid' => $f->supervisorUserId]); + qdb_save($tmpDb, $lockFp); + + $login = $this->api()->loginWeb( + \Tests\Support\DatabaseSeeder::SUPERVISOR_USERNAME, + \Tests\Support\DatabaseSeeder::PASSWORD + ); + $this->assertApiOk($login); + $this->assertTrue($login['data']['mustChangePassword']); + $tempToken = $login['data']['token']; + + $change = $this->api()->request('POST', 'auth/change-password', [ + 'username' => 'test_supervisor', + 'old_password' => 'TestPass1!', + 'new_password' => 'NewPass2!', + ], ['Authorization' => 'Bearer ' . $tempToken, 'X-QDB-Client' => 'web']); + $this->assertApiOk($change); + $this->assertNotEmpty($change['data']['token']); + + $session = $this->api()->withToken($change['data']['token'], 'GET', 'session'); + $this->assertApiOk($session); + $this->assertFalse($session['data']['mustChangePassword'] ?? false); + } +} diff --git a/tests/Integration/ClientsLifecycleTest.php b/tests/Integration/ClientsLifecycleTest.php new file mode 100644 index 0000000..2225b9d --- /dev/null +++ b/tests/Integration/ClientsLifecycleTest.php @@ -0,0 +1,114 @@ +submitFixtureQuestionnaire(); + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $code = $this->fixture()->clientCode; + $res = $this->api()->withToken($token, 'GET', 'clients', null, [ + 'clientCode' => $code, + 'detail' => '1', + ]); + $this->assertApiOk($res); + $this->assertSame($code, $res['data']['client']['clientCode'] ?? ''); + $this->assertNotEmpty($res['data']['questionnaires']); + } + + public function testSupervisorCreatesClientForOwnCoach(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::SUPERVISOR_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $code = 'CLIENT-SV-' . bin2hex(random_bytes(2)); + $res = $this->api()->withToken($token, 'POST', 'clients', [ + 'clientCode' => $code, + 'coachID' => $this->fixture()->coachId, + ]); + $this->assertApiOk($res); + $this->assertSame($code, $res['data']['clientCode']); + } + + public function testArchiveHidesClientFromListsAndFollowUp(): void + { + $this->submitFixtureQuestionnaire(); + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $code = $this->fixture()->clientCode; + + $archive = $this->api()->withToken($token, 'PATCH', 'clients', [ + 'clientCode' => $code, + 'archived' => true, + ]); + $this->assertApiOk($archive); + $this->assertSame(1, (int)($archive['data']['archived'] ?? 0)); + + $active = $this->api()->withToken($token, 'GET', 'clients'); + $this->assertApiOk($active); + $activeCodes = array_column($active['data']['clients'], 'clientCode'); + $this->assertNotContains($code, $activeCodes); + + $archivedOnly = $this->api()->withToken($token, 'GET', 'clients', null, [ + 'archiveFilter' => 'archived', + ]); + $this->assertApiOk($archivedOnly); + $archivedCodes = array_column($archivedOnly['data']['clients'], 'clientCode'); + $this->assertContains($code, $archivedCodes); + + $assign = $this->api()->withToken($token, 'GET', 'assignments'); + $this->assertApiOk($assign); + $assignCodes = array_column($assign['data']['clients'], 'clientCode'); + $this->assertNotContains($code, $assignCodes); + + $stale = $this->api()->withToken($token, 'GET', 'analytics', null, ['staleClients' => '1']); + $this->assertApiOk($stale); + $staleCodes = array_column($stale['data']['clients'], 'clientCode'); + $this->assertNotContains($code, $staleCodes); + + $restore = $this->api()->withToken($token, 'PATCH', 'clients', [ + 'clientCode' => $code, + 'archived' => false, + ]); + $this->assertApiOk($restore); + $this->assertSame(0, (int)($restore['data']['archived'] ?? 1)); + + $activeAgain = $this->api()->withToken($token, 'GET', 'clients'); + $this->assertApiOk($activeAgain); + $this->assertContains($code, array_column($activeAgain['data']['clients'], 'clientCode')); + } + + public function testAdminDeletesClient(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $code = 'CLIENT-DEL-' . bin2hex(random_bytes(2)); + $create = $this->api()->withToken($token, 'POST', 'clients', [ + 'clientCode' => $code, + 'coachID' => $this->fixture()->coachId, + ]); + $this->assertApiOk($create); + + $del = $this->api()->withToken($token, 'DELETE', 'clients', ['clientCode' => $code]); + $this->assertApiOk($del); + + $list = $this->api()->withToken($token, 'GET', 'clients'); + $codes = array_column($list['data']['clients'], 'clientCode'); + $this->assertNotContains($code, $codes); + } +} diff --git a/tests/Integration/ClientsQuestionnairesTest.php b/tests/Integration/ClientsQuestionnairesTest.php new file mode 100644 index 0000000..2c18813 --- /dev/null +++ b/tests/Integration/ClientsQuestionnairesTest.php @@ -0,0 +1,60 @@ +api()->loginWebAndGetToken( + DatabaseSeeder::SUPERVISOR_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'clients'); + $this->assertApiOk($res); + $codes = array_column($res['data']['clients'], 'clientCode'); + $this->assertContains($this->fixture()->clientCode, $codes); + } + + public function testAdminCreatesClient(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $code = 'CLIENT-NEW-' . bin2hex(random_bytes(2)); + $res = $this->api()->withToken($token, 'POST', 'clients', [ + 'clientCode' => $code, + 'coachID' => $this->fixture()->coachId, + ]); + $this->assertApiOk($res); + $this->assertSame($code, $res['data']['clientCode']); + } + + public function testQuestionnairesList(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'questionnaires'); + $this->assertApiOk($res); + $ids = array_column($res['data']['questionnaires'], 'questionnaireID'); + $this->assertContains($this->fixture()->questionnaireId, $ids); + } + + public function testQuestionsRequireQuestionnaireId(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'questions'); + $this->assertApiError($res, 'BAD_REQUEST', 400); + } +} diff --git a/tests/Integration/CoachesActivityTest.php b/tests/Integration/CoachesActivityTest.php new file mode 100644 index 0000000..6a28884 --- /dev/null +++ b/tests/Integration/CoachesActivityTest.php @@ -0,0 +1,27 @@ +submitFixtureQuestionnaire(); + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::SUPERVISOR_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'coaches', null, [ + 'coachID' => $this->fixture()->coachId, + 'recent' => '1', + 'limit' => '10', + ]); + $this->assertApiOk($res); + $this->assertArrayHasKey('submissions', $res['data']); + } +} diff --git a/tests/Integration/DbInitTest.php b/tests/Integration/DbInitTest.php new file mode 100644 index 0000000..965225b --- /dev/null +++ b/tests/Integration/DbInitTest.php @@ -0,0 +1,52 @@ +assertFileExists(QDB_PATH); + [$pdo1, $t1, $l1] = qdb_open(false); + $count1 = (int)$pdo1->query('SELECT COUNT(*) FROM users')->fetchColumn(); + qdb_discard($t1, $l1); + + [$pdo2, $t2, $l2] = qdb_open(true); + $pdo2->exec("INSERT INTO language (languageCode, name) VALUES ('fr', 'French') + ON CONFLICT(languageCode) DO NOTHING"); + qdb_save($t2, $l2); + + [$pdo3, $t3, $l3] = qdb_open(false); + $fr = $pdo3->query("SELECT name FROM language WHERE languageCode = 'fr'")->fetchColumn(); + qdb_discard($t3, $l3); + $this->assertSame('French', $fr); + $this->assertGreaterThan(0, $count1); + } + + public function testSystemSettingTableExists(): void + { + [$pdo, $tmp, $lock] = qdb_open(false); + $this->assertTrue(qdb_table_exists($pdo, 'system_setting')); + $this->assertTrue(qdb_table_exists($pdo, 'session')); + qdb_discard($tmp, $lock); + } + + public function testMigrationsRecreateDroppedSubmissionTables(): void + { + [$pdo, $tmp, $lock] = qdb_open(true); + $pdo->exec('DROP TABLE IF EXISTS client_answer_submission'); + $pdo->exec('DROP TABLE IF EXISTS questionnaire_submission'); + qdb_save($tmp, $lock); + + [$pdo2, $tmp2, $lock2] = qdb_open(false); + $this->assertTrue(qdb_table_exists($pdo2, 'questionnaire_submission')); + $this->assertTrue(qdb_table_exists($pdo2, 'client_answer_submission')); + qdb_discard($tmp2, $lock2); + + $this->assertFileExists(QDB_PATH); + } +} diff --git a/tests/Integration/DownloadsTest.php b/tests/Integration/DownloadsTest.php new file mode 100644 index 0000000..35db2f4 --- /dev/null +++ b/tests/Integration/DownloadsTest.php @@ -0,0 +1,50 @@ +api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'translations', null, ['exportBundle' => '1']); + $this->assertRawOk($res, 'application/json'); + $decoded = json_decode($res['body'], true); + $this->assertIsArray($decoded); + } + + public function testExportQuestionnairesBundle(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::SUPERVISOR_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'export', null, ['bundle' => '1']); + $this->assertRawOk($res, 'application/json'); + $decoded = json_decode($res['body'], true); + $this->assertIsArray($decoded); + } + + public function testExportAllZipAsAdmin(): void + { + if (!class_exists('ZipArchive')) { + $this->markTestSkipped('php-zip extension not installed'); + } + $this->submitFixtureQuestionnaire(); + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'export', null, ['exportAll' => '1']); + $this->assertRawOk($res, 'application/zip'); + $this->assertStringStartsWith('PK', $res['body']); + } +} diff --git a/tests/Integration/EditorCrudTest.php b/tests/Integration/EditorCrudTest.php new file mode 100644 index 0000000..bc007ec --- /dev/null +++ b/tests/Integration/EditorCrudTest.php @@ -0,0 +1,119 @@ +api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + + $create = $this->api()->withToken($token, 'POST', 'answer_options', [ + 'questionID' => $f->questionId, + 'optionKey' => 'temp_opt', + 'defaultText' => 'Temporary', + 'points' => 1, + ]); + $this->assertApiOk($create); + $optId = $create['data']['answerOption']['answerOptionID'] ?? ''; + $this->assertNotSame('', $optId); + + $update = $this->api()->withToken($token, 'PUT', 'answer_options', [ + 'answerOptionID' => $optId, + 'optionKey' => 'temp_opt', + 'defaultText' => 'Temporary updated', + 'points' => 5, + ]); + $this->assertApiOk($update); + $this->assertSame(5, $update['data']['answerOption']['points'] ?? 0); + + $del = $this->api()->withToken($token, 'DELETE', 'answer_options', [ + 'answerOptionID' => $optId, + ]); + $this->assertApiOk($del); + + $list = $this->api()->withToken($token, 'GET', 'answer_options', null, [ + 'questionID' => $f->questionId, + ]); + $this->assertApiOk($list); + $ids = array_column($list['data']['answerOptions'], 'answerOptionID'); + $this->assertNotContains($optId, $ids); + } + + public function testAdminUpdatesAndDeletesQuestion(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + + $create = $this->api()->withToken($token, 'POST', 'questions', [ + 'questionnaireID' => $f->questionnaireId, + 'questionKey' => 'disposable_q', + 'defaultText' => 'Disposable', + 'type' => 'free_text_question', + 'isRequired' => 0, + 'configJson' => '{}', + ]); + $this->assertApiOk($create); + $qid = $create['data']['question']['questionID'] ?? ''; + $this->assertNotSame('', $qid); + + $update = $this->api()->withToken($token, 'PUT', 'questions', [ + 'questionID' => $qid, + 'defaultText' => 'Disposable updated', + 'questionKey' => 'disposable_q', + ]); + $this->assertApiOk($update); + $this->assertSame('Disposable updated', $update['data']['question']['defaultText'] ?? ''); + + $del = $this->api()->withToken($token, 'DELETE', 'questions', [ + 'questionID' => $qid, + ]); + $this->assertApiOk($del); + + $list = $this->api()->withToken($token, 'GET', 'questions', null, [ + 'questionnaireID' => $f->questionnaireId, + ]); + $ids = array_column($list['data']['questions'], 'questionID'); + $this->assertNotContains($qid, $ids); + } + + public function testAdminReordersQuestions(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + + $before = $this->api()->withToken($token, 'GET', 'questions', null, [ + 'questionnaireID' => $f->questionnaireId, + ]); + $this->assertApiOk($before); + $ids = array_column($before['data']['questions'], 'questionID'); + $reversed = array_reverse($ids); + + $patch = $this->api()->withToken($token, 'PATCH', 'questions', [ + 'questionnaireID' => $f->questionnaireId, + 'order' => $reversed, + ]); + $this->assertApiOk($patch); + + $after = $this->api()->withToken($token, 'GET', 'questions', null, [ + 'questionnaireID' => $f->questionnaireId, + ]); + $afterIds = array_column($after['data']['questions'], 'questionID'); + $this->assertSame($reversed, $afterIds); + } +} diff --git a/tests/Integration/ExportExtendedTest.php b/tests/Integration/ExportExtendedTest.php new file mode 100644 index 0000000..708a55d --- /dev/null +++ b/tests/Integration/ExportExtendedTest.php @@ -0,0 +1,116 @@ +api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $f = $this->fixture(); + $this->assertApiOk($this->submitFixtureQuestionnaire()); + $this->submitQuestionnaireAs($login['token'], $f->questionnaireId, $f->clientCode, [ + ['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes'], + ]); + + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'export', null, [ + 'questionnaireID' => $f->questionnaireId, + 'allVersions' => '1', + ]); + $this->assertRawOk($res, 'text/csv'); + $this->assertStringContainsString($f->clientCode, $res['body']); + $lines = array_filter(explode("\n", trim($res['body']))); + $this->assertGreaterThanOrEqual(3, count($lines), 'header plus at least two data rows'); + } + + public function testExportAllZipContainsCsvForSubmittedQuestionnaire(): void + { + if (!class_exists(ZipArchive::class)) { + $this->markTestSkipped('php-zip extension not installed'); + } + + $this->submitFixtureQuestionnaire(); + $f = $this->fixture(); + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + + $res = $this->api()->withToken($token, 'GET', 'export', null, ['exportAll' => '1']); + $this->assertRawOk($res, 'application/zip'); + + $zipPath = sys_get_temp_dir() . '/qdb_test_export_' . bin2hex(random_bytes(4)) . '.zip'; + file_put_contents($zipPath, $res['body']); + $zip = new ZipArchive(); + $this->assertTrue($zip->open($zipPath) === true); + $this->assertGreaterThanOrEqual(1, $zip->numFiles); + $foundCsv = false; + for ($i = 0; $i < $zip->numFiles; $i++) { + $name = $zip->getNameIndex($i); + if (is_string($name) && str_ends_with(strtolower($name), '.csv')) { + $csv = $zip->getFromIndex($i); + if (is_string($csv) && str_contains($csv, $f->clientCode)) { + $foundCsv = true; + break; + } + } + } + $zip->close(); + @unlink($zipPath); + $this->assertTrue($foundCsv, 'ZIP should contain a CSV with submitted client code'); + } + + public function testCoachForbiddenOnPerQuestionnaireCsvExport(): void + { + $this->submitFixtureQuestionnaire(); + $token = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + $res = $this->api()->withMobileToken($token, 'GET', 'export', null, [ + 'questionnaireID' => $f->questionnaireId, + ]); + $this->assertApiError($res, 'FORBIDDEN', 403); + } + + public function testExportAllWithAllVersionsZip(): void + { + if (!class_exists(\ZipArchive::class)) { + $this->markTestSkipped('php-zip extension not installed'); + } + $login = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $f = $this->fixture(); + $this->submitFixtureQuestionnaire(); + $this->submitQuestionnaireAs($login['token'], $f->questionnaireId, $f->clientCode, [ + ['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes'], + ]); + + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'export', null, [ + 'exportAll' => '1', + 'allVersions' => '1', + ]); + $this->assertRawOk($res, 'application/zip'); + $this->assertStringStartsWith('PK', $res['body']); + } +} diff --git a/tests/Integration/ExportTest.php b/tests/Integration/ExportTest.php new file mode 100644 index 0000000..d03852b --- /dev/null +++ b/tests/Integration/ExportTest.php @@ -0,0 +1,50 @@ +api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'export'); + $this->assertApiError($res, 'MISSING_PARAM', 400); + } + + public function testExportAllZipRequiresAdminRole(): void + { + if (!class_exists('ZipArchive')) { + $this->markTestSkipped('php-zip extension not installed'); + } + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::SUPERVISOR_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'export', null, ['exportAll' => '1']); + $this->assertApiError($res, 'FORBIDDEN', 403); + } + + public function testExportCsvForQuestionnaire(): void + { + $this->submitFixtureQuestionnaire(); + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + + $res = $this->api()->withToken($token, 'GET', 'export', null, [ + 'questionnaireID' => $f->questionnaireId, + ]); + $this->assertRawOk($res, 'text/csv'); + $this->assertStringContainsString($f->clientCode, $res['body']); + } +} diff --git a/tests/Integration/QuestionnaireEditingTest.php b/tests/Integration/QuestionnaireEditingTest.php new file mode 100644 index 0000000..238df1e --- /dev/null +++ b/tests/Integration/QuestionnaireEditingTest.php @@ -0,0 +1,57 @@ +api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + $res = $this->api()->withToken($token, 'POST', 'questions', [ + 'questionnaireID' => $f->questionnaireId, + 'questionKey' => 'extra_q', + 'defaultText' => 'Extra question text', + 'type' => 'free_text', + 'isRequired' => 0, + 'configJson' => '{}', + ]); + $this->assertApiOk($res); + $this->assertSame('extra_q', $res['data']['question']['questionKey'] ?? ''); + } + + public function testAdminCreatesAnswerOption(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + $res = $this->api()->withToken($token, 'POST', 'answer_options', [ + 'questionID' => $f->questionId, + 'optionKey' => 'no_option', + 'defaultText' => 'No', + 'points' => 0, + ]); + $this->assertApiOk($res); + $this->assertSame('no_option', $res['data']['answerOption']['optionKey'] ?? ''); + } + + public function testAnswerOptionsRequireQuestionId(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'answer_options'); + $this->assertApiError($res, 'MISSING_PARAM', 400); + } +} diff --git a/tests/Integration/QuestionnaireImportExportTest.php b/tests/Integration/QuestionnaireImportExportTest.php new file mode 100644 index 0000000..833c58b --- /dev/null +++ b/tests/Integration/QuestionnaireImportExportTest.php @@ -0,0 +1,76 @@ +api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + + $export = $this->api()->withToken($token, 'GET', 'export', null, ['bundle' => '1']); + $this->assertRawOk($export, 'application/json'); + $bundle = json_decode($export['body'], true, 512, JSON_THROW_ON_ERROR); + $this->assertGreaterThanOrEqual(1, $bundle['questionnaireCount'] ?? 0); + + $del = $this->api()->withToken($token, 'DELETE', 'questionnaires', [ + 'questionnaireID' => $f->questionnaireId, + ]); + $this->assertApiOk($del); + + $list = $this->api()->withToken($token, 'GET', 'questionnaires'); + $ids = array_column($list['data']['questionnaires'], 'questionnaireID'); + $this->assertNotContains($f->questionnaireId, $ids); + + $import = $this->api()->withToken($token, 'POST', 'questionnaires', [ + 'action' => 'importBundle', + 'bundle' => $bundle, + 'replaceIfExists' => true, + ]); + $this->assertApiOk($import); + $this->assertGreaterThanOrEqual(1, $import['data']['imported'] ?? 0); + + $questions = $this->api()->withToken($token, 'GET', 'questions', null, [ + 'questionnaireID' => $f->questionnaireId, + ]); + $this->assertApiOk($questions); + $qIds = array_column($questions['data']['questions'], 'questionID'); + $this->assertContains($f->questionId, $qIds); + $this->assertContains($f->freeTextQuestionId, $qIds); + } + + public function testAdminCreatesAndUpdatesQuestionnaire(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + + $create = $this->api()->withToken($token, 'POST', 'questionnaires', [ + 'name' => 'Imported via API', + 'version' => '2', + 'state' => 'draft', + ]); + $this->assertApiOk($create); + $newId = $create['data']['questionnaire']['questionnaireID'] ?? ''; + $this->assertNotSame('', $newId); + + $update = $this->api()->withToken($token, 'PUT', 'questionnaires', [ + 'questionnaireID' => $newId, + 'name' => 'Renamed questionnaire', + 'state' => 'active', + ]); + $this->assertApiOk($update); + $this->assertSame('Renamed questionnaire', $update['data']['questionnaire']['name'] ?? ''); + $this->assertSame('active', $update['data']['questionnaire']['state'] ?? ''); + } +} diff --git a/tests/Integration/RbacIntegrationTest.php b/tests/Integration/RbacIntegrationTest.php new file mode 100644 index 0000000..479d76c --- /dev/null +++ b/tests/Integration/RbacIntegrationTest.php @@ -0,0 +1,85 @@ +api()->loginWebAndGetToken( + DatabaseSeeder::SUPERVISOR_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'POST', 'users', [ + 'username' => 'new_supervisor', + 'password' => 'Secret1!', + 'role' => 'supervisor', + 'location' => 'Elsewhere', + ]); + $this->assertApiError($res, 'FORBIDDEN', 403); + } + + public function testSupervisorCannotAssignClientToForeignCoach(): void + { + [$pdo, $tmpDb, $lockFp] = qdb_open(true); + $foreignSupervisorId = 'sv-foreign-' . bin2hex(random_bytes(4)); + $foreignCoachId = 'coach-foreign-' . bin2hex(random_bytes(4)); + $pdo->prepare('INSERT INTO supervisor (supervisorID, username, location) VALUES (:id, :u, :loc)') + ->execute([':id' => $foreignSupervisorId, ':u' => 'foreign_sv', ':loc' => 'Region B']); + $pdo->prepare('INSERT INTO coach (coachID, supervisorID, username) VALUES (:id, :sid, :u)') + ->execute([':id' => $foreignCoachId, ':sid' => $foreignSupervisorId, ':u' => 'foreign_coach']); + qdb_save($tmpDb, $lockFp); + + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::SUPERVISOR_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'POST', 'clients', [ + 'clientCode' => 'CLIENT-FOREIGN-001', + 'coachID' => $foreignCoachId, + ]); + $this->assertApiError($res, 'NOT_FOUND', 404); + } + + public function testCoachMobileTokenRejectedOnWebSettings(): void + { + $token = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'settings'); + $this->assertApiError($res, 'FORBIDDEN', 403); + } + + public function testCoachCannotCreateAnswerOptionOnWeb(): void + { + $token = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'POST', 'answer_options', [ + 'questionID' => $this->fixture()->questionId, + 'optionKey' => 'blocked', + 'defaultText' => 'Blocked', + ]); + $this->assertApiError($res, 'FORBIDDEN', 403); + } + + public function testSupervisorCannotReassignCoach(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::SUPERVISOR_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'PUT', 'users', [ + 'userID' => $this->fixture()->coachUserId, + 'supervisorID' => $this->fixture()->supervisorId, + ]); + $this->assertApiError($res, 'FORBIDDEN', 403); + } +} diff --git a/tests/Integration/ResultsAnalyticsTest.php b/tests/Integration/ResultsAnalyticsTest.php new file mode 100644 index 0000000..e14484e --- /dev/null +++ b/tests/Integration/ResultsAnalyticsTest.php @@ -0,0 +1,86 @@ +submitFixtureQuestionnaire(); + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + + $res = $this->api()->withToken($token, 'GET', 'results', null, [ + 'questionnaireID' => $f->questionnaireId, + ]); + $this->assertApiOk($res); + $byCode = []; + foreach ($res['data']['clients'] as $row) { + $byCode[$row['clientCode']] = $row; + } + $this->assertArrayHasKey($f->clientCode, $byCode); + $this->assertSame('completed', $byCode[$f->clientCode]['status']); + $this->assertNotNull($byCode[$f->clientCode]['completedAt']); + } + + public function testAnalyticsOverviewForSupervisor(): void + { + $this->submitFixtureQuestionnaire(); + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::SUPERVISOR_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + + $res = $this->api()->withToken($token, 'GET', 'analytics', null, ['overview' => '1']); + $this->assertApiOk($res); + $this->assertArrayHasKey('clientCount', $res['data']); + $this->assertArrayHasKey('questionnaires', $res['data']); + $this->assertGreaterThan(0, $res['data']['clientCount']); + } + + public function testStaleClientsList(): void + { + $this->submitFixtureQuestionnaire(); + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::SUPERVISOR_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'analytics', null, ['staleClients' => '1']); + $this->assertApiOk($res); + $this->assertArrayHasKey('clients', $res['data']); + } + + public function testFollowUpNoteRoundTrip(): void + { + $this->submitFixtureQuestionnaire(); + $token = $this->adminToken(); + $code = $this->fixture()->clientCode; + $note = 'Call back next week'; + $save = $this->api()->withToken($token, 'PUT', 'analytics', [ + 'clientCode' => $code, + 'note' => $note, + ]); + $this->assertApiOk($save); + $this->assertSame($note, $save['data']['note']); + + $stale = $this->api()->withToken($token, 'GET', 'analytics', null, ['staleClients' => '1']); + $this->assertApiOk($stale); + $match = null; + foreach ($stale['data']['clients'] as $row) { + if (($row['clientCode'] ?? '') === $code) { + $match = $row; + break; + } + } + $this->assertIsArray($match); + $this->assertSame($note, $match['note']); + } +} diff --git a/tests/Integration/SecurityPathsTest.php b/tests/Integration/SecurityPathsTest.php new file mode 100644 index 0000000..61217a5 --- /dev/null +++ b/tests/Integration/SecurityPathsTest.php @@ -0,0 +1,90 @@ +fixture(); + [$pdo, $tmpDb, $lockFp] = qdb_open(true); + $pdo->prepare( + 'INSERT INTO session (token, userID, role, entityID, createdAt, expiresAt, temp) + VALUES (:t, :uid, :role, :eid, :ca, :ea, 0)' + )->execute([ + ':t' => token_storage_key($token), + ':uid' => $f->adminUserId, + ':role' => 'admin', + ':eid' => 'ent', + ':ca' => time() - 7200, + ':ea' => time() - 3600, + ]); + qdb_save($tmpDb, $lockFp); + + $res = $this->api()->withToken($token, 'GET', 'session'); + $this->assertApiError($res, 'UNAUTHORIZED', 401); + } + + public function testInvalidBearerRejected(): void + { + $res = $this->api()->request('GET', 'session', null, [ + 'Authorization' => 'Bearer not-a-valid-session-token', + 'X-QDB-Client' => 'web', + ]); + $this->assertApiError($res, 'UNAUTHORIZED', 401); + } + + public function testAppSubmitRequiresEncryptedBody(): void + { + $login = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $f = $this->fixture(); + $res = $this->api()->withMobileToken($login['token'], 'POST', 'app_questionnaires', [ + 'questionnaireID' => $f->questionnaireId, + 'clientCode' => $f->clientCode, + 'answers' => [], + ]); + $this->assertApiError($res, 'ENCRYPTION_REQUIRED', 400); + } + + public function testAppSubmitValidationFailure(): void + { + $login = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $f = $this->fixture(); + $res = $this->api()->encryptedPost($login['token'], 'app_questionnaires', [ + 'questionnaireID' => $f->questionnaireId, + 'clientCode' => $f->clientCode, + 'answers' => [], + ]); + $this->assertApiError($res, 'VALIDATION_FAILED', 400); + $this->assertNotEmpty($res['error']['details']['errors'] ?? []); + } + + public function testCoachCannotSubmitForUnknownClient(): void + { + $login = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $f = $this->fixture(); + $res = $this->api()->encryptedPost($login['token'], 'app_questionnaires', [ + 'questionnaireID' => $f->questionnaireId, + 'clientCode' => 'CLIENT-DOES-NOT-EXIST', + 'answers' => [ + ['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes'], + ], + ]); + $this->assertApiError($res, 'NOT_FOUND', 404); + } +} diff --git a/tests/Integration/SessionLogoutTest.php b/tests/Integration/SessionLogoutTest.php new file mode 100644 index 0000000..75de33f --- /dev/null +++ b/tests/Integration/SessionLogoutTest.php @@ -0,0 +1,40 @@ +api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + ); + $res = $this->api()->withToken($login['token'], 'GET', 'session'); + $this->assertApiOk($res); + $this->assertTrue($res['data']['valid']); + } + + public function testSessionRejectsMissingToken(): void + { + $res = $this->api()->request('GET', 'session'); + $this->assertApiError($res, 'UNAUTHORIZED', 401); + } + + public function testLogoutRevokesToken(): void + { + $login = $this->api()->loginWebAndGetToken( + DatabaseSeeder::SUPERVISOR_USERNAME, + DatabaseSeeder::PASSWORD + ); + $del = $this->api()->withToken($login['token'], 'DELETE', 'logout'); + $this->assertApiOk($del); + $check = $this->api()->withToken($login['token'], 'GET', 'session'); + $this->assertApiError($check, 'UNAUTHORIZED', 401); + } +} diff --git a/tests/Integration/SettingsApiTest.php b/tests/Integration/SettingsApiTest.php new file mode 100644 index 0000000..5a6bad8 --- /dev/null +++ b/tests/Integration/SettingsApiTest.php @@ -0,0 +1,66 @@ +api()->withToken($this->adminToken(), 'GET', 'settings'); + $this->assertApiOk($res); + $this->assertArrayHasKey('settings', $res['data']); + $this->assertArrayHasKey('activeSessions', $res['data']); + } + + public function testSupervisorForbidden(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::SUPERVISOR_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'settings'); + $this->assertApiError($res, 'FORBIDDEN', 403); + } + + public function testUpdateSettings(): void + { + $token = $this->adminToken(); + $res = $this->api()->withToken($token, 'PUT', 'settings', [ + 'login_max_attempts' => 8, + 'login_window_seconds' => 1200, + 'login_lockout_seconds' => 1200, + 'session_ttl_seconds' => 86400, + 'temp_session_ttl_seconds' => 900, + ]); + $this->assertApiOk($res); + $this->assertSame(8, $res['data']['settings']['login_max_attempts']); + } + + public function testRevokeAllSessionsRequiresPhrase(): void + { + $res = $this->api()->withToken($this->adminToken(), 'POST', 'settings', [ + 'action' => 'revokeAllSessions', + 'confirmPhrase' => 'wrong', + ]); + $this->assertApiError($res, 'CONFIRMATION_REQUIRED', 400); + } + + public function testRevokeAllSessions(): void + { + $admin = $this->adminToken(); + $this->api()->loginWebAndGetToken(DatabaseSeeder::SUPERVISOR_USERNAME, DatabaseSeeder::PASSWORD); + $res = $this->api()->withToken($admin, 'POST', 'settings', [ + 'action' => 'revokeAllSessions', + 'confirmPhrase' => 'REVOKE ALL SESSIONS', + ]); + $this->assertApiOk($res); + $this->assertGreaterThanOrEqual(1, $res['data']['revokedSessions']); + $check = $this->api()->withToken($admin, 'GET', 'session'); + $this->assertApiError($check, 'UNAUTHORIZED', 401); + } +} diff --git a/tests/Integration/TokenTest.php b/tests/Integration/TokenTest.php new file mode 100644 index 0000000..a7efcf4 --- /dev/null +++ b/tests/Integration/TokenTest.php @@ -0,0 +1,65 @@ +fixture(); + token_add($token, 3600, [ + 'userID' => $f->adminUserId, + 'role' => 'admin', + 'entityID' => 'ent', + ]); + $rec = token_get_record($token); + $this->assertNotNull($rec); + $this->assertSame('admin', $rec['role']); + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $stored = $pdo->query('SELECT token FROM session LIMIT 1')->fetchColumn(); + qdb_discard($tmpDb, $lockFp); + $this->assertSame(token_storage_key($token), $stored); + $this->assertNotSame($token, $stored); + + token_revoke($token); + $this->assertNull(token_get_record($token)); + } + + public function testRevokeAllForUser(): void + { + $f = $this->fixture(); + $t1 = bin2hex(random_bytes(16)); + $t2 = bin2hex(random_bytes(16)); + token_add($t1, 3600, ['userID' => $f->supervisorUserId, 'role' => 'supervisor', 'entityID' => 'x']); + token_add($t2, 3600, ['userID' => $f->supervisorUserId, 'role' => 'supervisor', 'entityID' => 'x']); + $n = token_revoke_all_for_user($f->supervisorUserId); + $this->assertGreaterThanOrEqual(2, $n); + $this->assertNull(token_get_record($t1)); + } + + public function testTokenLookupRefreshesExpiry(): void + { + $token = bin2hex(random_bytes(32)); + $f = $this->fixture(); + token_add($token, 3600, [ + 'userID' => $f->adminUserId, + 'role' => 'admin', + 'entityID' => 'ent', + ]); + + [$pdo, $tmpDb, $lockFp] = qdb_open(true); + $oldExpiry = time() + 60; + $pdo->prepare('UPDATE session SET expiresAt = :exp WHERE token = :t') + ->execute([':exp' => $oldExpiry, ':t' => token_storage_key($token)]); + qdb_save($tmpDb, $lockFp); + + $rec = token_get_record($token); + $this->assertNotNull($rec); + $this->assertGreaterThan($oldExpiry + 3000, $rec['exp']); + } +} diff --git a/tests/Integration/TranslationsImportExportTest.php b/tests/Integration/TranslationsImportExportTest.php new file mode 100644 index 0000000..c9fbd05 --- /dev/null +++ b/tests/Integration/TranslationsImportExportTest.php @@ -0,0 +1,83 @@ +api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + + $this->api()->withToken($token, 'PUT', 'translations', [ + 'type' => 'language', + 'languageCode' => 'es', + 'name' => 'Spanish', + ]); + $this->api()->withToken($token, 'PUT', 'translations', [ + 'type' => 'question', + 'id' => $f->questionId, + 'languageCode' => 'es', + 'text' => 'Consentimiento', + ]); + + $export = $this->api()->withToken($token, 'GET', 'translations', null, ['exportBundle' => '1']); + $this->assertRawOk($export, 'application/json'); + $bundle = json_decode($export['body'], true, 512, JSON_THROW_ON_ERROR); + + $this->api()->withToken($token, 'DELETE', 'translations', [ + 'type' => 'question', + 'id' => $f->questionId, + 'languageCode' => 'es', + ]); + + $import = $this->api()->withToken($token, 'POST', 'translations', [ + 'action' => 'importBundle', + 'bundle' => $bundle, + ]); + $this->assertApiOk($import); + + $get = $this->api()->withToken($token, 'GET', 'translations', null, [ + 'type' => 'question', + 'id' => $f->questionId, + ]); + $es = null; + foreach ($get['data']['translations'] as $row) { + if ($row['languageCode'] === 'es') { + $es = $row['text']; + } + } + $this->assertSame('Consentimiento', $es); + } + + public function testDeleteTranslationLanguage(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + + $this->api()->withToken($token, 'PUT', 'translations', [ + 'type' => 'language', + 'languageCode' => 'it', + 'name' => 'Italian', + ]); + $del = $this->api()->withToken($token, 'DELETE', 'translations', [ + 'type' => 'language', + 'languageCode' => 'it', + ]); + $this->assertApiOk($del); + + $langs = $this->api()->withToken($token, 'GET', 'translations', null, ['languages' => '1']); + $codes = array_column($langs['data']['languages'], 'languageCode'); + $this->assertNotContains('it', $codes); + } +} diff --git a/tests/Integration/TranslationsWriteTest.php b/tests/Integration/TranslationsWriteTest.php new file mode 100644 index 0000000..a9dc9bd --- /dev/null +++ b/tests/Integration/TranslationsWriteTest.php @@ -0,0 +1,104 @@ +api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + + $save = $this->api()->withToken($token, 'PUT', 'translations', [ + 'type' => 'language', + 'languageCode' => 'en', + 'name' => 'English', + ]); + $this->assertApiOk($save); + + $put = $this->api()->withToken($token, 'PUT', 'translations', [ + 'type' => 'question', + 'id' => $f->questionId, + 'languageCode' => 'en', + 'text' => 'Consent in English?', + ]); + $this->assertApiOk($put); + + $get = $this->api()->withToken($token, 'GET', 'translations', null, [ + 'type' => 'question', + 'id' => $f->questionId, + ]); + $this->assertApiOk($get); + $texts = []; + foreach ($get['data']['translations'] as $row) { + $texts[$row['languageCode']] = $row['text']; + } + $this->assertArrayHasKey('en', $texts); + $this->assertSame('Consent in English?', $texts['en']); + } + + public function testPutAnswerOptionTranslation(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + + $this->api()->withToken($token, 'PUT', 'translations', [ + 'type' => 'language', + 'languageCode' => 'fr', + 'name' => 'French', + ]); + + $put = $this->api()->withToken($token, 'PUT', 'translations', [ + 'type' => 'answer_option', + 'id' => $f->optionId, + 'languageCode' => 'fr', + 'text' => 'Oui', + ]); + $this->assertApiOk($put); + + $get = $this->api()->withToken($token, 'GET', 'translations', null, [ + 'type' => 'answer_option', + 'id' => $f->optionId, + ]); + $this->assertApiOk($get); + $fr = null; + foreach ($get['data']['translations'] as $row) { + if ($row['languageCode'] === 'fr') { + $fr = $row['text']; + } + } + $this->assertSame('Oui', $fr); + } + + public function testAllTranslationsIncludeGlassSymptomKeys(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + + $res = $this->api()->withToken($token, 'GET', 'translations', null, ['all' => '1']); + $this->assertApiOk($res); + + $stringKeys = []; + foreach ($res['data']['entries'] as $entry) { + if (($entry['type'] ?? '') === 'string') { + $stringKeys[] = $entry['key'] ?? ''; + } + } + $this->assertContains($f->glassSymptomKey, $stringKeys); + $this->assertContains('fatigue', $stringKeys); + } +} diff --git a/tests/Integration/UsersAdminTest.php b/tests/Integration/UsersAdminTest.php new file mode 100644 index 0000000..41ffa26 --- /dev/null +++ b/tests/Integration/UsersAdminTest.php @@ -0,0 +1,179 @@ +api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + $username = 'coach_new_' . bin2hex(random_bytes(2)); + + $res = $this->api()->withToken($token, 'POST', 'users', [ + 'username' => $username, + 'password' => 'CoachPass1!', + 'role' => 'coach', + 'supervisorID' => $f->supervisorId, + ]); + $this->assertApiOk($res); + $this->assertSame($username, $res['data']['username']); + $this->assertSame('coach', $res['data']['role']); + $this->assertSame($f->supervisorId, $res['data']['supervisorID']); + + $login = $this->api()->loginMobile($username, 'CoachPass1!'); + $this->assertApiOk($login); + } + + public function testSupervisorCreatesCoachUnderSelf(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::SUPERVISOR_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $username = 'coach_sv_' . bin2hex(random_bytes(2)); + + $res = $this->api()->withToken($token, 'POST', 'users', [ + 'username' => $username, + 'password' => 'CoachPass2!', + 'role' => 'coach', + ]); + $this->assertApiOk($res); + $this->assertSame($this->fixture()->supervisorId, $res['data']['supervisorID']); + } + + public function testAdminResetsCoachPassword(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + + $res = $this->api()->withToken($token, 'PUT', 'users', [ + 'userID' => $f->coachUserId, + 'newPassword' => 'ResetPass3!', + 'mustChangePassword' => 0, + ]); + $this->assertApiOk($res); + + $oldLogin = $this->api()->loginMobile(DatabaseSeeder::COACH_USERNAME, DatabaseSeeder::PASSWORD); + $this->assertApiError($oldLogin, 'INVALID_CREDENTIALS', 401); + + $newLogin = $this->api()->loginMobile(DatabaseSeeder::COACH_USERNAME, 'ResetPass3!'); + $this->assertApiOk($newLogin); + } + + public function testAdminDeletesCreatedCoach(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + $username = 'coach_del_' . bin2hex(random_bytes(2)); + + $create = $this->api()->withToken($token, 'POST', 'users', [ + 'username' => $username, + 'password' => 'CoachPass4!', + 'role' => 'coach', + 'supervisorID' => $f->supervisorId, + ]); + $this->assertApiOk($create); + $userId = $create['data']['userID']; + + $del = $this->api()->withToken($token, 'DELETE', 'users', ['userID' => $userId]); + $this->assertApiOk($del); + + $list = $this->api()->withToken($token, 'GET', 'users'); + $ids = array_column($list['data']['users'], 'userID'); + $this->assertNotContains($userId, $ids); + } + + public function testAdminCreatesSupervisor(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $username = 'supervisor_new_' . bin2hex(random_bytes(2)); + $res = $this->api()->withToken($token, 'POST', 'users', [ + 'username' => $username, + 'password' => 'SuperPass1!', + 'role' => 'supervisor', + 'location' => 'Region B', + ]); + $this->assertApiOk($res); + $this->assertSame('supervisor', $res['data']['role']); + } + + public function testAdminReassignsCoachToSupervisor(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + + $sv = $this->api()->withToken($token, 'POST', 'users', [ + 'username' => 'supervisor_reassign_' . bin2hex(random_bytes(2)), + 'password' => 'SuperPass2!', + 'role' => 'supervisor', + 'location' => 'Region C', + ]); + $this->assertApiOk($sv); + $newSvId = $sv['data']['entityID']; + + $res = $this->api()->withToken($token, 'PUT', 'users', [ + 'userID' => $f->coachUserId, + 'supervisorID' => $newSvId, + ]); + $this->assertApiOk($res); + $this->assertSame($newSvId, $res['data']['supervisorID']); + + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $stmt = $pdo->prepare('SELECT supervisorID FROM coach WHERE coachID = :cid'); + $stmt->execute([':cid' => $f->coachId]); + $this->assertSame($newSvId, $stmt->fetchColumn()); + qdb_discard($tmpDb, $lockFp); + } + + public function testAdminReassignsClientViaAssignments(): void + { + $token = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $f = $this->fixture(); + + $newCoach = $this->api()->withToken($token, 'POST', 'users', [ + 'username' => 'coach_assign_' . bin2hex(random_bytes(2)), + 'password' => 'CoachPass5!', + 'role' => 'coach', + 'supervisorID' => $f->supervisorId, + ]); + $this->assertApiOk($newCoach); + $newCoachId = $newCoach['data']['entityID']; + + $assign = $this->api()->withToken($token, 'POST', 'assignments', [ + 'coachID' => $newCoachId, + 'clientCodes' => [$f->clientCode], + ]); + $this->assertApiOk($assign); + $this->assertSame(1, $assign['data']['assigned']); + + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $stmt = $pdo->prepare('SELECT coachID FROM client WHERE clientCode = :cc'); + $stmt->execute([':cc' => $f->clientCode]); + $this->assertSame($newCoachId, $stmt->fetchColumn()); + qdb_discard($tmpDb, $lockFp); + } +} diff --git a/tests/Integration/UsersTest.php b/tests/Integration/UsersTest.php new file mode 100644 index 0000000..24cca07 --- /dev/null +++ b/tests/Integration/UsersTest.php @@ -0,0 +1,55 @@ +api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + $res = $this->api()->withToken($token, 'GET', 'users'); + $this->assertApiOk($res); + $this->assertGreaterThanOrEqual(3, count($res['data']['users'])); + } + + public function testRevokeUserSessions(): void + { + $admin = $this->adminToken(); + $coach = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + + $f = $this->fixture(); + $res = $this->api()->withToken($admin, 'PUT', 'users', [ + 'action' => 'revokeSessions', + 'userID' => $f->coachUserId, + ]); + $this->assertApiOk($res); + $this->assertGreaterThanOrEqual(1, $res['data']['revokedSessions']); + + $dead = $this->api()->withMobileToken($coach['token'], 'GET', 'session'); + $this->assertApiError($dead, 'UNAUTHORIZED', 401); + } + + public function testCannotRevokeOwnSessions(): void + { + $login = $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + ); + $res = $this->api()->withToken($login['token'], 'PUT', 'users', [ + 'action' => 'revokeSessions', + 'userID' => $this->fixture()->adminUserId, + ]); + $this->assertApiError($res, 'FORBIDDEN', 400); + } +} diff --git a/tests/Support/ApiClient.php b/tests/Support/ApiClient.php new file mode 100644 index 0000000..f133240 --- /dev/null +++ b/tests/Support/ApiClient.php @@ -0,0 +1,181 @@ + $headers + * @return array{ok: bool, status: int, data?: mixed, error?: array} + */ + public function request( + string $method, + string $route, + ?array $body = null, + array $headers = [], + array $query = [], + ): array { + qdb_test_reset_http_state(); + + $_SERVER['REQUEST_METHOD'] = strtoupper($method); + $queryString = $query !== [] ? '?' . http_build_query($query) : ''; + $_SERVER['REQUEST_URI'] = '/api/' . ltrim($route, '/') . $queryString; + $_SERVER['SCRIPT_NAME'] = '/api/index.php'; + $_GET = $query; + if ($queryString !== '') { + parse_str(ltrim($queryString, '?'), $_GET); + } + + unset($_SERVER['HTTP_AUTHORIZATION'], $_SERVER['Authorization']); + unset($_SERVER['HTTP_X_QDB_CLIENT']); + + foreach ($headers as $name => $value) { + $key = 'HTTP_' . strtoupper(str_replace('-', '_', $name)); + $_SERVER[$key] = $value; + } + + if ($body !== null) { + qdb_test_set_request_body(json_encode($body, JSON_THROW_ON_ERROR)); + } + + ob_start(); + try { + require $this->apiIndexPath; + ob_end_clean(); + return ['ok' => false, 'status' => 500, 'error' => ['code' => 'NO_RESPONSE', 'message' => 'Handler did not return JSON']]; + } catch (JsonSuccessResponse $e) { + $output = ob_get_clean(); + return [ + 'ok' => true, + 'status' => $e->httpStatus, + 'data' => $e->data, + '_raw' => $output, + ]; + } catch (JsonErrorResponse $e) { + ob_get_clean(); + $err = ['code' => $e->errorCode, 'message' => $e->errorMessage]; + if ($e->details !== null) { + $err['details'] = $e->details; + } + return [ + 'ok' => false, + 'status' => $e->httpStatus, + 'error' => $err, + ]; + } catch (RawHttpResponse $e) { + ob_get_clean(); + return [ + 'ok' => true, + 'status' => $e->httpStatus, + 'raw' => true, + 'body' => $e->body, + 'headers' => $e->headers, + ]; + } catch (\Throwable $e) { + ob_end_clean(); + throw $e; + } + } + + public function loginWeb(string $username, string $password): array + { + return $this->request('POST', 'auth/login', [ + 'username' => $username, + 'password' => $password, + ], ['X-QDB-Client' => 'web']); + } + + public function loginMobile(string $username, string $password): array + { + return $this->request('POST', 'auth/login', [ + 'username' => $username, + 'password' => $password, + ]); + } + + /** + * @return array{token: string, data: array} + */ + public function loginMobileAndGetToken(string $username, string $password): array + { + $res = $this->loginMobile($username, $password); + if (!$res['ok']) { + throw new \RuntimeException('Login failed: ' . ($res['error']['message'] ?? 'unknown')); + } + $token = $res['data']['token'] ?? ''; + if ($token === '') { + throw new \RuntimeException('Login response missing token'); + } + return ['token' => $token, 'data' => $res['data']]; + } + + /** + * @return array{token: string, data: array} + */ + public function loginWebAndGetToken(string $username, string $password): array + { + $res = $this->loginWeb($username, $password); + if (!$res['ok']) { + throw new \RuntimeException('Login failed: ' . ($res['error']['message'] ?? 'unknown')); + } + $token = $res['data']['token'] ?? ''; + if ($token === '') { + throw new \RuntimeException('Login response missing token'); + } + return ['token' => $token, 'data' => $res['data']]; + } + + /** + * @param array $extraHeaders + */ + public function withToken(string $token, string $method, string $route, ?array $body = null, array $query = [], array $extraHeaders = []): array + { + $headers = array_merge([ + 'Authorization' => 'Bearer ' . $token, + 'X-QDB-Client' => 'web', + ], $extraHeaders); + + return $this->request($method, $route, $body, $headers, $query); + } + + /** + * @param array $extraHeaders + */ + public function withMobileToken( + string $token, + string $method, + string $route, + ?array $body = null, + array $query = [], + array $extraHeaders = [], + ): array { + $headers = array_merge(['Authorization' => 'Bearer ' . $token], $extraHeaders); + + return $this->request($method, $route, $body, $headers, $query); + } + + /** + * POST an encrypted payload (Android app API). + * + * @param array $payload + */ + public function encryptedPost(string $token, string $route, array $payload): array + { + $envelope = qdb_sensitive_envelope( + json_encode($payload, JSON_THROW_ON_ERROR), + $token + ); + + return $this->withMobileToken($token, 'POST', $route, $envelope); + } +} diff --git a/tests/Support/DatabaseSeeder.php b/tests/Support/DatabaseSeeder.php new file mode 100644 index 0000000..ee4a45d --- /dev/null +++ b/tests/Support/DatabaseSeeder.php @@ -0,0 +1,233 @@ +prepare('INSERT INTO admin (adminID, username, location) VALUES (:id, :u, :loc)') + ->execute([':id' => $adminId, ':u' => self::ADMIN_USERNAME, ':loc' => 'HQ']); + $pdo->prepare( + 'INSERT INTO users (userID, username, passwordHash, role, entityID, mustChangePassword, createdAt) + VALUES (:uid, :u, :h, :role, :eid, 0, :now)' + )->execute([ + ':uid' => $adminUserId, + ':u' => self::ADMIN_USERNAME, + ':h' => $passwordHash, + ':role' => 'admin', + ':eid' => $adminId, + ':now' => $now, + ]); + + $pdo->prepare('INSERT INTO supervisor (supervisorID, username, location) VALUES (:id, :u, :loc)') + ->execute([':id' => $supervisorId, ':u' => self::SUPERVISOR_USERNAME, ':loc' => 'Region A']); + $pdo->prepare( + 'INSERT INTO users (userID, username, passwordHash, role, entityID, mustChangePassword, createdAt) + VALUES (:uid, :u, :h, :role, :eid, 0, :now)' + )->execute([ + ':uid' => $supervisorUserId, + ':u' => self::SUPERVISOR_USERNAME, + ':h' => $passwordHash, + ':role' => 'supervisor', + ':eid' => $supervisorId, + ':now' => $now, + ]); + + $pdo->prepare('INSERT INTO coach (coachID, supervisorID, username) VALUES (:id, :sid, :u)') + ->execute([':id' => $coachId, ':sid' => $supervisorId, ':u' => self::COACH_USERNAME]); + $pdo->prepare( + 'INSERT INTO users (userID, username, passwordHash, role, entityID, mustChangePassword, createdAt) + VALUES (:uid, :u, :h, :role, :eid, 0, :now)' + )->execute([ + ':uid' => $coachUserId, + ':u' => self::COACH_USERNAME, + ':h' => $passwordHash, + ':role' => 'coach', + ':eid' => $coachId, + ':now' => $now, + ]); + + $pdo->prepare('INSERT INTO client (clientCode, coachID) VALUES (:cc, :cid)') + ->execute([':cc' => 'CLIENT-TEST-001', ':cid' => $coachId]); + + $pdo->prepare( + 'INSERT INTO questionnaire (questionnaireID, name, version, state, orderIndex, showPoints, conditionJson, categoryKey) + VALUES (:id, :n, :v, :s, 1, 0, :cj, :ck)' + )->execute([ + ':id' => $qnId, + ':n' => 'Test questionnaire', + ':v' => '1', + ':s' => 'active', + ':cj' => '{}', + ':ck' => '', + ]); + $pdo->prepare('UPDATE questionnaire SET showPoints = 1 WHERE questionnaireID = :id') + ->execute([':id' => $qnId]); + + $pdo->prepare( + 'INSERT INTO question (questionID, questionnaireID, defaultText, type, orderIndex, isRequired, configJson) + VALUES (:id, :qn, :txt, :type, 0, 1, :cfg)' + )->execute([ + ':id' => $questionId, + ':qn' => $qnId, + ':txt' => 'Consent?', + ':type' => 'single_choice_question', + ':cfg' => json_encode(['questionKey' => 'consent_q'], JSON_THROW_ON_ERROR), + ]); + + $pdo->prepare( + 'INSERT INTO answer_option (answerOptionID, questionID, defaultText, points, orderIndex, nextQuestionId) + VALUES (:id, :qid, :txt, 0, 0, :next)' + )->execute([ + ':next' => '', + ':id' => $optionId, + ':qid' => $questionId, + ':txt' => 'yes', + ]); + + $pdo->prepare( + 'INSERT INTO question (questionID, questionnaireID, defaultText, type, orderIndex, isRequired, configJson) + VALUES (:id, :qn, :txt, :type, 1, 0, :cfg)' + )->execute([ + ':id' => $freeTextQuestionId, + ':qn' => $qnId, + ':txt' => 'Notes', + ':type' => 'free_text_question', + ':cfg' => json_encode(['questionKey' => 'notes_q'], JSON_THROW_ON_ERROR), + ]); + + $glassConfig = json_encode( + ['questionKey' => 'symptoms_q', 'symptoms' => [$glassSymptomKey, 'fatigue']], + JSON_THROW_ON_ERROR + ); + $pdo->prepare( + 'INSERT INTO question (questionID, questionnaireID, defaultText, type, orderIndex, isRequired, configJson) + VALUES (:id, :qn, :txt, :type, 2, 0, :cfg)' + )->execute([ + ':id' => $glassQuestionId, + ':qn' => $qnId, + ':txt' => 'Symptoms', + ':type' => 'glass_scale_question', + ':cfg' => $glassConfig, + ]); + + require_once dirname(__DIR__, 2) . '/lib/settings.php'; + qdb_settings_save_on_pdo($pdo, qdb_settings_defaults()); + + $pdo->prepare( + 'INSERT INTO question (questionID, questionnaireID, defaultText, type, orderIndex, isRequired, configJson) + VALUES (:id, :qn, :txt, :type, 3, 0, :cfg)' + )->execute([ + ':id' => $multiQuestionId, + ':qn' => $qnId, + ':txt' => 'Pick several', + ':type' => 'multi_check_box_question', + ':cfg' => json_encode(['questionKey' => 'multi_q'], JSON_THROW_ON_ERROR), + ]); + $pdo->prepare( + 'INSERT INTO answer_option (answerOptionID, questionID, defaultText, points, orderIndex, nextQuestionId) + VALUES (:id, :qid, :txt, 1, 0, :next)' + )->execute([':next' => '', ':id' => $multiOptionAId, ':qid' => $multiQuestionId, ':txt' => 'opt_a']); + $pdo->prepare( + 'INSERT INTO answer_option (answerOptionID, questionID, defaultText, points, orderIndex, nextQuestionId) + VALUES (:id, :qid, :txt, 2, 1, :next)' + )->execute([':next' => '', ':id' => $multiOptionBId, ':qid' => $multiQuestionId, ':txt' => 'opt_b']); + qdb_upsert_source_translation($pdo, 'answer_option', $multiOptionAId, 'A'); + qdb_upsert_source_translation($pdo, 'answer_option', $multiOptionBId, 'B'); + + $pdo->prepare( + 'INSERT INTO questionnaire (questionnaireID, name, version, state, orderIndex, showPoints, conditionJson, categoryKey) + VALUES (:id, :n, :v, :s, 2, 0, :cj, :ck)' + )->execute([ + ':id' => $draftQuestionnaireId, + ':n' => 'Draft only', + ':v' => '1', + ':s' => 'draft', + ':cj' => '{}', + ':ck' => '', + ]); + + qdb_upsert_source_translation($pdo, 'answer_option', $optionId, 'Ja'); + + return new FixtureIds( + adminUserId: $adminUserId, + supervisorUserId: $supervisorUserId, + coachUserId: $coachUserId, + coachId: $coachId, + supervisorId: $supervisorId, + questionnaireId: $qnId, + questionId: $questionId, + questionShortId: 'q1', + optionId: $optionId, + freeTextQuestionId: $freeTextQuestionId, + freeTextShortId: 'q2', + glassQuestionId: $glassQuestionId, + glassShortId: 'q3', + glassSymptomKey: $glassSymptomKey, + multiCheckQuestionId: $multiQuestionId, + multiCheckShortId: 'q4', + draftQuestionnaireId: $draftQuestionnaireId, + clientCode: 'CLIENT-TEST-001', + ); + } +} + +final class FixtureIds +{ + public function __construct( + public readonly string $adminUserId, + public readonly string $supervisorUserId, + public readonly string $coachUserId, + public readonly string $coachId, + public readonly string $supervisorId, + public readonly string $questionnaireId, + public readonly string $questionId, + public readonly string $questionShortId, + public readonly string $optionId, + public readonly string $freeTextQuestionId, + public readonly string $freeTextShortId, + public readonly string $glassQuestionId, + public readonly string $glassShortId, + public readonly string $glassSymptomKey, + public readonly string $multiCheckQuestionId, + public readonly string $multiCheckShortId, + public readonly string $draftQuestionnaireId, + public readonly string $clientCode, + ) { + } +} diff --git a/tests/Support/JsonErrorResponse.php b/tests/Support/JsonErrorResponse.php new file mode 100644 index 0000000..4048527 --- /dev/null +++ b/tests/Support/JsonErrorResponse.php @@ -0,0 +1,20 @@ +bootstrapDatabase(); + } + + protected function bootstrapDatabase(): void + { + if (defined('QDB_PATH') && is_file(QDB_PATH)) { + @unlink(QDB_PATH); + } + if (defined('QDB_LOCK') && is_file(QDB_LOCK)) { + @unlink(QDB_LOCK); + } + + [$pdo, $tmpDb, $lockFp] = qdb_open(true); + $this->fixtureIds = DatabaseSeeder::seed($pdo); + qdb_save($tmpDb, $lockFp); + } + + protected function api(): ApiClient + { + return self::$api; + } + + protected function fixture(): FixtureIds + { + if ($this->fixtureIds === null) { + $this->fail('Fixture not seeded'); + } + return $this->fixtureIds; + } + + protected function adminToken(): string + { + return $this->api()->loginWebAndGetToken( + DatabaseSeeder::ADMIN_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + } + + protected function supervisorToken(): string + { + return $this->api()->loginWebAndGetToken( + DatabaseSeeder::SUPERVISOR_USERNAME, + DatabaseSeeder::PASSWORD + )['token']; + } + + /** + * @return array{token: string, data: array} + */ + protected function coachMobileLogin(): array + { + return $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + } + + protected function assertApiOk(array $response, string $message = ''): void + { + $this->assertTrue($response['ok'] ?? false, $message . ' ' . json_encode($response['error'] ?? [])); + } + + protected function assertApiError(array $response, string $code, ?int $status = null): void + { + $this->assertFalse($response['ok'] ?? true); + $this->assertSame($code, $response['error']['code'] ?? null); + if ($status !== null) { + $this->assertSame($status, $response['status']); + } + } + + protected function assertRawOk(array $response, string $contentTypePrefix): void + { + $this->assertTrue($response['ok'] ?? false); + $this->assertTrue($response['raw'] ?? false); + $this->assertStringStartsWith($contentTypePrefix, $response['headers']['Content-Type'] ?? ''); + $this->assertNotSame('', $response['body'] ?? ''); + } + + /** + * @return array API response from app submit + */ + protected function submitFixtureQuestionnaire(): array + { + $login = $this->api()->loginMobileAndGetToken( + DatabaseSeeder::COACH_USERNAME, + DatabaseSeeder::PASSWORD + ); + $f = $this->fixture(); + $now = time(); + + return $this->submitQuestionnaireAs( + $login['token'], + $f->questionnaireId, + $f->clientCode, + [ + ['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes'], + ], + $now - 60, + $now + ); + } + + /** + * @param list> $answers + * @return array + */ + protected function submitQuestionnaireAs( + string $token, + string $questionnaireId, + string $clientCode, + array $answers, + ?int $startedAt = null, + ?int $completedAt = null, + ?int $structureRevision = null, + ): array { + $now = time(); + $startedAt ??= $now - 60; + $completedAt ??= $now; + + $payload = [ + 'questionnaireID' => $questionnaireId, + 'clientCode' => $clientCode, + 'answers' => $answers, + 'startedAt' => $startedAt, + 'completedAt' => $completedAt, + ]; + if ($structureRevision !== null) { + $payload['structureRevision'] = $structureRevision; + } + + return $this->api()->encryptedPost($token, 'app_questionnaires', $payload); + } + + protected function submissionVersionCount(string $clientCode, string $questionnaireId): int + { + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $stmt = $pdo->prepare( + 'SELECT COUNT(*) FROM questionnaire_submission WHERE clientCode = :cc AND questionnaireID = :qn' + ); + $stmt->execute([':cc' => $clientCode, ':qn' => $questionnaireId]); + $count = (int)$stmt->fetchColumn(); + qdb_discard($tmpDb, $lockFp); + + return $count; + } +} diff --git a/tests/Support/RawHttpResponse.php b/tests/Support/RawHttpResponse.php new file mode 100644 index 0000000..df965f1 --- /dev/null +++ b/tests/Support/RawHttpResponse.php @@ -0,0 +1,22 @@ + $headers + */ + public function __construct( + public readonly string $body, + public readonly array $headers, + public readonly int $httpStatus = 200, + ) { + parent::__construct('Raw HTTP response', $httpStatus); + } +} diff --git a/tests/Support/TestFilesystem.php b/tests/Support/TestFilesystem.php new file mode 100644 index 0000000..c311037 --- /dev/null +++ b/tests/Support/TestFilesystem.php @@ -0,0 +1,48 @@ +logDir = $_ENV['QDB_API_LOG_DIR'] ?? (QDB_TEST_UPLOADS . '/logs/api'); + if (!is_dir($this->logDir)) { + mkdir($this->logDir, 0775, true); + } + $this->clearLogFiles(); + unset($GLOBALS['qdb_api_log_ctx']); + } + + protected function tearDown(): void + { + $this->clearLogFiles(); + unset($GLOBALS['qdb_api_log_ctx']); + } + + public function testClassifyActivity(): void + { + $this->assertNull(qdb_api_log_classify_activity('OPTIONS', 'web', 'users')); + $this->assertSame('web_change', qdb_api_log_classify_activity('POST', 'web', 'users')); + $this->assertSame('app_change', qdb_api_log_classify_activity('POST', '', 'app_questionnaires')); + $this->assertSame('app_sync', qdb_api_log_classify_activity('GET', '', 'app_questionnaires')); + $this->assertSame('web_export', qdb_api_log_classify_activity('GET', 'web', 'export')); + $this->assertSame('web_change', qdb_api_log_classify_activity('POST', 'web', 'backup')); + $this->assertTrue(qdb_api_log_is_website_download('backup')); + } + + public function testRedactSensitiveFields(): void + { + $redacted = qdb_api_log_redact_value([ + 'username' => 'coach1', + 'password' => 'secret', + 'encrypted' => str_repeat('x', 40), + ]); + $this->assertSame('coach1', $redacted['username']); + $this->assertSame('[REDACTED]', $redacted['password']); + $this->assertSame('[ENCRYPTED:40 chars]', $redacted['encrypted']); + } + + public function testRedactQueryString(): void + { + $qs = qdb_api_log_redact_query_string('questionnaireID=qn1&token=abc&password=pw'); + parse_str((string)$qs, $params); + $this->assertSame('qn1', $params['questionnaireID'] ?? null); + $this->assertSame('[REDACTED]', $params['token'] ?? null); + $this->assertSame('[REDACTED]', $params['password'] ?? null); + } + + public function testTokenHintMasksMiddle(): void + { + $hint = qdb_api_log_token_hint('0123456789abcdef'); + $this->assertStringContainsString('…', $hint); + $this->assertStringNotContainsString('56789', $hint); + } + + public function testClientIpUsesForwardedFor(): void + { + $_SERVER['HTTP_X_FORWARDED_FOR'] = '203.0.113.50, 198.51.100.1'; + $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; + $this->assertSame('203.0.113.50', qdb_api_log_client_ip()); + unset($_SERVER['HTTP_X_FORWARDED_FOR']); + } + + public function testBeginFinishWritesWebChangeEntry(): void + { + $_SERVER['HTTP_X_QDB_CLIENT'] = 'web'; + $_SERVER['REQUEST_URI'] = '/api/users'; + $_SERVER['QUERY_STRING'] = ''; + qdb_test_set_request_body(json_encode(['username' => 'new_coach'], JSON_THROW_ON_ERROR)); + http_response_code(200); + + qdb_api_log_begin('POST', 'users'); + qdb_api_log_finish(); + + $date = date('Y-m-d'); + $read = qdb_api_log_read_entries($date, 'web_change', 10); + $entries = $read['entries']; + $this->assertNotEmpty($entries); + $last = $entries[0]; + $this->assertSame('web_change', $last['activity']); + $this->assertSame('POST', $last['method']); + $this->assertSame('users', $last['route']); + $changes = $last['changes'] ?? []; + $fields = array_column($changes, 'field'); + $this->assertContains('username', $fields); + + qdb_test_reset_http_state(); + } + + private function clearLogFiles(): void + { + foreach (glob($this->logDir . '/api-*.log') ?: [] as $path) { + @unlink($path); + } + } +} diff --git a/tests/Unit/AppAnswersTest.php b/tests/Unit/AppAnswersTest.php new file mode 100644 index 0000000..8609e72 --- /dev/null +++ b/tests/Unit/AppAnswersTest.php @@ -0,0 +1,74 @@ +assertEqualsCanonicalizing(['a', 'b'], $decoded); + } + + public function testDecodeMultiCheckFromJsonArray(): void + { + $this->assertSame(['x', 'y'], qdb_decode_multi_check_values('["x","y"]')); + } + + public function testDecodeMultiCheckFromCommaSeparated(): void + { + $this->assertSame(['one', 'two'], qdb_decode_multi_check_values('one, two')); + } + + public function testBuildGlassSymptomJsonOmitsEmptyAndReturnsNullForNoSymptoms(): void + { + require_once dirname(__DIR__, 2) . '/lib/app_answers.php'; + $this->assertNull(qdb_build_glass_symptom_json([])); + $this->assertNull(qdb_build_glass_symptom_json(['pain' => ''])); + $json = qdb_build_glass_symptom_json(['pain' => 'mild', 'fatigue' => 'severe']); + $this->assertSame(['pain' => 'mild', 'fatigue' => 'severe'], json_decode((string)$json, true)); + } + + public function testGroupAppSubmitAnswersMergesMultiCheck(): void + { + $answers = [ + ['questionID' => 'q1', 'answerOptionKey' => 'opt_a', 'answeredAt' => 100], + ['questionID' => 'q1', 'answerOptionKey' => 'opt_b', 'answeredAt' => 100], + ['questionID' => 'q2', 'freeTextValue' => 'hello'], + ]; + $grouped = qdb_group_app_submit_answers( + $answers, + ['q1' => 'multi_check_box_question', 'q2' => 'text_question'], + [] + ); + $this->assertCount(2, $grouped); + $this->assertSame('q1', $grouped[0]['questionID']); + $decoded = json_decode((string)$grouped[0]['freeTextValue'], true, 512, JSON_THROW_ON_ERROR); + $this->assertEqualsCanonicalizing(['opt_a', 'opt_b'], $decoded); + $this->assertSame('hello', $grouped[1]['freeTextValue']); + } + + public function testGroupAppSubmitAnswersPreservesSymptomRows(): void + { + $answers = [ + ['questionID' => 'fatigue', 'answerOptionKey' => 'moderate'], + ]; + $grouped = qdb_group_app_submit_answers( + $answers, + ['fatigue' => 'glass_scale_question'], + ['fatigue' => 'glass_parent'] + ); + $this->assertCount(1, $grouped); + $this->assertSame('moderate', $grouped[0]['answerOptionKey']); + } +} diff --git a/tests/Unit/AppSubmitValidateTest.php b/tests/Unit/AppSubmitValidateTest.php new file mode 100644 index 0000000..3772bde --- /dev/null +++ b/tests/Unit/AppSubmitValidateTest.php @@ -0,0 +1,131 @@ +fixture(); + $errors = qdb_validate_app_submit_payload($pdo, $f->questionnaireId, [], [], [], [], []); + qdb_discard($tmpDb, $lockFp); + $this->assertNotEmpty($errors); + $this->assertSame('ANSWERS_REQUIRED', $errors[0]['code']); + } + + public function testUnknownQuestionRejected(): void + { + require_once dirname(__DIR__, 2) . '/lib/app_submit_validate.php'; + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $f = $this->fixture(); + $errors = qdb_validate_app_submit_payload( + $pdo, + $f->questionnaireId, + [['questionID' => 'nope', 'answerOptionKey' => 'x']], + [], + [], + [], + [] + ); + qdb_discard($tmpDb, $lockFp); + $this->assertSame('UNKNOWN_QUESTION', $errors[0]['code'] ?? ''); + } + + public function testUnknownQuestionAllowedInLegacyManifest(): void + { + require_once dirname(__DIR__, 2) . '/lib/app_submit_validate.php'; + require_once dirname(__DIR__, 2) . '/lib/questionnaire_structure.php'; + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $f = $this->fixture(); + $manifest = qdb_build_structure_manifest($pdo, $f->questionnaireId, true); + $maps = qdb_submit_maps_from_manifest($pdo, $f->questionnaireId, $manifest); + $errors = qdb_validate_app_submit_payload( + $pdo, + $f->questionnaireId, + [['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes']], + $maps['shortIdMap'], + $maps['shortIdToType'], + $maps['symptomParentMap'], + $maps['optionMap'], + $manifest + ); + qdb_discard($tmpDb, $lockFp); + $this->assertSame([], $errors); + } + + public function testValidSingleChoiceWithOptionKeyPasses(): void + { + require_once dirname(__DIR__, 2) . '/lib/app_submit_validate.php'; + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $f = $this->fixture(); + $shortMap = [$f->questionShortId => $f->questionId]; + $typeMap = [$f->questionShortId => 'single_choice_question']; + $optMap = [ + $f->questionId => [ + 'yes' => ['answerOptionID' => $f->optionId, 'points' => 0], + ], + ]; + $errors = qdb_validate_app_submit_payload( + $pdo, + $f->questionnaireId, + [['questionID' => $f->questionShortId, 'answerOptionKey' => 'yes']], + $shortMap, + $typeMap, + [], + $optMap, + ); + qdb_discard($tmpDb, $lockFp); + $this->assertSame([], $errors); + } + + public function testMultiCheckRequiresSelection(): void + { + require_once dirname(__DIR__, 2) . '/lib/app_submit_validate.php'; + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $f = $this->fixture(); + $shortMap = [$f->multiCheckShortId => $f->multiCheckQuestionId]; + $typeMap = [$f->multiCheckShortId => 'multi_check_box_question']; + $errors = qdb_validate_app_submit_payload( + $pdo, + $f->questionnaireId, + [['questionID' => $f->multiCheckShortId, 'freeTextValue' => '']], + $shortMap, + $typeMap, + [], + [], + ); + qdb_discard($tmpDb, $lockFp); + $this->assertSame('EMPTY_ANSWER', $errors[0]['code'] ?? ''); + } + + public function testInvalidOptionKeyRejected(): void + { + require_once dirname(__DIR__, 2) . '/lib/app_submit_validate.php'; + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $f = $this->fixture(); + $shortMap = [$f->questionShortId => $f->questionId]; + $typeMap = [$f->questionShortId => 'single_choice_question']; + $optMap = [ + $f->questionId => [ + 'yes' => ['answerOptionID' => $f->optionId, 'points' => 0], + ], + ]; + $errors = qdb_validate_app_submit_payload( + $pdo, + $f->questionnaireId, + [['questionID' => $f->questionShortId, 'answerOptionKey' => 'No']], + $shortMap, + $typeMap, + [], + $optMap, + ); + qdb_discard($tmpDb, $lockFp); + $this->assertSame('INVALID_OPTION', $errors[0]['code'] ?? ''); + } +} diff --git a/tests/Unit/CommonBundleTest.php b/tests/Unit/CommonBundleTest.php new file mode 100644 index 0000000..b47dd84 --- /dev/null +++ b/tests/Unit/CommonBundleTest.php @@ -0,0 +1,62 @@ + []]); + $this->fail('Expected JsonErrorResponse'); + } catch (JsonErrorResponse $e) { + $this->assertSame('MISSING_FIELDS', $e->errorCode); + } finally { + qdb_discard($tmpDb, $lockFp); + } + } + + public function testImportTranslationsBundleRoundTripOnPdo(): void + { + [$pdo, $tmpDb, $lockFp] = qdb_open(true); + $f = $this->fixture(); + qdb_put_translation($pdo, 'question', $f->questionId, 'en', 'Hello EN'); + qdb_save($tmpDb, $lockFp); + + [$pdo2, $tmp2, $lock2] = qdb_open(false); + $bundle = qdb_export_translations_bundle($pdo2); + qdb_discard($tmp2, $lock2); + + [$pdo3, $tmp3, $lock3] = qdb_open(true); + $pdo3->prepare('DELETE FROM question_translation WHERE questionID = :id AND languageCode = :lc') + ->execute([':id' => $f->questionId, ':lc' => 'en']); + $result = qdb_import_translations_bundle($pdo3, $bundle); + qdb_save($tmp3, $lock3); + $this->assertGreaterThan(0, $result['imported'] ?? 0); + + [$pdo4, $tmp4, $lock4] = qdb_open(false); + $stmt = $pdo4->prepare( + 'SELECT text FROM question_translation WHERE questionID = :id AND languageCode = :lc' + ); + $stmt->execute([':id' => $f->questionId, ':lc' => 'en']); + $this->assertSame('Hello EN', $stmt->fetchColumn()); + qdb_discard($tmp4, $lock4); + } + + public function testExportQuestionnaireBundleIncludesQuestionKeys(): void + { + [$pdo, $tmpDb, $lockFp] = qdb_open(false); + $f = $this->fixture(); + $item = qdb_export_questionnaire_bundle($pdo, $f->questionnaireId); + qdb_discard($tmpDb, $lockFp); + $this->assertNotNull($item); + $keys = array_column($item['questions'], 'questionKey'); + $this->assertContains('consent_q', $keys); + } +} diff --git a/tests/Unit/CommonHelpersTest.php b/tests/Unit/CommonHelpersTest.php new file mode 100644 index 0000000..0db7995 --- /dev/null +++ b/tests/Unit/CommonHelpersTest.php @@ -0,0 +1,100 @@ +assertTrue(qdb_is_stable_key('question_key')); + $this->assertTrue(qdb_is_stable_key('yes')); + $this->assertFalse(qdb_is_stable_key('Question Key')); + $this->assertFalse(qdb_is_stable_key('')); + } + + public function testNormalizeStableKeyCoercesSpaces(): void + { + $this->assertSame('My_Key', qdb_normalize_stable_key('My Key')); + $this->assertSame('consent_q', qdb_normalize_stable_key('consent_q')); + } + + public function testValidateStableKeyReturnsNormalized(): void + { + $this->assertSame('My_Key', qdb_validate_stable_key('My Key', 'Question key')); + } + + public function testMessageKeyFromConditionJson(): void + { + $json = json_encode(['messageKey' => 'locked_hint'], JSON_THROW_ON_ERROR); + $this->assertSame('locked_hint', qdb_message_key_from_condition_json($json)); + $this->assertSame('', qdb_message_key_from_condition_json('{}')); + } + + public function testMergeGlassSymptomJson(): void + { + $merged = qdb_merge_glass_symptom_json('{"pain":"mild"}', ['fatigue' => 'severe']); + $data = json_decode($merged, true, 512, JSON_THROW_ON_ERROR); + $this->assertSame('mild', $data['pain']); + $this->assertSame('severe', $data['fatigue']); + } + + public function testGlassSymptomAnswerValue(): void + { + $json = json_encode(['pain' => 'moderate'], JSON_THROW_ON_ERROR); + $this->assertSame('moderate', qdb_glass_symptom_answer_value($json, 'pain')); + $this->assertSame('', qdb_glass_symptom_answer_value($json, 'missing')); + } + + public function testApplyGlassSymptomsConfig(): void + { + $config = ['scaleType' => 'glass']; + $rows = [ + ['key' => 'pain', 'labelDe' => 'Schmerz'], + ['key' => 'fatigue', 'labelDe' => 'Müdigkeit'], + ]; + $out = qdb_apply_glass_symptoms_config($config, $rows); + $this->assertSame(['pain', 'fatigue'], $out['symptoms']); + $this->assertSame('glass', $out['scaleType']); + } + + public function testApplyGlassSymptomsConfigClearsWhenEmpty(): void + { + $config = ['symptoms' => ['old_key'], 'scaleType' => 'thermometer']; + $out = qdb_apply_glass_symptoms_config($config, []); + $this->assertArrayNotHasKey('symptoms', $out); + } + + public function testParseGlassSymptomsBody(): void + { + $body = [ + 'glassSymptoms' => [ + ['key' => 'pain', 'labelDe' => 'Schmerz'], + ['key' => 'invalid key', 'labelDe' => 'x'], + ], + ]; + $rows = qdb_parse_glass_symptoms_body($body, []); + $this->assertSame([ + ['key' => 'pain', 'labelDe' => 'Schmerz'], + ['key' => 'invalid_key', 'labelDe' => 'x'], + ], $rows); + } + + public function testParseGlassSymptomsBodyFromConfigWhenMissing(): void + { + $config = ['symptoms' => ['fatigue', 'pain']]; + $rows = qdb_parse_glass_symptoms_body([], $config); + $this->assertSame([ + ['key' => 'fatigue', 'labelDe' => ''], + ['key' => 'pain', 'labelDe' => ''], + ], $rows); + } +} diff --git a/tests/Unit/CryptoTest.php b/tests/Unit/CryptoTest.php new file mode 100644 index 0000000..3f44fc1 --- /dev/null +++ b/tests/Unit/CryptoTest.php @@ -0,0 +1,53 @@ +assertGreaterThan(16, strlen($enc)); + $dec = aes256_cbc_decrypt_bytes($enc, $key); + $this->assertSame($plain, $dec); + } + + public function testHkdfDeterministicForToken(): void + { + $token = bin2hex(random_bytes(16)); + $k1 = hkdf_session_key_from_token($token); + $k2 = hkdf_session_key_from_token($token); + $this->assertSame(32, strlen($k1)); + $this->assertSame($k1, $k2); + } + + public function testHkdfDiffersForDifferentTokens(): void + { + $a = hkdf_session_key_from_token(bin2hex(random_bytes(16))); + $b = hkdf_session_key_from_token(bin2hex(random_bytes(16))); + $this->assertNotSame($a, $b); + } + + public function testDecryptFailsWithWrongKey(): void + { + $key = str_repeat('K', 32); + $enc = aes256_cbc_encrypt_bytes('payload', $key); + $this->expectException(\Exception::class); + aes256_cbc_decrypt_bytes($enc, str_repeat('X', 32)); + } + + public function testDecryptFailsWhenCiphertextTampered(): void + { + $key = str_repeat('K', 32); + $enc = aes256_cbc_encrypt_bytes('payload', $key); + $enc[20] = $enc[20] === 'a' ? 'b' : 'a'; + $this->expectException(\Exception::class); + aes256_cbc_decrypt_bytes($enc, $key); + } +} diff --git a/tests/Unit/EncryptedPayloadTest.php b/tests/Unit/EncryptedPayloadTest.php new file mode 100644 index 0000000..e1097ec --- /dev/null +++ b/tests/Unit/EncryptedPayloadTest.php @@ -0,0 +1,51 @@ +assertTrue($env['encrypted']); + $this->assertSame('A256GCM', $env['alg']); + $plain = qdb_decrypt_sensitive_envelope($env, $token); + $this->assertSame($json, $plain); + } + + public function testLegacyCbcEnvelopeStillDecrypts(): void + { + require_once dirname(__DIR__, 2) . '/lib/encrypted_payload.php'; + $token = bin2hex(random_bytes(32)); + $json = '{"legacy":true}'; + $key = hkdf_session_key_from_token($token); + $env = [ + 'encrypted' => true, + 'payload' => base64_encode(aes256_cbc_encrypt_bytes($json, $key)), + ]; + $this->assertSame($json, qdb_decrypt_sensitive_envelope($env, $token)); + } + + public function testWrongTokenFailsDecrypt(): void + { + require_once dirname(__DIR__, 2) . '/lib/encrypted_payload.php'; + $env = qdb_sensitive_envelope('{}', bin2hex(random_bytes(32))); + $this->expectException(\Exception::class); + qdb_decrypt_sensitive_envelope($env, bin2hex(random_bytes(32))); + } + + public function testInvalidEnvelopeRejected(): void + { + require_once dirname(__DIR__, 2) . '/lib/encrypted_payload.php'; + $this->expectException(InvalidArgumentException::class); + qdb_decrypt_sensitive_envelope(['encrypted' => false], bin2hex(random_bytes(8))); + } +} diff --git a/tests/Unit/ErrorsTest.php b/tests/Unit/ErrorsTest.php new file mode 100644 index 0000000..2736a42 --- /dev/null +++ b/tests/Unit/ErrorsTest.php @@ -0,0 +1,56 @@ +assertTrue(qdb_is_lock_exception(new Exception('Could not acquire lock'))); + $this->assertTrue(qdb_is_lock_exception(new Exception('Could not open lock file'))); + $this->assertFalse(qdb_is_lock_exception(new Exception('other'))); + } + + public function testPublicErrorMessageForLock(): void + { + $msg = qdb_public_error_message(new Exception('Could not acquire lock'), 'Save'); + $this->assertStringContainsString('busy', strtolower($msg)); + } + + public function testPublicErrorMessageForDecrypt(): void + { + $msg = qdb_public_error_message(new Exception('decrypt failed'), 'Open'); + $this->assertStringContainsString('configuration', strtolower($msg)); + } + + public function testPublicErrorMessageForPdo(): void + { + $msg = qdb_public_error_message(new PDOException('SQLITE_CONSTRAINT'), 'Query'); + $this->assertStringContainsString('database error', strtolower($msg)); + } + + public function testApiErrorForLockUses503(): void + { + [$code, , $status] = qdb_api_error_for_exception(new Exception('Could not acquire lock'), 'Write'); + $this->assertSame('LOCKED', $code); + $this->assertSame(503, $status); + } + + public function testApiErrorForGenericUses500(): void + { + [$code, , $status] = qdb_api_error_for_exception(new Exception('boom'), 'Op'); + $this->assertSame('SERVER_ERROR', $code); + $this->assertSame(500, $status); + } +} diff --git a/tests/Unit/LoginRateLimitTest.php b/tests/Unit/LoginRateLimitTest.php new file mode 100644 index 0000000..7b9e641 --- /dev/null +++ b/tests/Unit/LoginRateLimitTest.php @@ -0,0 +1,58 @@ + 3, + 'login_window_seconds' => 300, + 'login_lockout_seconds' => 600, + ]; + [$ok, $retry] = qdb_login_rate_limit_check('user_a', $settings); + $this->assertTrue($ok); + $this->assertSame(0, $retry); + } + + public function testLocksAfterMaxFailures(): void + { + $settings = [ + 'login_max_attempts' => 2, + 'login_window_seconds' => 300, + 'login_lockout_seconds' => 600, + ]; + $user = 'lock_test_' . bin2hex(random_bytes(4)); + qdb_login_rate_limit_record_failure($user, $settings); + qdb_login_rate_limit_record_failure($user, $settings); + [$ok, $retry] = qdb_login_rate_limit_check($user, $settings); + $this->assertFalse($ok); + $this->assertGreaterThan(0, $retry); + } + + public function testClearResetsCounter(): void + { + $settings = [ + 'login_max_attempts' => 1, + 'login_window_seconds' => 300, + 'login_lockout_seconds' => 600, + ]; + $user = 'clear_test_' . bin2hex(random_bytes(4)); + qdb_login_rate_limit_record_failure($user, $settings); + qdb_login_rate_limit_clear($user); + [$ok,] = qdb_login_rate_limit_check($user, $settings); + $this->assertTrue($ok); + } +} diff --git a/tests/Unit/QuestionnaireStructureTest.php b/tests/Unit/QuestionnaireStructureTest.php new file mode 100644 index 0000000..1096494 --- /dev/null +++ b/tests/Unit/QuestionnaireStructureTest.php @@ -0,0 +1,56 @@ +fixture(); + + $before = qdb_questionnaire_structure_revision($pdo, $f->questionnaireId); + $newRev = qdb_bump_structure_revision($pdo, $f->questionnaireId, 'test_bump'); + $after = qdb_questionnaire_structure_revision($pdo, $f->questionnaireId); + + $this->assertSame($before + 1, $newRev); + $this->assertSame($newRev, $after); + + $manifest = qdb_load_structure_manifest($pdo, $f->questionnaireId, $before); + $this->assertNotNull($manifest); + $this->assertNotEmpty($manifest['questions'] ?? []); + $this->assertSame($before, (int)($manifest['structureRevision'] ?? 0)); + + qdb_save($tmpDb, $lockFp); + } + + public function testRetirePreservesClientAnswers(): void + { + require_once dirname(__DIR__, 2) . '/lib/questionnaire_structure.php'; + $this->assertApiOk($this->submitFixtureQuestionnaire()); + + [$pdo, $tmpDb, $lockFp] = qdb_open(true); + $f = $this->fixture(); + + $this->assertTrue(qdb_question_has_client_data($pdo, $f->questionId)); + $rev = qdb_bump_structure_revision($pdo, $f->questionnaireId, 'retire_test'); + qdb_retire_question($pdo, $f->questionId, $rev); + + $row = $pdo->prepare('SELECT retiredAt FROM question WHERE questionID = :id'); + $row->execute([':id' => $f->questionId]); + $this->assertGreaterThan(0, (int)$row->fetchColumn()); + + $ans = $pdo->prepare( + 'SELECT COUNT(*) FROM client_answer WHERE clientCode = :cc AND questionID = :qid' + ); + $ans->execute([':cc' => $f->clientCode, ':qid' => $f->questionId]); + $this->assertSame(1, (int)$ans->fetchColumn()); + + qdb_save($tmpDb, $lockFp); + } +} diff --git a/tests/Unit/RbacTest.php b/tests/Unit/RbacTest.php new file mode 100644 index 0000000..1ad807b --- /dev/null +++ b/tests/Unit/RbacTest.php @@ -0,0 +1,48 @@ + 'admin', 'entityID' => 'a1'], 'cl'); + $this->assertStringContainsString('1=1', $clause); + $this->assertStringContainsString('archived', $clause); + $this->assertSame([], $params); + + [$allClause] = rbac_client_filter(['role' => 'admin', 'entityID' => 'a1'], 'cl', 'all'); + $this->assertStringNotContainsString('archived', $allClause); + } + + public function testSupervisorFilterBindsEntity(): void + { + [$clause, $params] = rbac_client_filter( + ['role' => 'supervisor', 'entityID' => 'sv-99'], + 'cl' + ); + $this->assertStringContainsString('supervisorID', $clause); + $this->assertSame([':rbac_eid' => 'sv-99'], $params); + } + + public function testCoachFilterBindsCoach(): void + { + [$clause, $params] = rbac_client_filter( + ['role' => 'coach', 'entityID' => 'coach-1'], + 'cl' + ); + $this->assertStringContainsString('coachID', $clause); + $this->assertSame([':rbac_eid' => 'coach-1'], $params); + } + + public function testWebRolesList(): void + { + $this->assertContains('admin', qdb_web_roles()); + $this->assertContains('supervisor', qdb_web_roles()); + $this->assertNotContains('coach', qdb_web_roles()); + } +} diff --git a/tests/Unit/ScoringTest.php b/tests/Unit/ScoringTest.php new file mode 100644 index 0000000..731c489 --- /dev/null +++ b/tests/Unit/ScoringTest.php @@ -0,0 +1,211 @@ +pdo = new PDO('sqlite::memory:'); + $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->pdo->exec('PRAGMA foreign_keys = ON'); + $schema = file_get_contents(dirname(__DIR__, 2) . '/schema.sql'); + $this->pdo->exec($schema); + } + + public function testBandForTotal(): void + { + $legacy = ['greenMax' => 12, 'yellowMax' => 36]; + $this->assertSame('green', qdb_band_for_total(0, $legacy)); + $this->assertSame('green', qdb_band_for_total(12, $legacy)); + $this->assertSame('yellow', qdb_band_for_total(13, $legacy)); + $this->assertSame('yellow', qdb_band_for_total(36, $legacy)); + $this->assertSame('red', qdb_band_for_total(37, $legacy)); + + $custom = [ + 'greenMin' => 5, + 'greenMax' => 15, + 'yellowMin' => 16, + 'yellowMax' => 30, + 'redMin' => 31, + ]; + $this->assertSame('green', qdb_band_for_total(5, $custom)); + $this->assertSame('green', qdb_band_for_total(15, $custom)); + $this->assertSame('yellow', qdb_band_for_total(16, $custom)); + $this->assertSame('red', qdb_band_for_total(31, $custom)); + } + + public function testValidateScoringBands(): void + { + $this->assertNull(qdb_validate_scoring_bands(qdb_normalize_scoring_bands(['greenMax' => 12, 'yellowMax' => 36]))); + $this->assertNotNull(qdb_validate_scoring_bands([ + 'greenMin' => 0, 'greenMax' => 20, + 'yellowMin' => 15, 'yellowMax' => 36, + 'redMin' => 37, + ])); + } + + public function testGlassScoreRulesFromSymptoms(): void + { + $rules = qdb_score_rules_from_glass_symptoms([ + ['key' => 'pain', 'scoreLevels' => ['never_glass' => 0, 'extreme_glass' => 5]], + ]); + $this->assertCount(5, $rules); + $painExtreme = array_values(array_filter( + $rules, + fn ($r) => $r['scopeKey'] === 'pain' && $r['levelKey'] === 'extreme_glass' + )); + $this->assertSame(5, $painExtreme[0]['points'] ?? null); + } + + private function seedCoachChain(): void + { + $this->pdo->exec("INSERT INTO supervisor (supervisorID, username) VALUES ('sup1', 'sup')"); + $this->pdo->exec("INSERT INTO coach (coachID, supervisorID, username) VALUES ('coach1', 'sup1', 'coach')"); + } + + public function testRadioAndGlassScoring(): void + { + $this->seedCoachChain(); + $qnID = 'qn_test'; + $this->pdo->exec("INSERT INTO questionnaire (questionnaireID, name, version, state, orderIndex, showPoints, conditionJson, categoryKey) + VALUES ('$qnID', 'Test', '1', 'active', 0, 0, '{}', '')"); + + $radioQ = "{$qnID}__radio"; + $this->pdo->exec("INSERT INTO question (questionID, questionnaireID, defaultText, type, orderIndex, isRequired, configJson) + VALUES ('$radioQ', '$qnID', 'Pick one', 'radio_question', 0, 1, '{}')"); + $this->pdo->exec("INSERT INTO answer_option (answerOptionID, questionID, defaultText, points, orderIndex, nextQuestionId) + VALUES ('ao1', '$radioQ', 'yes', 3, 0, '')"); + + $glassQ = "{$qnID}__glass"; + $this->pdo->exec("INSERT INTO question (questionID, questionnaireID, defaultText, type, orderIndex, isRequired, configJson) + VALUES ('$glassQ', '$qnID', 'Symptoms', 'glass_scale_question', 1, 1, '{\"symptoms\":[\"pain\"]}')"); + qdb_sync_question_score_rules($this->pdo, $glassQ, [ + ['scopeKey' => 'pain', 'levelKey' => 'moderate_glass', 'points' => 2], + ]); + + $client = 'CLIENT-001'; + $this->pdo->exec("INSERT INTO client (clientCode, coachID) VALUES ('$client', 'coach1')"); + $this->pdo->exec("INSERT INTO client_answer (clientCode, questionID, answerOptionID, freeTextValue, numericValue) + VALUES ('$client', '$radioQ', 'ao1', NULL, NULL)"); + $this->pdo->exec("INSERT INTO client_answer (clientCode, questionID, answerOptionID, freeTextValue, numericValue) + VALUES ('$client', '$glassQ', NULL, '{\"pain\":\"moderate_glass\"}', NULL)"); + + $score = qdb_compute_questionnaire_score($this->pdo, $client, $qnID); + $this->assertSame(5, $score); + } + + public function testIncompleteProfileProducesNoResult(): void + { + $this->seedCoachChain(); + $qnA = 'qn_a'; + $qnB = 'qn_b'; + foreach ([$qnA, $qnB] as $id) { + $this->pdo->exec("INSERT INTO questionnaire (questionnaireID, name, version, state, orderIndex, showPoints, conditionJson, categoryKey) + VALUES ('$id', '$id', '1', 'active', 0, 0, '{}', '')"); + } + $profileID = 'profile1'; + $this->pdo->exec("INSERT INTO scoring_profile (profileID, name, description, isActive, + greenMin, greenMax, yellowMin, yellowMax, redMin, createdAt, updatedAt) + VALUES ('$profileID', 'Combo', '', 1, 0, 10, 11, 20, 21, 0, 0)"); + $this->pdo->exec("INSERT INTO scoring_profile_questionnaire (profileID, questionnaireID, weight, orderIndex) + VALUES ('$profileID', '$qnA', 1.0, 0), ('$profileID', '$qnB', 1.0, 1)"); + + $client = 'CLIENT-002'; + $this->pdo->exec("INSERT INTO client (clientCode, coachID) VALUES ('$client', 'coach1')"); + $this->pdo->exec("INSERT INTO completed_questionnaire (clientCode, questionnaireID, status, sumPoints, completedAt) + VALUES ('$client', '$qnA', 'completed', 5, 1)"); + + qdb_recompute_profile_scores_for_client($this->pdo, $client); + + $stmt = $this->pdo->prepare('SELECT COUNT(*) FROM client_scoring_profile_result WHERE clientCode = :cc'); + $stmt->execute([':cc' => $client]); + $this->assertSame(0, (int)$stmt->fetchColumn()); + } + + public function testCoachScoringReviewPreservesComputedBand(): void + { + $this->seedCoachChain(); + $qnID = 'qn_review'; + $this->pdo->exec("INSERT INTO questionnaire (questionnaireID, name, version, state, orderIndex, showPoints, conditionJson, categoryKey) + VALUES ('$qnID', 'Review', '1', 'active', 0, 0, '{}', '')"); + $profileID = 'profile_review'; + $this->pdo->exec("INSERT INTO scoring_profile (profileID, name, description, isActive, + greenMin, greenMax, yellowMin, yellowMax, redMin, createdAt, updatedAt) + VALUES ('$profileID', 'Review profile', '', 1, 0, 10, 11, 20, 21, 0, 0)"); + $this->pdo->exec("INSERT INTO scoring_profile_questionnaire (profileID, questionnaireID, weight, orderIndex) + VALUES ('$profileID', '$qnID', 1.0, 0)"); + + $client = 'CLIENT-REVIEW'; + $this->pdo->exec("INSERT INTO client (clientCode, coachID) VALUES ('$client', 'coach1')"); + $this->pdo->exec("INSERT INTO completed_questionnaire (clientCode, questionnaireID, status, sumPoints, completedAt) + VALUES ('$client', '$qnID', 'completed', 15, 1)"); + + qdb_recompute_profile_scores_for_client($this->pdo, $client); + + $row = $this->pdo->query( + "SELECT band, coachBand FROM client_scoring_profile_result WHERE clientCode = '$client'" + )->fetch(PDO::FETCH_ASSOC); + $this->assertSame('yellow', $row['band']); + $this->assertSame('', $row['coachBand']); + + qdb_set_coach_scoring_band($this->pdo, $client, $profileID, 'green', 'user1'); + + $row = $this->pdo->query( + "SELECT band, coachBand FROM client_scoring_profile_result WHERE clientCode = '$client'" + )->fetch(PDO::FETCH_ASSOC); + $this->assertSame('yellow', $row['band']); + $this->assertSame('green', $row['coachBand']); + + qdb_recompute_profile_scores_for_client($this->pdo, $client); + $row = $this->pdo->query( + "SELECT band, coachBand FROM client_scoring_profile_result WHERE clientCode = '$client'" + )->fetch(PDO::FETCH_ASSOC); + $this->assertSame('yellow', $row['band']); + $this->assertSame('green', $row['coachBand']); + } + + public function testSaveCoachReviewFromAppBeforeServerRecompute(): void + { + $this->seedCoachChain(); + $qnID = 'qn_pre'; + $this->pdo->exec("INSERT INTO questionnaire (questionnaireID, name, version, state, orderIndex, showPoints, conditionJson, categoryKey) + VALUES ('$qnID', 'Pre', '1', 'active', 0, 0, '{}', '')"); + $profileID = 'profile_pre'; + $this->pdo->exec("INSERT INTO scoring_profile (profileID, name, description, isActive, + greenMin, greenMax, yellowMin, yellowMax, redMin, createdAt, updatedAt) + VALUES ('$profileID', 'Pre profile', '', 1, 0, 10, 11, 20, 21, 0, 0)"); + $this->pdo->exec("INSERT INTO scoring_profile_questionnaire (profileID, questionnaireID, weight, orderIndex) + VALUES ('$profileID', '$qnID', 1.0, 0)"); + + $client = 'CLIENT-PRE'; + $this->pdo->exec("INSERT INTO client (clientCode, coachID) VALUES ('$client', 'coach1')"); + + qdb_save_coach_scoring_review( + $this->pdo, + $client, + $profileID, + 'yellow', + 'user1', + 15.0, + 'yellow' + ); + + $row = $this->pdo->query( + "SELECT band, coachBand, weightedTotal FROM client_scoring_profile_result WHERE clientCode = '$client'" + )->fetch(PDO::FETCH_ASSOC); + $this->assertSame('yellow', $row['band']); + $this->assertSame('yellow', $row['coachBand']); + $this->assertSame(15.0, (float)$row['weightedTotal']); + } +} diff --git a/tests/Unit/SettingsTest.php b/tests/Unit/SettingsTest.php new file mode 100644 index 0000000..75ee315 --- /dev/null +++ b/tests/Unit/SettingsTest.php @@ -0,0 +1,54 @@ +assertSame(10, $d['login_max_attempts']); + $this->assertSame(30 * 24 * 60 * 60, $d['session_ttl_seconds']); + } + + public function testValidateRejectsOutOfRange(): void + { + require_once dirname(__DIR__, 2) . '/lib/settings.php'; + $this->expectException(InvalidArgumentException::class); + qdb_settings_validate_and_merge(['login_max_attempts' => 0]); + } + + public function testSaveAndLoadRoundTrip(): void + { + require_once dirname(__DIR__, 2) . '/lib/settings.php'; + [$pdo, $tmpDb, $lockFp] = qdb_open(true); + $custom = qdb_settings_validate_and_merge([ + 'login_max_attempts' => 5, + 'login_window_seconds' => 600, + 'login_lockout_seconds' => 1200, + 'session_ttl_seconds' => 86400, + 'temp_session_ttl_seconds' => 900, + ]); + qdb_settings_save_on_pdo($pdo, $custom); + qdb_save($tmpDb, $lockFp); + [$pdo2, $tmp2, $lock2] = qdb_open(false); + $loaded = qdb_settings_get_on_pdo($pdo2); + qdb_discard($tmp2, $lock2); + $this->assertSame(5, $loaded['login_max_attempts']); + $this->assertSame(86400, $loaded['session_ttl_seconds']); + } + + public function testSessionTtlHelper(): void + { + require_once dirname(__DIR__, 2) . '/lib/settings.php'; + $this->assertSame(600, qdb_session_ttl_seconds(['temp_session_ttl_seconds' => 600], true)); + $this->assertSame(3600, qdb_session_ttl_seconds(['session_ttl_seconds' => 3600], false)); + } +} diff --git a/tests/Unit/TextSanitizeTest.php b/tests/Unit/TextSanitizeTest.php new file mode 100644 index 0000000..f97fbb9 --- /dev/null +++ b/tests/Unit/TextSanitizeTest.php @@ -0,0 +1,31 @@ +assertStringNotContainsString('DROP TABLE', $out); + $this->assertStringNotContainsString('--', $out); + } + + public function testPreservesNewlines(): void + { + require_once dirname(__DIR__, 2) . '/lib/text_sanitize.php'; + $out = sanitize_free_text("line1\nline2"); + $this->assertStringContainsString("\n", $out); + } + + public function testNonStringReturnsEmpty(): void + { + require_once dirname(__DIR__, 2) . '/lib/text_sanitize.php'; + $this->assertSame('', sanitize_free_text(null)); + } +} diff --git a/tests/Unit/ValidateTest.php b/tests/Unit/ValidateTest.php new file mode 100644 index 0000000..d5f56fc --- /dev/null +++ b/tests/Unit/ValidateTest.php @@ -0,0 +1,55 @@ + '1', 'b' => 'x']; + require_fields($body, ['a', 'b']); + $this->assertSame(['a' => '1', 'b' => 'x'], $body); + } + + public function testRequireFieldsMissing(): void + { + $this->expectException(JsonErrorResponse::class); + $this->expectExceptionMessage('Required fields'); + try { + require_fields(['a' => ''], ['a', 'b']); + } catch (JsonErrorResponse $e) { + $this->assertSame('MISSING_FIELDS', $e->errorCode); + $this->assertSame(400, $e->httpStatus); + throw $e; + } + } + + public function testValidateStringTrims(): void + { + $s = validate_string(' hello ', 'field', 1, 20); + $this->assertSame('hello', $s); + } + + public function testValidateEnum(): void + { + $this->assertSame('admin', validate_enum('admin', 'role', ['admin', 'coach'])); + } + + public function testValidateIntBounds(): void + { + $this->assertSame(5, validate_int('5', 'n', 1, 10)); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..8c10c6d --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,29 @@ +&1', + $envPrefix, + escapeshellarg(PHP_BINARY), + escapeshellarg($root) +); + +$output = []; +exec($cmd, $output, $exitCode); +echo implode("\n", $output) . "\n"; + +if ($exitCode !== 0) { + exit($exitCode); +} + +$linesPct = null; +foreach ($output as $line) { + // PHPUnit 11 / Xdebug: "Lines: 25.33% (1484/5859)" + if (preg_match('/^\s*Lines:\s+([\d.]+)%\s+\(/', $line, $m)) { + $linesPct = (float)$m[1]; + break; + } + // PCOV / older format: "Lines: 1484/5859 (25.33%)" + if (preg_match('/^\s*Lines:\s+[\d.]+\/\d+\s+\(\s*([\d.]+)%/', $line, $m)) { + $linesPct = (float)$m[1]; + break; + } +} + +if ($linesPct === null) { + fwrite(STDERR, "Could not parse coverage summary. Is PCOV or Xdebug enabled?\n"); + exit(1); +} + +if ($linesPct < $min) { + fwrite(STDERR, sprintf("Line coverage %.2f%% is below minimum %.2f%%\n", $linesPct, $min)); + exit(1); +} + +fwrite(STDERR, sprintf("Line coverage %.2f%% meets minimum %.2f%%\n", $linesPct, $min)); +exit(0); diff --git a/uploads/logs/.gitkeep b/uploads/logs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/uploads/logs/api/.gitkeep b/uploads/logs/api/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/website/css/style.css b/website/css/style.css new file mode 100644 index 0000000..ec4549b --- /dev/null +++ b/website/css/style.css @@ -0,0 +1,3975 @@ +:root { + color-scheme: light; + --bg: #f3f7f6; + --surface: #ffffff; + --surface-raised: #fbfdfd; + --surface-hover: #eaf3f0; + --surface-muted: #f4f8f7; + --surface-input: #ffffff; + --surface-elevated: #fbfdfd; + --primary: #0f766e; + --primary-hover: #0b5f58; + --primary-subtle: rgba(15, 118, 110, 0.1); + --primary-border: rgba(15, 118, 110, 0.28); + --primary-text: #0f766e; + --danger: #dc2626; + --danger-hover: #b91c1c; + --danger-subtle: rgba(220, 38, 38, 0.08); + --danger-subtle-hover: rgba(220, 38, 38, 0.14); + --danger-border: rgba(220, 38, 38, 0.35); + --danger-text: #b91c1c; + --danger-focus-ring: rgba(220, 38, 38, 0.25); + --success: #059669; + --warning: #d97706; + --text: #102a27; + --text-secondary: #5d746f; + --text-on-primary: #ffffff; + --border: #d5e4e0; + --radius: 8px; + --radius-lg: 16px; + --shadow: 0 1px 2px rgba(16, 42, 39, 0.06), 0 1px 3px rgba(16, 42, 39, 0.08); + --shadow-lg: 0 8px 24px rgba(16, 42, 39, 0.08), 0 2px 6px rgba(16, 42, 39, 0.04); + --shadow-inset: inset 0 1px 0 rgba(255, 255, 255, 0.85); + --focus-ring: rgba(15, 118, 110, 0.22); + --sidebar-width: 240px; + --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; + --table-header-bg: #eaf3f0; + --table-stripe-bg: rgba(234, 243, 240, 0.85); + --row-hover-bg: #e7f5f1; + --sticky-shadow: rgba(16, 42, 39, 0.08); + --atmosphere-blue: rgba(15, 118, 110, 0.09); + --atmosphere-indigo: rgba(14, 116, 144, 0.06); + --grid-line: rgba(93, 116, 111, 0.1); + --accent-gradient: linear-gradient(135deg, #0f766e 0%, #0e7490 45%, #059669 100%); + --accent-gradient-soft: linear-gradient(90deg, rgba(15, 118, 110, 0.55), rgba(14, 116, 144, 0.4)); + --accent-glow: 0 4px 18px rgba(15, 118, 110, 0.18); + --shine-line: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.9), transparent); + /* Incomplete translation highlight */ + --trans-missing-bg: #fffbeb; + --trans-missing-bg-hover: #fef3c7; + --trans-missing-border: #fcd34d; + --trans-missing-accent: #d97706; + --trans-missing-placeholder: #92400e; + /* Status badges */ + --badge-draft-bg: #f1f5f9; + --badge-draft-fg: #64748b; + --badge-active-bg: rgba(5, 150, 105, 0.12); + --badge-active-fg: #047857; + --badge-archived-bg: rgba(217, 119, 6, 0.12); + --badge-archived-fg: #b45309; + --badge-in-progress-bg: var(--primary-subtle); + --badge-in-progress-fg: var(--primary-text); + --badge-pending-bg: var(--badge-draft-bg); + --badge-pending-fg: var(--badge-draft-fg); + --badge-admin-bg: rgba(109, 40, 217, 0.1); + --badge-admin-fg: #6d28d9; + --badge-supervisor-bg: rgba(190, 24, 93, 0.1); + --badge-supervisor-fg: #be185d; + --badge-coach-bg: rgba(22, 163, 74, 0.1); + --badge-coach-fg: #15803d; + --badge-you-bg: var(--primary-subtle); + --badge-you-fg: var(--primary-text); + --badge-warn-bg: rgba(217, 119, 6, 0.12); + --badge-warn-fg: #b45309; + --badge-q-bg: var(--primary-subtle); + --badge-q-fg: var(--primary-text); + --badge-opt-bg: #f1f5f9; + --badge-opt-fg: #475569; + --badge-app-bg: rgba(22, 163, 74, 0.1); + --badge-app-fg: #15803d; + --badge-str-bg: rgba(109, 40, 217, 0.1); + --badge-str-fg: #6d28d9; + --badge-branch-bg: rgba(109, 40, 217, 0.1); + --badge-branch-fg: #6d28d9; + --lang-chip-fixed-bg: var(--primary-subtle); + --lang-chip-fixed-border: var(--primary-border); + --toast-success-bg: #ecfdf5; + --toast-success-fg: #166534; + --toast-success-border: #86efac; + --toast-error-bg: #fef2f2; + --toast-error-fg: #991b1b; + --toast-error-border: #fca5a5; + --toast-info-bg: #ddf4ef; + --toast-info-fg: #0f766e; + --toast-info-border: #99d5c9; + --save-flash-bg: var(--primary-subtle); + --progress-track: #e2e8f0; +} + +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + +body { + font-family: var(--font); + background: var(--bg); + color: var(--text); + line-height: 1.5; + min-height: 100vh; +} + +hr { + border: none; + border-top: 1px solid var(--border); + margin: 20px 0; +} + +pre, code { + font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, monospace; +} +pre { + background: var(--surface-muted); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 12px; + color: var(--text); +} +code { + background: var(--surface-raised); + padding: 1px 5px; + border-radius: 4px; + font-size: 0.9em; +} + +/* Layout */ +.layout { + display: flex; + min-height: 100vh; + position: relative; + isolation: isolate; +} + +/* Ambient backdrop (logged-in app shell — matches login atmosphere) */ +body.logged-in:not(.login-active) .layout::before { + content: ''; + position: fixed; + inset: 0; + z-index: 0; + pointer-events: none; + background: + radial-gradient(ellipse 85% 65% at 18% 22%, var(--atmosphere-blue), transparent 62%), + radial-gradient(ellipse 60% 50% at 82% 78%, var(--atmosphere-indigo), transparent 58%), + var(--bg); +} +body.logged-in:not(.login-active) .layout::after { + content: ''; + position: fixed; + inset: 0; + z-index: 0; + pointer-events: none; + background-image: + linear-gradient(var(--grid-line) 1px, transparent 1px), + linear-gradient(90deg, var(--grid-line) 1px, transparent 1px); + background-size: 56px 56px; + mask-image: radial-gradient(ellipse 100% 90% at 55% 40%, #000 15%, transparent 72%); +} + +/* Sidebar */ +.sidebar { + width: var(--sidebar-width); + background: var(--surface); + backdrop-filter: blur(14px); + border-right: 1px solid var(--border); + display: flex; + flex-direction: column; + position: fixed; + top: 0; + left: 0; + bottom: 0; + z-index: 100; + box-shadow: 2px 0 16px rgba(15, 23, 42, 0.06); +} +.sidebar-brand { + position: relative; + padding: 20px 20px 20px 24px; + border-bottom: 1px solid var(--border); + font-weight: 700; + font-size: 1.05rem; + line-height: 1.35; + letter-spacing: -0.02em; + color: var(--text); + background: linear-gradient(180deg, rgba(15, 118, 110, 0.06) 0%, transparent 100%); +} +.sidebar-brand::before { + content: ''; + position: absolute; + left: 0; + top: 18px; + bottom: 18px; + width: 3px; + border-radius: 0 2px 2px 0; + background: var(--accent-gradient); + box-shadow: 0 0 8px rgba(15, 118, 110, 0.25); +} +.sidebar-brand small { + display: block; + font-weight: 600; + font-size: .75rem; + color: var(--primary-text); +} +.sidebar-nav { + flex: 1; + padding: 12px 0; + list-style: none; +} +.sidebar-nav a { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 20px; + margin: 0 8px; + border-radius: 6px; + color: var(--text-secondary); + text-decoration: none; + font-size: .9rem; + transition: background .15s, color .15s, box-shadow .15s; +} +.sidebar-nav a:hover { + background: var(--surface-hover); + color: var(--text); +} +.sidebar-nav a.active { + background: var(--primary-subtle); + color: var(--primary-text); + box-shadow: inset 3px 0 0 var(--primary); + border-radius: 0 6px 6px 0; + margin-left: 0; + padding-left: 17px; +} +.sidebar-nav a svg { + width: 18px; + height: 18px; + flex-shrink: 0; + transition: color 0.15s, filter 0.15s; +} +.sidebar-nav a.active svg { + color: var(--primary); + filter: drop-shadow(0 0 4px rgba(15, 118, 110, 0.2)); +} +.sidebar-footer { + padding: 16px 20px; + border-top: 1px solid var(--border); + font-size: .85rem; + background: var(--surface-muted); +} +.sidebar-footer .user-info { + color: var(--text-secondary); + margin-bottom: 8px; +} + +/* Main content */ +.main-content { + position: relative; + z-index: 1; + margin-left: var(--sidebar-width); + flex: 1; + padding: 28px 32px; + min-width: 0; +} + +/* Cards */ +.card { + position: relative; + overflow: hidden; + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-lg), var(--shadow-inset); + padding: 24px; + transition: border-color 0.2s, box-shadow 0.2s; +} +body.logged-in .card::before { + content: ''; + position: absolute; + inset: 0 0 auto 0; + height: 1px; + background: var(--shine-line); + pointer-events: none; +} +body.logged-in .card > h3 { + margin: 0 0 14px; + font-size: 1rem; + font-weight: 600; + letter-spacing: -0.01em; + padding-bottom: 10px; + border-bottom: 1px solid var(--border); + background: linear-gradient(90deg, var(--text) 0%, var(--text-secondary) 120%); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} + +/* Elevated panels & inputs (login visual language) */ +body.logged-in :is( + .question-item, + .inline-form-card, + .trans-lang-panel, + .trans-top-bar, + .trans-sheet, + .lang-manager, + .condition-editor, + .scoring-profile-card, + .scoring-profile-picker, + .coach-client-group, + .client-qn-panel, + .table-wrapper, + .trans-table-wrapper, + .trans-list, + .pending-option-item, + .translations-panel, + .coach-recent-panel, + .client-detail-panel, + .option-item +) { + border-radius: var(--radius-lg); + box-shadow: var(--shadow), var(--shadow-inset); +} + +body.logged-in .table-wrapper, +body.logged-in .trans-table-wrapper { + border: 1px solid var(--border); + background: var(--surface); +} + +body.logged-in :is( + .category-german-input, + .coach-supervisor-select, + .type-select, + .trans-select, + .lang-add-code, + .lang-add-name, + .trans-search-input, + .followup-note-input, + .condition-json-fallback, + .scoring-profile-picker input, + .scoring-profile-picker select, + .value-score-pts, + .translation-row input[type="text"], + .trans-list-item .trans-cell-input +) { + background: var(--surface-input); + border-radius: var(--radius); +} + +body.logged-in .btn-home { + border-radius: var(--radius); + background: var(--surface-input); + box-shadow: var(--shadow); +} +body.logged-in .btn-home:hover { + background: var(--surface-hover); + border-color: var(--primary-border); + box-shadow: var(--accent-glow); +} +body.logged-in .btn-home svg { + transition: color 0.15s; +} +body.logged-in .btn-home:hover svg { + color: var(--primary-text); +} + +/* Buttons */ +.btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 16px; + font-size: .875rem; + font-weight: 500; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--surface-input); + color: var(--text); + cursor: pointer; + transition: background .15s, box-shadow .15s; + text-decoration: none; + line-height: 1.4; +} +.btn:hover:not(.btn-primary):not(.btn-danger) { + background: var(--surface-hover); + box-shadow: var(--shadow); +} +.btn-primary { + background: var(--accent-gradient); + color: var(--text-on-primary); + border-color: rgba(15, 118, 110, 0.35); + box-shadow: 0 1px 2px rgba(16, 42, 39, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.2); +} +.btn-primary:hover { + background: linear-gradient(135deg, #0f766e 0%, #0b5f58 45%, #059669 100%); + border-color: rgba(11, 95, 88, 0.45); + box-shadow: var(--accent-glow), inset 0 1px 0 rgba(255, 255, 255, 0.22); +} +.btn-danger { + background: var(--danger-subtle); + color: var(--danger-text); + border-color: var(--danger-border); +} +.btn-danger:hover { + background: var(--danger-subtle-hover); + border-color: var(--danger); + color: var(--danger-hover); + box-shadow: 0 0 0 1px var(--danger-border); +} +.btn-danger:focus-visible { + outline: none; + box-shadow: 0 0 0 3px var(--danger-focus-ring); +} +.btn-sm { padding: 4px 10px; font-size: .8rem; } +.btn-block { width: 100%; justify-content: center; } +.btn-icon { + padding: 6px; + border: none; + background: transparent; + color: var(--text-secondary); +} +.btn-icon:hover:not(.btn-icon-danger) { + background: var(--surface-hover); + border-radius: 4px; + color: var(--text); +} +.btn-icon-danger { + color: var(--danger-text); +} +.btn-icon-danger:hover { + background: var(--danger-subtle); + color: var(--danger-hover); + border-radius: 4px; +} + +/* Forms */ +.form-group { + margin-bottom: 16px; +} +.form-group label { + display: block; + font-size: .85rem; + font-weight: 500; + margin-bottom: 4px; + color: var(--text-secondary); +} +.form-group input, +.form-group select, +.form-group textarea { + width: 100%; + padding: 10px 12px; + border: 1px solid var(--border); + border-radius: var(--radius); + font-size: .9rem; + font-family: var(--font); + background: var(--surface-input); + color: var(--text); + transition: border-color .15s, box-shadow .15s; +} +.form-group input:focus, +.form-group select:focus, +.form-group textarea:focus { + outline: none; + border-color: var(--primary); + box-shadow: 0 0 0 3px var(--focus-ring); +} +.form-row { + display: flex; + gap: 12px; +} +.form-row .form-group { flex: 1; } + +/* Badges */ +.badge { + display: inline-block; + padding: 2px 8px; + font-size: .75rem; + font-weight: 600; + border-radius: 999px; + text-transform: uppercase; + letter-spacing: .03em; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7); +} +.badge-draft { background: var(--badge-draft-bg); color: var(--badge-draft-fg); } +.badge-active { background: var(--badge-active-bg); color: var(--badge-active-fg); } +.badge-archived { background: var(--badge-archived-bg); color: var(--badge-archived-fg); } + +.retired-questions-panel summary { + cursor: pointer; + font-weight: 600; + margin-bottom: 8px; +} +.retired-question-item { + opacity: 0.85; +} +.retired-question-item .question-header { + cursor: default; +} +.badge-completed { background: var(--badge-active-bg); color: var(--badge-active-fg); } +.badge-in_progress { background: var(--badge-in-progress-bg); color: var(--badge-in-progress-fg); } +.badge-pending { background: var(--badge-pending-bg); color: var(--badge-pending-fg); } +.badge-activity-app_sync { background: var(--badge-coach-bg); color: var(--badge-coach-fg); } +.badge-activity-app_change { background: var(--badge-in-progress-bg); color: var(--badge-in-progress-fg); } +.badge-activity-web_change { background: var(--badge-supervisor-bg); color: var(--badge-supervisor-fg); } +.badge-activity-web_export { background: var(--badge-admin-bg); color: var(--badge-admin-fg); } +.badge-activity-api_error { background: var(--badge-archived-bg); color: var(--badge-archived-fg); } +.security-settings-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); + gap: 14px 20px; + margin-bottom: 16px; +} +.security-settings-grid .form-group { + margin-bottom: 0; +} +.revoke-confirm-input { + width: 100%; + max-width: 360px; + padding: 8px 10px; + border: 1px solid var(--border); + border-radius: 6px; + font-size: 0.9rem; + font-family: var(--font-mono, ui-monospace, monospace); +} +.user-actions-cell { + white-space: nowrap; +} +.user-actions-cell .btn + .btn { + margin-left: 6px; +} +.activity-log-card .table-wrapper { max-height: 420px; overflow: auto; } +.activity-log-card .filter-bar label { + margin: 0; + line-height: 1.2; +} +.activity-log-detail { + font-size: 0.8rem; + max-width: 280px; + word-break: break-word; +} +table.data-table.activity-log-table th, +table.data-table.activity-log-table td { + vertical-align: top; + line-height: 1.45; +} +table.data-table.activity-log-table .activity-log-col-time, +table.data-table.activity-log-table .activity-log-col-meta { + white-space: nowrap; +} +table.data-table.activity-log-table .activity-log-col-kind { + white-space: nowrap; +} +table.data-table.activity-log-table .activity-log-col-route code { + display: inline-block; + max-width: 12rem; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: top; +} +table.data-table.activity-log-table code { + font-size: 0.8rem; + line-height: 1.45; + vertical-align: top; +} +table.data-table.activity-log-table .badge { + vertical-align: top; +} +.activity-log-changes-cell { + min-width: 220px; + max-width: 420px; +} +.activity-log-subject, +.activity-log-actor { + font-size: 0.85rem; +} +.activity-log-subject strong, +.activity-log-actor strong { + color: var(--text); +} +.activity-log-role { + font-weight: 400; + color: var(--text-secondary); + font-size: 0.78rem; +} +.activity-log-error { + margin-top: 6px; + font-size: 0.8rem; + color: var(--danger); + line-height: 1.35; +} +.activity-change-raw { + font-size: 0.72rem; + color: var(--text-secondary); + margin-top: 2px; + font-family: var(--font-mono, ui-monospace, monospace); + word-break: break-all; +} +.activity-changes-table { + width: 100%; + border-collapse: collapse; + font-size: 0.78rem; +} +.activity-changes-table th { + text-align: left; + font-weight: 600; + color: var(--text-secondary); + padding: 3px 10px 3px 0; + vertical-align: top; + white-space: nowrap; + line-height: 1.45; +} +.activity-changes-table td { + padding: 3px 0; + word-break: break-word; + color: var(--text); + vertical-align: top; + line-height: 1.45; +} +.activity-changes-table tr + tr th, +.activity-changes-table tr + tr td { + border-top: 1px solid var(--border); + padding-top: 4px; +} + +/* Page header */ +.page-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 28px; + flex-wrap: wrap; + gap: 12px; +} +.page-header-start { + display: flex; + align-items: center; + gap: 12px; + min-width: 0; +} +.page-header-titles { + min-width: 0; +} +body.logged-in .page-header-titles { + position: relative; + padding-left: 14px; +} +body.logged-in .page-header-titles::before { + content: ''; + position: absolute; + left: 0; + top: 3px; + bottom: 3px; + width: 3px; + border-radius: 2px; + background: var(--accent-gradient); + box-shadow: 0 0 8px rgba(15, 118, 110, 0.2); +} +.page-header h1 { + font-size: 1.5rem; + font-weight: 700; + margin: 0; + letter-spacing: -0.02em; +} +.page-header .actions { + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; +} +.btn-home { + display: inline-flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + min-width: 40px; + padding: 0; + flex-shrink: 0; +} +.btn-home svg { + width: 20px; + height: 20px; +} + +.user-actions-cell { + display: flex; + flex-wrap: wrap; + gap: 6px; + align-items: center; +} + +.mobile-logout-btn, +.mobile-change-password-btn { + display: none; + position: fixed; + top: 12px; + z-index: 150; + box-shadow: var(--shadow); +} +.mobile-logout-btn { + right: 12px; +} +.mobile-change-password-btn { + right: 96px; +} +.coach-supervisor-select { + max-width: 100%; + min-width: 160px; + font-size: 0.85rem; + padding: 8px 10px; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--surface-input); + color: var(--text); +} + +/* Questionnaire dashboard root (not .q-grid — avoids grid layout on nested cards) */ +.q-dashboard-root { + display: block; + width: 100%; +} + +/* Questionnaire dashboard — category sections with horizontal card rows */ +.q-dashboard-groups { + display: flex; + flex-direction: column; + gap: 28px; + width: 100%; +} + +.q-category-group { + display: flex; + flex-direction: column; + gap: 12px; + width: 100%; + min-width: 0; +} + +.q-category-title { + margin: 0; + font-size: 1.05rem; + font-weight: 600; + color: var(--text-secondary); + letter-spacing: -0.01em; + padding-bottom: 4px; + padding-left: 12px; + border-bottom: 1px solid var(--border); + position: relative; +} +.q-category-title::before { + content: ''; + position: absolute; + left: 0; + top: 0.15em; + width: 4px; + height: 1em; + border-radius: 2px; + background: var(--accent-gradient-soft); +} + +.q-category-title .category-title-input { + font: inherit; + color: inherit; + letter-spacing: inherit; + width: 100%; + max-width: 480px; +} + +.category-german-input { + width: 100%; + min-width: 140px; + max-width: 320px; + padding: 6px 10px; + font-size: .9rem; + border: 1px solid var(--border); + border-radius: 6px; + background: var(--surface); + color: inherit; +} + +.category-german-input:focus { + outline: none; + border-color: var(--primary); + box-shadow: 0 0 0 2px var(--primary-subtle); +} + +.category-german-input:disabled { + opacity: .6; +} + +/* Category rows: horizontal flow, wrap when the line is full */ +.q-category-grid { + display: flex; + flex-flow: row wrap; + align-items: stretch; + align-content: flex-start; + gap: 16px; + width: 100%; + min-width: 0; + margin: 0; +} + +.q-category-grid > .q-card { + flex: 0 0 300px; + width: 300px; + max-width: 100%; + min-width: 0; + box-sizing: border-box; +} + +/* Questionnaire card grid (legacy / other pages) */ +.q-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 16px; +} + +.q-card .drag-handle { + margin-right: 6px; + cursor: grab; + color: var(--text-secondary); +} +.q-card { + position: relative; + overflow: hidden; + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + padding: 20px; + box-shadow: var(--shadow), var(--shadow-inset); + transition: box-shadow .2s, transform .2s, border-color .2s; + cursor: pointer; +} +.q-card::after { + content: ''; + position: absolute; + inset: 0 0 auto 0; + height: 2px; + background: var(--accent-gradient-soft); + opacity: 0; + transition: opacity 0.2s; +} +.q-card:hover { + box-shadow: var(--shadow-lg), var(--accent-glow); + border-color: color-mix(in srgb, var(--primary-border) 60%, var(--border)); + transform: translateY(-3px); +} +.q-card:hover::after { + opacity: 1; +} +.q-card-header { + display: flex; + align-items: start; + justify-content: space-between; + margin-bottom: 12px; +} +.q-card-title { + font-size: 1.05rem; + font-weight: 600; +} +.q-card-meta { + display: flex; + gap: 16px; + color: var(--text-secondary); + font-size: .85rem; +} +.q-card-actions { + display: flex; + gap: 6px; + margin-top: 16px; + padding-top: 12px; + border-top: 1px solid var(--border); +} + +/* Tables — scroll inside wrapper so sticky headers stay visible */ +.table-wrapper { + overflow: auto; + max-height: min(72vh, calc(100vh - 220px)); + -webkit-overflow-scrolling: touch; +} +table.data-table { + width: 100%; + border-collapse: separate; + border-spacing: 0; + font-size: .875rem; +} +table.data-table th, +table.data-table td { + padding: 10px 12px; + text-align: left; + border-bottom: 1px solid var(--border); +} +table.data-table th { + font-weight: 600; + color: var(--text-secondary); + font-size: .8rem; + text-transform: uppercase; + letter-spacing: .04em; + background: var(--table-header-bg); + position: sticky; + top: 0; + z-index: 2; + box-shadow: 0 1px 0 var(--border); +} +table.data-table tbody tr { + transition: background 0.12s ease; +} +table.data-table tbody tr:nth-child(even) td { + background: var(--table-stripe-bg); +} +table.data-table tbody tr:nth-child(even) td.sticky-col { + background: color-mix(in srgb, var(--table-stripe-bg) 80%, var(--surface)); +} +table.data-table tr:hover td { + background: var(--row-hover-bg); +} +table.data-table tbody tr:hover td.sticky-col { + background: color-mix(in srgb, var(--row-hover-bg) 85%, var(--surface)); +} +table.data-table tbody tr:hover td:first-child:not(.sticky-col) { + box-shadow: inset 3px 0 0 var(--primary); +} +table.data-table .sticky-col { + position: sticky; + left: 0; + z-index: 1; + background: var(--surface); + box-shadow: 2px 0 4px var(--sticky-shadow); +} +table.data-table thead th.sticky-col { + z-index: 5; + top: 0; + background: var(--table-header-bg); +} +th.sortable { cursor: pointer; user-select: none; } +th.sortable::after { content: ' \2195'; opacity: .4; } +th.sort-asc::after { content: ' \2191'; opacity: 1; } +th.sort-desc::after { content: ' \2193'; opacity: 1; } + +/* Accordion / question list */ +.question-list { + list-style: none; +} +.question-item { + border: 1px solid var(--border); + border-radius: var(--radius-lg); + margin-bottom: 8px; + background: var(--surface); + box-shadow: var(--shadow), var(--shadow-inset); +} +.question-header { + display: flex; + align-items: center; + gap: 10px; + padding: 12px 16px; + cursor: pointer; + user-select: none; +} +.question-header:hover { background: var(--row-hover-bg); } +.drag-handle { + cursor: grab; + color: var(--text-secondary); + font-size: 1.1rem; + line-height: 1; +} +.drag-handle:active { cursor: grabbing; } +.question-header .q-text { + flex: 1; + font-weight: 500; +} +.question-header .q-type { + font-size: .8rem; + color: var(--text-secondary); + background: var(--surface-raised); + padding: 2px 8px; + border-radius: 4px; +} +.question-body { + display: none; + padding: 0 16px 16px 42px; +} +.question-item.open { + border-color: color-mix(in srgb, var(--primary-border) 45%, var(--border)); + box-shadow: var(--shadow), 0 0 0 1px color-mix(in srgb, var(--primary-border) 30%, transparent); +} +.question-item.open .question-body { + display: block; +} +.question-item.open .question-header { + background: color-mix(in srgb, var(--primary-subtle) 40%, transparent); +} +.question-item.open .chevron { + transform: rotate(90deg); +} +.chevron { + transition: transform .2s; + font-size: .8rem; + color: var(--text-secondary); +} + +/* Answer options inside a question */ +.option-list { + list-style: none; + margin-top: 8px; +} +.option-item { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 10px; + border: 1px solid var(--border); + border-radius: 6px; + margin-bottom: 4px; + background: var(--surface-muted); + font-size: .875rem; +} +.option-item .opt-text { flex: 1; } +.option-item .opt-points { + font-weight: 600; + color: var(--primary-text); + font-size: .8rem; + background: var(--primary-subtle); + padding: 1px 6px; + border-radius: 4px; +} + +/* Translations panel */ +.translations-panel { + margin-top: 12px; + padding: 12px; + background: var(--surface-muted); + border-radius: var(--radius-lg); + border: 1px solid var(--border); + box-shadow: var(--shadow), var(--shadow-inset); +} +.translations-panel h4 { + font-size: .85rem; + margin-bottom: 8px; + color: var(--text-secondary); +} +.translation-row { + display: flex; + gap: 8px; + align-items: center; + margin-bottom: 6px; +} +.translation-row input[type="text"] { + flex: 1; + padding: 4px 8px; + border: 1px solid var(--border); + border-radius: 4px; + font-size: .85rem; +} +.translation-row .lang-code { + font-weight: 600; + font-size: .8rem; + width: 30px; + text-transform: uppercase; + color: var(--text-secondary); +} + +/* Inline edit */ +.inline-edit { + border: 1px solid transparent; + background: transparent; + padding: 2px 4px; + font: inherit; + border-radius: 4px; + width: 100%; + transition: border-color .15s; +} +.inline-edit:hover { border-color: var(--border); } +.inline-edit:focus { + outline: none; + border-color: var(--primary); + background: var(--surface); + box-shadow: 0 0 0 3px var(--focus-ring); +} + +/* Login */ +body.login-active .main-content { + margin-left: 0; + padding: 0; + min-height: 100vh; +} + +.login-screen { + position: relative; + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; + padding: 48px 32px; + overflow: hidden; + isolation: isolate; +} + +.login-screen__backdrop { + position: absolute; + inset: 0; + z-index: 0; + background: + linear-gradient(135deg, rgba(221, 244, 239, 0.78) 0%, rgba(243, 247, 246, 0.96) 48%, rgba(231, 245, 241, 0.72) 100%), + var(--bg); +} + +.login-grid { + position: absolute; + inset: 0; + background-image: + linear-gradient(rgba(100, 116, 139, 0.08) 1px, transparent 1px), + linear-gradient(90deg, rgba(100, 116, 139, 0.08) 1px, transparent 1px); + background-size: 48px 48px; + mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.85), transparent 92%); +} + +.login-screen__content { + position: relative; + z-index: 1; + width: 100%; + max-width: 980px; +} + +.login-brand { + margin-bottom: 26px; +} + +.login-brand__title { + font-size: 1.75rem; + font-weight: 750; + line-height: 1.25; + letter-spacing: -0.02em; + margin-bottom: 6px; + color: var(--text); +} + +.login-brand__subtitle { + font-size: 0.875rem; + color: var(--primary-text); + font-weight: 600; +} + +.login-card { + position: relative; + overflow: hidden; + min-height: 360px; + padding: 30px 32px 32px; + border-color: rgba(207, 225, 221, 0.9); + border-radius: 16px; + background: rgba(255, 255, 255, 0.94); + box-shadow: 0 18px 50px rgba(16, 42, 39, 0.10), 0 2px 8px rgba(16, 42, 39, 0.06); +} +.login-card::before { + content: ''; + position: absolute; + inset: 0 0 auto 0; + height: 2px; + background: var(--accent-gradient); + opacity: 0.75; +} + +.login-card__heading { + font-size: 1.25rem; + font-weight: 750; + margin-bottom: 6px; +} + +.login-card__lead { + font-size: 0.925rem; + color: var(--text-secondary); + margin-bottom: 26px; +} + +.login-options { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 20px; + align-items: stretch; +} + +.login-options .login-card { + min-width: 0; +} + +.login-options .login-card[hidden] { + display: none; +} + +.login-card--sso { + display: flex; + flex-direction: column; +} + +.login-card--sso .login-sso { + margin-top: 28px; +} + +.login-sso__logo { + display: flex; + align-items: center; + justify-content: center; + min-height: 96px; + margin-bottom: 16px; + padding: 0 8px; +} + +.login-sso__logo img { + display: block; + max-width: 100%; + max-height: 90px; + object-fit: contain; +} + +.login-sso__logo span { + display: none; + color: var(--text); + font-size: 1rem; + font-weight: 700; + text-align: center; +} + +.login-sso__logo--fallback span { + display: block; +} + +.login-form .form-group { + margin-bottom: 18px; +} + +.login-sso { + margin-top: 0; +} + +.login-sso__button { + min-height: 50px; + background: #ffffff; + border-color: rgba(15, 118, 110, 0.34); + color: var(--primary-text); + font-weight: 600; +} + +.login-sso__button:disabled { + background: #f8fafc; + border-color: rgba(148, 163, 184, 0.36); + color: #64748b; + cursor: default; + box-shadow: none; +} + +.login-form .btn-primary { + min-height: 50px; + margin-top: 4px; + font-size: 0.95rem; +} + +.login-alert { + margin-top: 12px; + padding: 10px 12px; + border-radius: var(--radius); + font-size: 0.85rem; + line-height: 1.45; +} +.login-alert--error { + color: var(--danger-text); + background: var(--danger-subtle); + border: 1px solid var(--danger-border); +} + +.login-card__footnote { + margin-top: 22px; + text-align: center; + font-size: 0.8rem; + color: var(--text-secondary); +} + +.error-text { color: var(--danger); font-size: .85rem; margin-top: 8px; } + +@media (max-width: 480px) { + .login-screen { + padding: 20px 16px; + align-items: flex-start; + padding-top: 48px; + } + + .login-options { + grid-template-columns: 1fr; + } + + .login-card { + min-height: 0; + padding: 26px 22px; + } + + .login-brand__title { + font-size: 1.45rem; + } + +} + +@media (prefers-reduced-motion: reduce) { + .spinner { + animation: none; + } + .q-card:hover, + .home-kpi-link:hover, + .home-link-card:hover { + transform: none; + } +} + +/* Toast */ +#toastContainer { + position: fixed; + top: 20px; + right: 20px; + z-index: 9999; + display: flex; + flex-direction: column; + gap: 8px; +} +.toast { + padding: 12px 16px 12px 14px; + border-radius: var(--radius-lg); + font-size: .875rem; + font-weight: 500; + line-height: 1.45; + box-shadow: var(--shadow-lg), 0 8px 24px rgba(15, 23, 42, 0.1); + opacity: 0; + transform: translateX(40px); + transition: opacity .3s, transform .3s; + max-width: 360px; + border: 1px solid var(--border); + border-left-width: 4px; + background: var(--surface-raised); + color: var(--text); +} +.toast.show { opacity: 1; transform: translateX(0); } +.toast-success { + background: var(--toast-success-bg); + color: var(--toast-success-fg); + border-color: var(--toast-success-border); + border-left-color: var(--success); +} +.toast-error { + background: var(--toast-error-bg); + color: var(--toast-error-fg); + border-color: var(--toast-error-border); + border-left-color: var(--danger); +} +.toast-info { + background: var(--toast-info-bg); + color: var(--toast-info-fg); + border-color: var(--toast-info-border); + border-left-color: var(--primary); +} + +/* Filter bar */ +.filter-bar { + display: flex; + gap: 12px; + align-items: center; + margin-bottom: 16px; + flex-wrap: wrap; +} +.filter-bar select, +.filter-bar input { + padding: 8px 10px; + border: 1px solid var(--border); + border-radius: var(--radius); + font-size: .85rem; + background: var(--surface-input); + color: var(--text); +} +.filter-bar input[type="search"], +.filter-bar input[type="text"].filter-search { + min-width: 180px; +} +.pagination-bar { + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; + margin-top: 12px; + font-size: .85rem; + color: var(--text-secondary); +} +.pagination-bar .btn { + min-width: 2.5rem; +} +.data-toolbar-hint { + font-size: .8rem; + color: var(--text-secondary); +} +#qdb-table th.col-key { + font-size: .75rem; + font-weight: 500; + text-transform: none; + letter-spacing: 0; + max-width: 140px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +#qdb-table thead tr:nth-child(1) th[data-id] { + font-size: .7rem; + max-width: 120px; + overflow: hidden; + text-overflow: ellipsis; +} + +/* Empty state */ +.empty-state { + text-align: center; + padding: 56px 24px; + color: var(--text-secondary); + border: 1px dashed color-mix(in srgb, var(--border) 80%, var(--primary-border)); + border-radius: var(--radius-lg); + background: color-mix(in srgb, var(--surface-muted) 70%, transparent); +} +.empty-state svg { + margin-bottom: 16px; + opacity: .5; + color: var(--primary-text); +} +.empty-state h3 { margin-bottom: 8px; color: var(--text); } + +/* Dragging */ +.question-item.dragging { + opacity: .5; + border-style: dashed; +} +.option-item.dragging { + opacity: .5; + border-style: dashed; +} + +/* Loading spinner */ +.spinner { + display: inline-block; + width: 26px; + height: 26px; + border: 2px solid var(--border); + border-top-color: var(--primary); + border-right-color: #0e7490; + border-radius: 50%; + animation: spin .7s linear infinite; + box-shadow: 0 0 10px rgba(15, 118, 110, 0.12); +} +#app > .spinner, +.main-content > .spinner, +[id$="Content"] > .spinner:only-child, +[id$="Root"] > .spinner:only-child { + display: block; + margin: 56px auto; +} +@keyframes spin { to { transform: rotate(360deg); } } + +/* Responsive */ +@media (max-width: 768px) { + .sidebar { + transform: translateX(-100%); + transition: transform .3s; + } + .sidebar.open { transform: translateX(0); } + .main-content { margin-left: 0; padding: 16px; } + .q-grid { grid-template-columns: 1fr; } + body.logged-in .mobile-logout-btn, + body.logged-in .mobile-change-password-btn { + display: inline-flex; + } + body.logged-in .page-header { + padding-right: 200px; + } +} + +@media (max-width: 520px) { + .q-category-grid > .q-card { + flex: 1 1 100%; + width: 100%; + } +} + +/* Role badges */ +.badge-admin { background: var(--badge-admin-bg); color: var(--badge-admin-fg); } +.badge-supervisor { background: var(--badge-supervisor-bg); color: var(--badge-supervisor-fg); } +.badge-coach { background: var(--badge-coach-bg); color: var(--badge-coach-fg); } +.badge-you { background: var(--badge-you-bg); color: var(--badge-you-fg); font-size: .7rem; } + +/* Dashboard — category keys panel */ +.category-keys-table code { + font-size: .8rem; +} +table.data-table tbody tr.row-orphan-category td { + background: color-mix(in srgb, var(--surface-hover) 60%, transparent); +} + +/* Assignment page — panels fill remaining viewport; lists scroll inside */ +.main-content:has(.assign-page) { + display: flex; + flex-direction: column; + min-height: 100vh; + min-height: 100dvh; +} + +.main-content:has(.assign-page) .page-header { + flex-shrink: 0; +} + +.main-content:has(.assign-page) #assignContent { + flex: 1 1 auto; + min-height: 0; + display: flex; + flex-direction: column; +} + +.assign-page { + display: flex; + flex-direction: column; + flex: 1 1 auto; + min-height: 0; + gap: 16px; +} + +.assign-layout { + display: grid; + grid-template-columns: minmax(260px, min(32vw, 380px)) minmax(0, 1fr); + grid-template-rows: minmax(0, 1fr); + gap: 16px; + align-items: stretch; + flex: 1 1 auto; + min-height: 0; +} + +.assign-layout > .assign-coaches, +.assign-layout > .assign-clients-card { + display: flex; + flex-direction: column; + padding: 0; + overflow: hidden; + height: 100%; + max-height: 100%; + min-height: 0; +} + +.assign-coaches .assign-panel-body { + flex: 1 1 0; + min-height: 0; + overflow-y: auto; + overflow-x: hidden; +} + +.assign-panel-header { + flex-shrink: 0; + padding: 14px 16px; + border-bottom: 1px solid var(--border); + background: var(--surface-muted); +} + +.assign-panel-header h3 { + margin: 0 0 10px; + font-size: 1rem; +} + +.assign-panel-header .filter-bar { + margin-bottom: 0; +} + +.assign-panel-body { + flex: 1 1 auto; + min-height: 0; + overflow: auto; + -webkit-overflow-scrolling: touch; + padding: 6px 8px 10px; +} + +.assign-clients-toolbar { + flex-shrink: 0; + padding: 14px 16px; + border-bottom: 1px solid var(--border); + display: flex; + flex-direction: column; + gap: 12px; +} + +.assign-clients-toolbar-top { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; + flex-wrap: wrap; +} + +.assign-clients-toolbar-top h3 { + margin: 0; + font-size: 1rem; + line-height: 1.4; +} + +.assign-clients-actions { + display: flex; + gap: 6px; + flex-shrink: 0; +} + +.assign-clients-filters { + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; +} + +.assign-clients-filters .filter-search { + flex: 1 1 200px; + min-width: 160px; + max-width: 100%; +} + +.assign-clients-filters select { + flex: 0 1 auto; + min-width: 140px; + max-width: 100%; +} + +.assign-table-panel { + flex: 1 1 auto; + min-height: 0; + display: flex; + flex-direction: column; +} + +.assign-table-panel .table-wrapper { + flex: 1 1 auto; + min-height: 0; + max-height: none; + overflow: auto; + box-sizing: border-box; +} + +.assign-panel-footer, +.assign-clients-footer { + flex-shrink: 0; + padding: 10px 16px; + border-top: 1px solid var(--border); + background: var(--surface); +} + +.assign-panel-footer .pagination-bar, +.assign-clients-footer .pagination-bar { + margin-top: 0; +} + +.assign-clients-footer .pagination-bar + .selection-info { + margin-top: 8px; +} + +.assign-action-card { + flex-shrink: 0; + padding: 14px 16px; +} + +.assign-action-row { + display: flex; + gap: 10px; + align-items: center; + flex-wrap: wrap; + margin-top: 10px; +} + +.assign-action-row select { + flex: 1 1 220px; + min-width: 180px; + padding: 8px 12px; + border: 1px solid var(--border); + border-radius: 6px; + background: var(--surface); + color: inherit; +} + +.assign-action-card h4 { + margin: 0; + font-size: .95rem; +} + +@media (max-width: 1100px) { + .assign-layout { + grid-template-columns: minmax(220px, 280px) minmax(0, 1fr); + } +} + +@media (max-width: 900px) { + .assign-layout { + grid-template-columns: 1fr; + grid-template-rows: minmax(0, 38%) minmax(0, 1fr); + } +} + +/* Coach list */ +.coach-list { + list-style: none; + margin: 0; + padding: 0; +} + +.coach-list-item { + padding: 10px 12px; + border-radius: 8px; + cursor: pointer; + transition: background .15s, border-color .15s; + display: flex; + flex-direction: column; + gap: 2px; + border: 1px solid transparent; + margin-bottom: 4px; +} + +.coach-list-item:last-child { + margin-bottom: 0; +} + +.coach-list-item:hover { + background: var(--surface-hover); +} + +.coach-list-item.active { + background: var(--primary-subtle); + border-color: var(--primary); +} + +.coach-list-empty { + padding: 20px 12px; + text-align: center; + color: var(--text-secondary); + font-size: .875rem; +} + +.coach-name { + font-weight: 600; + font-size: .9rem; + word-break: break-word; +} + +.coach-supervisor { + font-size: .8rem; + color: var(--text-secondary); + word-break: break-word; +} + +.coach-client-count { + font-size: .75rem; + color: var(--primary); + font-weight: 500; + margin-top: 2px; +} + +/* Selection info bar */ +.selection-info { + margin: 0; + padding: 8px 12px; + background: var(--primary-subtle); + border: 1px solid var(--primary-border); + border-radius: var(--radius); + font-size: .875rem; + color: var(--primary-text); + box-shadow: 0 0 0 1px rgba(15, 118, 110, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.6); +} + +/* Tabs */ +.tab-bar { + display: flex; + gap: 4px; + padding: 4px; + border-bottom: none; + margin-bottom: 20px; + background: var(--surface-muted); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-inset); +} +.tab-bar button { + padding: 9px 18px; + font-size: .9rem; + font-weight: 500; + background: transparent; + border: none; + border-radius: var(--radius); + cursor: pointer; + color: var(--text-secondary); + transition: color .15s, background .15s, box-shadow .15s; +} +.tab-bar button:hover { + color: var(--text); + background: var(--surface-hover); +} +.tab-bar button.active { + color: var(--primary-text); + background: var(--primary-subtle); + box-shadow: inset 0 0 0 1px var(--primary-border), 0 1px 3px rgba(15, 118, 110, 0.08); +} + +/* Metadata form in editor */ +.meta-form { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + gap: 16px; + margin-bottom: 24px; +} +@media (max-width: 600px) { + .meta-form { grid-template-columns: 1fr; } +} + +/* Inline form cards (add/edit question, add/edit option) */ +.inline-form-card { + background: var(--surface-muted); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + padding: 16px; + margin-bottom: 12px; + box-shadow: var(--shadow), var(--shadow-inset); +} +.inline-form-card h4 { + font-size: .9rem; + font-weight: 600; + margin-bottom: 12px; +} +.inline-form-card .form-group { margin-bottom: 10px; } + +/* Type select */ +.type-select { + width: 100%; + padding: 10px 12px; + border: 1px solid var(--border); + border-radius: var(--radius); + font-size: .9rem; + font-family: var(--font); + background: var(--surface-input); + color: var(--text); +} +.type-select:focus { + outline: none; + border-color: var(--primary); + box-shadow: 0 0 0 3px var(--focus-ring); +} + +/* Checkbox label */ +.checkbox-label { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: .875rem; + cursor: pointer; + user-select: none; +} +.checkbox-label input[type="checkbox"] { + width: 16px; + height: 16px; + accent-color: var(--primary); + cursor: pointer; +} + +/* Modal dialog */ +.modal-overlay { + position: fixed; + inset: 0; + z-index: 10000; + display: flex; + align-items: center; + justify-content: center; + padding: 16px; + background: + radial-gradient(ellipse 70% 50% at 50% 40%, rgba(15, 118, 110, 0.06), transparent 70%), + color-mix(in srgb, var(--bg) 50%, rgba(15, 23, 42, 0.35)); + backdrop-filter: blur(4px); +} +.modal-overlay[hidden] { + display: none; +} +body.modal-open { + overflow: hidden; +} +.modal-dialog { + width: 100%; + max-width: 460px; + max-height: calc(100vh - 32px); + overflow: auto; + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-lg), var(--shadow-inset); + padding: 24px; +} +.modal-dialog h2 { + margin: 0 0 6px; + font-size: 1.15rem; +} +.modal-dialog .modal-subtitle { + margin: 0 0 16px; + font-size: 0.9rem; + color: var(--text-secondary); + line-height: 1.45; +} +.modal-dialog .modal-subtitle strong { + color: var(--text); +} +.modal-dialog .modal-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; + justify-content: flex-end; + margin-top: 20px; +} +.password-mode-fieldset { + border: none; + margin: 0 0 16px; + padding: 0; +} +.password-mode-fieldset > legend { + font-size: 0.85rem; + font-weight: 600; + margin-bottom: 10px; + padding: 0; +} +.password-mode-options { + display: flex; + flex-direction: column; + gap: 8px; +} +.password-mode-option { + display: block; + padding: 12px 14px; + border: 1px solid var(--border); + border-radius: var(--radius); + cursor: pointer; + transition: border-color 0.15s, background 0.15s; +} +.password-mode-option:hover { + background: var(--surface-hover); +} +.password-mode-option:has(input:checked) { + border-color: var(--primary); + background: color-mix(in srgb, var(--primary) 8%, var(--surface)); +} +.password-mode-option input { + margin: 0 10px 0 0; + vertical-align: top; + accent-color: var(--primary); +} +.password-mode-option-title { + display: block; + font-weight: 600; + font-size: 0.9rem; + margin-bottom: 4px; +} +.password-mode-option-desc { + display: block; + font-size: 0.8rem; + color: var(--text-secondary); + line-height: 1.4; + margin-left: 26px; +} +.modal-hint { + font-size: 0.8rem; + color: var(--text-secondary); + margin: 0 0 14px; + line-height: 1.45; + padding: 10px 12px; + background: var(--surface-muted); + border-radius: var(--radius); + border: 1px solid var(--border); +} + +.confirm-modal-message { + margin: 0 0 16px; + line-height: 1.5; +} +.confirm-modal-message p { + margin: 0 0 10px; +} +.confirm-modal-message p:last-child { + margin-bottom: 0; +} +.confirm-modal-choices { + margin-bottom: 16px; +} +.confirm-modal--danger .confirm-modal-dialog { + border-color: color-mix(in srgb, var(--danger-border) 40%, var(--border)); +} + +/* Options builder (inside add-question form) */ +.options-builder { + border-top: 1px solid var(--border); + margin-top: 12px; + padding-top: 12px; +} + +/* Pending options list (before question is saved) */ +.pending-options-list { + list-style: none; + margin-bottom: 6px; +} +.pending-option-item { + display: flex; + align-items: center; + gap: 8px; + padding: 5px 10px; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 6px; + margin-bottom: 4px; + font-size: .875rem; +} +.pending-option-item .opt-text { flex: 1; } +.pending-options-empty { + font-size: .8rem; + color: var(--text-secondary); + padding: 4px 0 6px; + font-style: italic; +} + +/* Branch indicator on answer options */ +.opt-branch { + font-size: .75rem; + font-weight: 600; + color: var(--badge-branch-fg); + background: var(--badge-branch-bg); + padding: 1px 6px; + border-radius: 4px; + white-space: nowrap; +} + +/* Config section inside question forms */ +.config-section { + border-top: 1px solid var(--border); + margin-top: 12px; + padding-top: 12px; +} +.config-section .form-group textarea { + width: 100%; + padding: 8px 12px; + border: 1px solid var(--border); + border-radius: 6px; + font-family: var(--font); + resize: vertical; +} + +/* Condition details toggle */ +.condition-details summary { + list-style: none; +} +.condition-details summary::-webkit-details-marker { + display: none; +} +.condition-details summary::before { + content: '\25B6 '; + font-size: .7rem; + transition: transform .2s; + display: inline-block; + margin-right: 4px; +} +.condition-details[open] summary::before { + transform: rotate(90deg); +} + +/* ── Translations page ── */ +.trans-page { padding: 20px 24px; } +.trans-controls { + display: flex; + flex-wrap: wrap; + gap: 16px 24px; + margin-bottom: 16px; +} +.trans-control { + display: flex; + flex-direction: column; + gap: 6px; + flex: 1; + min-width: 200px; + max-width: 360px; + font-size: .85rem; + color: var(--text-secondary); +} +.trans-control span { font-weight: 500; } +.trans-select { + padding: 8px 12px; + border: 1px solid var(--border); + border-radius: 6px; + font-size: .9rem; + font-family: var(--font); + background: var(--surface); +} +.trans-select:focus { + outline: none; + border-color: var(--primary); + box-shadow: 0 0 0 3px var(--focus-ring); +} +.trans-type-select { width: auto; min-width: 140px; flex-shrink: 0; } +.trans-lang-panel { + margin-bottom: 16px; + padding: 12px 14px; + background: var(--surface-muted); + border: 1px solid var(--border); + border-radius: var(--radius-lg); +} +.trans-lang-panel summary { + cursor: pointer; + font-weight: 600; + font-size: .9rem; + color: var(--text); + user-select: none; +} +.trans-lang-panel[open] summary { margin-bottom: 12px; } +.trans-lang-panel .lang-chips { margin-bottom: 10px; } +.trans-hint { padding: 24px 0; text-align: center; font-size: .9rem; } +.text-muted { color: var(--text-secondary); } +.required-mark { color: var(--danger); font-weight: 600; } +.trans-sheet { + border: 1px solid var(--border); + border-radius: var(--radius-lg); + background: var(--surface); + display: flex; + flex-direction: column; + max-height: calc(100vh - 420px); + overflow: hidden; +} +.trans-sheet-body { + overflow-y: auto; + flex: 1 1 auto; + min-height: 0; +} +.trans-sheet .trans-list-header { + flex-shrink: 0; + border: none; + border-bottom: 1px solid var(--border); + border-radius: 0; + margin: 0; +} +.trans-list-header { + display: grid; + grid-template-columns: minmax(140px, 1.1fr) 1fr 1fr; + gap: 12px; + padding: 8px 14px; + font-size: .75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: .04em; + color: var(--text-secondary); + background: var(--table-header-bg); +} +.trans-list-header-sticky { + position: sticky; + top: 0; + z-index: 2; + box-shadow: 0 1px 0 var(--border); +} +.trans-top-bar { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 12px 20px; + margin-bottom: 12px; + padding: 12px 14px; + background: var(--surface-muted); + border: 1px solid var(--border); + border-radius: var(--radius-lg); +} +.trans-top-stats { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 12px 20px; + font-size: .88rem; + color: var(--text-secondary); +} +.trans-top-actions { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 10px; +} +.trans-save-status { + font-size: .82rem; + color: var(--text-secondary); +} +.trans-save-status[data-state="saving"] { color: var(--primary); } +.trans-save-status[data-state="error"] { color: var(--danger); } +.trans-toolbar-field { + min-width: 180px; + max-width: 280px; + margin: 0; +} +.trans-empty-hint { margin-top: 12px; } +.trans-qn-block { + margin-bottom: 28px; + padding-bottom: 8px; + border-bottom: 1px solid var(--border); +} +.trans-qn-block:last-child { border-bottom: none; margin-bottom: 0; } +.trans-qn-hidden { display: none !important; } +.trans-qn-title { + font-size: 1.05rem; + font-weight: 600; + color: var(--text); + margin: 0 0 14px; +} +.trans-group { + margin-bottom: 18px; +} +.trans-group-hidden { display: none !important; } +.trans-group-title { + font-size: .8rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: .04em; + color: var(--text-secondary); + margin: 0 0 8px; + padding-bottom: 6px; + border-bottom: 1px solid var(--border); +} +.trans-groups-flat { display: block; } +.trans-group-rows { display: block; } +.trans-scope-block .trans-group-details:first-child { border-top: none; } +.trans-group-details { + border: none; + border-top: 1px solid var(--border); + border-radius: 0; + margin: 0; + background: transparent; +} +.trans-group-details.trans-group-hidden { display: none !important; } +.trans-group-summary { + cursor: pointer; + font-weight: 600; + font-size: .8rem; + padding: 10px 14px; + list-style: none; + user-select: none; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: .04em; + background: var(--surface-muted); +} +.trans-group-summary::-webkit-details-marker { display: none; } +.trans-group-summary::before { + content: '▸ '; + color: var(--text-secondary); +} +.trans-group-details[open] > .trans-group-summary::before { + content: '▾ '; +} +.trans-qn-section { + border: none; + border-top: 1px solid var(--border); + border-radius: 0; + margin: 0; + background: transparent; +} +.trans-qn-section.trans-qn-hidden { display: none !important; } +.trans-qn-section:first-child { border-top: none; } +.trans-qn-summary { + cursor: pointer; + font-weight: 600; + font-size: .92rem; + padding: 12px 14px; + list-style: none; + user-select: none; + color: var(--text); + background: var(--surface-muted); +} +.trans-qn-summary::-webkit-details-marker { display: none; } +.trans-qn-summary::before { + content: '▸ '; + color: var(--text-secondary); +} +.trans-qn-section[open] > .trans-qn-summary::before { + content: '▾ '; +} +.trans-sheet-body-stack .trans-group-summary { + font-size: .78rem; + padding: 8px 14px 8px 22px; + background: transparent; +} +.trans-list { + border: 1px solid var(--border); + border-radius: var(--radius-lg); + overflow: hidden; +} +.trans-list-item { + display: grid; + grid-template-columns: minmax(140px, 1.1fr) 1fr 1fr; + gap: 12px; + align-items: center; + padding: 10px 14px; + border-bottom: 1px solid var(--border); +} +.trans-key-labels { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} +.trans-key-primary { + font-size: .88rem; + font-weight: 500; + color: var(--text); + line-height: 1.35; + overflow: hidden; + text-overflow: ellipsis; +} +.trans-key-secondary { + font-size: .72rem; + font-family: var(--font-mono, ui-monospace, monospace); + color: var(--text-secondary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.trans-sheet-body > .trans-scope-block .trans-group-details:last-child .trans-list-item:last-child, +.trans-sheet-body > .trans-groups-flat .trans-group-details:last-child .trans-list-item:last-child { + border-bottom: none; +} +.trans-list-item:nth-child(even) { + background: var(--table-stripe-bg); +} +.trans-list-item:hover { background: var(--row-hover-bg); } +.trans-list-item-missing { + background: var(--trans-missing-bg); + box-shadow: inset 3px 0 0 var(--trans-missing-accent); +} +.trans-list-item-missing:hover { background: var(--trans-missing-bg-hover); } +.trans-list-key { + display: flex; + align-items: flex-start; + gap: 6px; + min-width: 0; +} +.trans-key-text { + font-family: monospace; + font-size: .8rem; + line-height: 1.35; + color: var(--text-secondary); + word-break: break-word; +} +.trans-list-source { + min-width: 0; +} +.trans-source-text { + display: block; + font-size: .9rem; + line-height: 1.4; + color: var(--text); + word-break: break-word; +} +.lang-chip-fixed { + background: var(--lang-chip-fixed-bg); + border-color: var(--lang-chip-fixed-border); +} +.trans-list-item .trans-cell-input { + border: 1px solid var(--border); + background: var(--surface); + border-radius: 6px; + padding: 8px 10px; +} +.trans-input-missing { + border-color: var(--trans-missing-border) !important; + background: var(--surface) !important; + box-shadow: inset 0 0 0 1px rgba(217, 119, 6, 0.18); +} +.trans-input-missing::placeholder { + color: var(--trans-missing-placeholder); + font-style: normal; +} + +@media (max-width: 768px) { + .trans-list-header, + .trans-list-item { + grid-template-columns: 1fr; + gap: 6px; + } + .trans-list-header { display: none; } + .trans-list-item .trans-list-key::before { + content: 'Label'; + display: block; + font-size: .7rem; + font-weight: 600; + text-transform: uppercase; + color: var(--text-secondary); + margin-bottom: 2px; + } + .trans-list-item .trans-list-source::before { + content: 'German'; + display: block; + font-size: .7rem; + font-weight: 600; + text-transform: uppercase; + color: var(--text-secondary); + margin-bottom: 2px; + } + .trans-list-item .trans-cell-input[data-field="translation"] { + margin-top: 4px; + } +} + +/* ── Language manager ── */ +.lang-manager { + background: var(--surface-muted); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + padding: 14px 16px; + margin-bottom: 20px; +} +.lang-chips { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 12px; +} +.lang-chip { + display: inline-flex; + align-items: center; + gap: 6px; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 20px; + padding: 4px 10px 4px 12px; + font-size: .85rem; +} +.lang-chip strong { color: var(--primary); } +.lang-chip-name { color: var(--text-secondary); } +.lang-chip-remove { + background: none; + border: none; + color: var(--text-secondary); + font-size: 1.1rem; + cursor: pointer; + padding: 0 2px; + line-height: 1; +} +.lang-chip-remove:hover { color: var(--danger); } +.lang-add-row { + display: flex; + gap: 8px; + align-items: center; +} +.lang-add-code { + width: 80px; + padding: 6px 10px; + border: 1px solid var(--border); + border-radius: 6px; + font-size: .85rem; +} +.lang-add-name { + width: 160px; + padding: 6px 10px; + border: 1px solid var(--border); + border-radius: 6px; + font-size: .85rem; +} + +/* ── Translations toolbar ── */ +.trans-toolbar { + display: flex; + gap: 16px; + align-items: center; + margin-bottom: 14px; + flex-wrap: wrap; +} +.trans-search-wrap { flex: 1; min-width: 200px; } +.trans-search-input { + width: 100%; + padding: 8px 12px; + border: 1px solid var(--border); + border-radius: 6px; + font-size: .9rem; + font-family: var(--font); +} +.trans-search-input:focus { + outline: none; + border-color: var(--primary); + box-shadow: 0 0 0 3px var(--focus-ring); +} +.trans-filter-types { + display: flex; + gap: 12px; + flex-shrink: 0; +} + +/* ── Translations table ── */ +.trans-table-wrapper { + overflow-x: auto; + border: 1px solid var(--border); + border-radius: var(--radius-lg); +} +.trans-table { + width: 100%; + border-collapse: collapse; + font-size: .875rem; +} +.trans-table th { + position: sticky; + top: 0; + background: var(--table-header-bg); + font-weight: 600; + text-align: left; + padding: 8px 10px; + border-bottom: 2px solid var(--border); + white-space: nowrap; + z-index: 1; +} +.trans-table td { padding: 2px 4px; } +.trans-table tbody tr { border-bottom: 1px solid var(--border); } +.trans-table tbody tr:nth-child(even) { + background: var(--table-stripe-bg); +} +.trans-table tbody tr:hover { background: var(--row-hover-bg); } +.trans-row-hidden { display: none !important; } +#tabContent-translations .trans-list, +#tabContent-translations .trans-list-header { + margin-top: 0; +} + +.col-key { min-width: 180px; } +.col-type { width: 50px; text-align: center; } +.col-lang { min-width: 160px; } + +.cell-key { + font-family: monospace; + font-size: .82rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 280px; + padding: 6px 10px !important; +} +.cell-type { text-align: center; padding: 6px 4px !important; } + +.trans-type-badge { + display: inline-block; + font-size: .7rem; + font-weight: 700; + padding: 2px 6px; + border-radius: 4px; + text-transform: uppercase; + letter-spacing: .3px; +} +.badge-q { background: var(--badge-q-bg); color: var(--badge-q-fg); } +.badge-opt { background: var(--badge-opt-bg); color: var(--badge-opt-fg); } +.badge-app { background: var(--badge-app-bg); color: var(--badge-app-fg); } +.badge-str { background: var(--badge-str-bg); color: var(--badge-str-fg); } + +.cell-trans { position: relative; } +.cell-missing { background: var(--trans-missing-bg); } + +.trans-cell-input { + width: 100%; + padding: 5px 8px; + border: 1px solid transparent; + border-radius: 4px; + font-size: .85rem; + font-family: var(--font); + background: transparent; + transition: border-color .15s, background .15s; +} +.trans-cell-input:hover { + border-color: var(--border); + background: var(--surface); +} +.trans-cell-input:focus { + outline: none; + border-color: var(--primary); + background: var(--surface); + box-shadow: 0 0 0 2px var(--focus-ring); +} +.trans-cell-input::placeholder { + color: var(--trans-missing-placeholder); + font-style: normal; + font-size: .8rem; +} +.trans-cell-input:not(.trans-input-missing)::placeholder { + color: var(--text-secondary); +} +.trans-cell-input.save-flash { + animation: saveFlash .4s ease; +} +@keyframes saveFlash { + 0% { background: var(--save-flash-bg); } + 100% { background: var(--surface); } +} +.trans-list-item .trans-cell-input.save-flash { + animation: saveFlash .4s ease; +} + +/* ── Stats bar ── */ +.trans-stats { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 12px; + font-size: .82rem; + color: var(--text-secondary); +} +.trans-stats-progress { + display: flex; + align-items: center; + gap: 8px; +} +.trans-progress-bar { + width: 120px; + height: 6px; + background: var(--progress-track); + border-radius: 3px; + overflow: hidden; +} +.trans-progress-fill { + display: block; + height: 100%; + background: var(--success); + border-radius: 3px; + transition: width .3s ease; +} + +.field-hint { + display: block; + font-size: 0.8rem; + color: var(--text-secondary); + margin-top: 4px; +} +.field-hint code { + font-size: 0.75rem; +} + +.badge-warn { + background: var(--badge-warn-bg); + color: var(--badge-warn-fg); +} + +.opt-key { + font-size: 0.75rem; + margin-right: 8px; + color: var(--text-secondary); +} + +/* Callouts */ +.callout-warn { + margin-bottom: 16px; + padding: 12px; + background: var(--badge-warn-bg); + border-radius: 8px; + color: var(--badge-warn-fg); +} +.callout-danger { + margin-bottom: 12px; + padding: 12px; + background: var(--toast-error-bg); + border-radius: 8px; + color: var(--toast-error-fg); +} +.insights-kpi-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + gap: 16px; +} +.page-header-sub { + margin: 6px 0 0; + font-size: 0.9rem; + color: var(--text-secondary); + font-weight: normal; +} +.home-kpi-link { + text-decoration: none; + color: inherit; + position: relative; + overflow: hidden; + transition: border-color 0.2s, background 0.2s, transform 0.2s, box-shadow 0.2s; +} +.home-kpi-link::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: var(--accent-gradient); + opacity: 0.75; +} +.home-kpi-link:hover { + border-color: var(--primary-border); + background: var(--surface-hover); + transform: translateY(-3px); + box-shadow: var(--shadow-lg), var(--accent-glow); +} +.home-dashboard-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 20px; + margin-top: 20px; +} +.home-link-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 10px; +} +.home-link-card { + display: block; + position: relative; + padding: 14px 36px 14px 16px; + border: 1px solid var(--border); + border-radius: var(--radius-lg); + background: var(--surface-muted); + text-decoration: none; + color: inherit; + transition: border-color 0.2s, background 0.2s, box-shadow 0.2s, transform 0.2s; + box-shadow: var(--shadow), var(--shadow-inset); +} +.home-link-card::after { + content: '→'; + position: absolute; + right: 14px; + top: 50%; + transform: translateY(-50%); + font-size: 1rem; + color: var(--primary-text); + opacity: 0.35; + transition: transform 0.2s, opacity 0.2s; +} +.home-link-card:hover { + border-color: var(--primary-border); + background: var(--surface-hover); + box-shadow: var(--shadow-lg), 0 0 0 1px var(--primary-border); + transform: translateX(2px); +} +.home-link-card:hover::after { + opacity: 1; + transform: translateY(-50%) translateX(3px); +} +.home-link-title { + display: block; + font-weight: 600; + font-size: 0.95rem; + margin-bottom: 4px; +} +.home-link-desc { + display: block; + font-size: 0.8rem; + color: var(--text-secondary); + line-height: 1.35; +} +.insights-kpi { + padding: 20px; + text-align: center; +} +.insights-kpi-value { + font-size: 2rem; + font-weight: 700; + line-height: 1.2; + letter-spacing: -0.03em; + background: linear-gradient(160deg, var(--text) 20%, var(--primary-text) 100%); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} +.insights-kpi-label { + font-size: 0.85rem; + color: var(--text-secondary); + margin-top: 6px; +} +.insights-charts-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 16px; + margin-top: 20px; +} +.insights-chart-card h3 { + margin: 0 0 4px; + font-size: 1rem; + letter-spacing: -0.01em; +} +body.logged-in .insights-chart-card.card { + transition: border-color 0.2s, box-shadow 0.2s; +} +body.logged-in .insights-chart-card.card:hover { + border-color: var(--primary-border); + box-shadow: var(--shadow-lg), 0 0 16px rgba(15, 118, 110, 0.06); +} +.insights-chart-hint { + margin: 0 0 14px; + font-size: 0.82rem; + color: var(--text-secondary); +} +.insights-vchart { + display: flex; + align-items: flex-end; + gap: 6px; + height: 160px; + padding: 8px 4px 0; + border-bottom: 1px solid var(--border); +} +.insights-vchart-col { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + align-items: center; + height: 100%; +} +.insights-vchart-bar-wrap { + flex: 1; + width: 100%; + max-width: 36px; + display: flex; + align-items: flex-end; + justify-content: center; +} +.insights-vchart-bar { + width: 100%; + min-height: 2px; + border-radius: 4px 4px 0 0; + background: linear-gradient(180deg, var(--primary-hover), var(--primary)); + transition: height 0.2s ease; +} +.insights-vchart-value { + font-size: 0.7rem; + color: var(--text-secondary); + margin-top: 4px; + line-height: 1; +} +.insights-vchart-label { + font-size: 0.65rem; + color: var(--text-secondary); + margin-top: 2px; + text-align: center; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.insights-hchart { + display: flex; + flex-direction: column; + gap: 10px; + margin-bottom: 16px; +} +.insights-hchart-row { + display: grid; + grid-template-columns: minmax(100px, 28%) 1fr minmax(40px, auto); + gap: 10px; + align-items: center; + font-size: 0.85rem; +} +.insights-hchart-label { + color: var(--text); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.insights-hchart-track { + height: 22px; + background: var(--surface-muted); + border-radius: 4px; + overflow: hidden; +} +.insights-hchart-fill { + height: 100%; + min-width: 2px; + border-radius: 4px; + background: linear-gradient(90deg, var(--primary), var(--primary-hover)); +} +.insights-hchart-fill--warn { + background: linear-gradient(90deg, #d97706, var(--warning)); +} +.insights-hchart-fill--muted { + background: var(--border); +} +.insights-hchart-num { + text-align: right; + color: var(--text-secondary); + font-variant-numeric: tabular-nums; +} +.followup-note-input { + width: 100%; + min-width: 180px; + max-width: 320px; + font-family: inherit; + font-size: 0.85rem; + padding: 8px; + border: 1px solid var(--border); + border-radius: 6px; + background: var(--surface); + color: var(--text); + resize: vertical; +} +.export-actions-cell { + display: flex; + flex-wrap: wrap; + gap: 6px; + align-items: center; +} +.export-actions-cell .btn { + margin: 0; +} +.coach-recent-panel { + padding: 12px 8px 8px 32px; + background: var(--surface-muted); + border-radius: 8px; + margin: 4px 0 8px; +} +.coach-recent-scroll { + max-height: min(420px, 55vh); + display: flex; + flex-direction: column; + overflow: hidden; +} +.coach-recent-toolbar { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 10px 16px; + margin-bottom: 10px; + flex-shrink: 0; +} +.coach-recent-toolbar .filter-search { + flex: 1; + min-width: 180px; + max-width: 320px; +} +.coach-client-groups { + flex: 1; + overflow-y: auto; + padding-right: 4px; + margin-bottom: 8px; +} +.coach-client-group { + border: 1px solid var(--border); + border-radius: 8px; + margin-bottom: 8px; + background: var(--surface); + overflow: hidden; +} +.coach-client-toggle { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 6px 10px; + width: 100%; + text-align: left; + padding: 10px 12px !important; + justify-content: flex-start; +} +.coach-client-meta { + font-size: 0.8rem; + color: var(--text-secondary); + font-weight: normal; +} +.coach-client-uploads { + padding: 0 12px 12px 32px; + border-top: 1px solid var(--border); +} +.coach-recent-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; + flex-shrink: 0; + padding-top: 4px; + border-top: 1px solid var(--border); +} +.data-table-compact td, +.data-table-compact th { + padding: 8px 10px; + font-size: 0.85rem; +} +.btn-link { + background: none; + border: none; + padding: 0 6px 0 0; + color: var(--accent); + cursor: pointer; + min-width: auto; +} +.coach-detail-row td { + border-top: none; + padding-top: 0; +} +.clients-list-filters { + flex-wrap: wrap; + align-items: center; + gap: 8px; +} + +.clients-archive-filter { + flex: 0 0 auto; + min-width: 120px; +} + +.client-actions-cell { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} + +.table-row-actions { + display: inline-flex; + align-items: stretch; + border: 1px solid var(--border); + border-radius: 8px; + overflow: hidden; + background: var(--surface-muted); +} + +.table-row-actions .btn { + border: none; + border-radius: 0; + box-shadow: none; + margin: 0; + min-height: 30px; +} + +.table-row-actions .btn:hover { + box-shadow: none; +} + +.table-row-actions .btn + .btn { + border-left: 1px solid var(--border); +} + +.table-row-actions .archive-client-btn, +.table-row-actions .restore-client-btn { + background: var(--surface); + color: var(--text-secondary); +} + +.table-row-actions .archive-client-btn:hover, +.table-row-actions .restore-client-btn:hover { + background: var(--surface-hover); + color: var(--text); +} + +.table-row-actions .delete-client-btn { + padding-left: 12px; + padding-right: 12px; +} + +.followup-note-cell { + min-width: 200px; +} + +.followup-note-cell .followup-note-input { + width: 100%; + margin-bottom: 8px; +} + +.followup-note-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; + justify-content: space-between; +} + +.followup-actions-cell { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} + +.client-row-archived td { + color: var(--text-secondary); +} + +.client-archived-badge { + display: inline-block; + margin-left: 6px; + padding: 1px 7px; + border-radius: 999px; + font-size: 0.72rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.03em; + color: var(--text-secondary); + background: var(--surface-raised); + border: 1px solid var(--border); + vertical-align: middle; +} + +.client-detail-panel { + padding: 12px 8px 8px 32px; + background: var(--surface-muted); + border-radius: 8px; + margin: 4px 0 8px; +} +.client-detail-meta { + margin: 0 0 12px; + font-size: 0.85rem; + color: var(--text-secondary); +} +.client-qn-panel { + padding: 10px 8px 10px 28px; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 8px; + margin-top: 8px; +} +.client-version-panel { + padding: 8px 8px 8px 28px; + margin-top: 8px; + border-left: 3px solid var(--primary-border); +} +.client-detail-row td { + border-top: none; + padding-top: 0; +} +tr.answer-changed td { + background: rgba(251, 191, 36, 0.1); +} +tr.answer-changed .answer-value-cell { + font-weight: 600; + color: var(--badge-warn-fg); +} +.client-qn-summary { + font-size: 0.82rem; + color: var(--text-secondary); + margin-left: 8px; +} + +input:disabled, +select:disabled { + background: var(--surface-muted); + color: var(--text-secondary); + cursor: not-allowed; +} + +/* Questionnaire availability condition editor */ +.condition-editor { + padding: 16px; + background: var(--surface-muted); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + box-shadow: var(--shadow), var(--shadow-inset); +} +.condition-mode-panel { + margin-top: 12px; +} +.condition-requires-list { + display: flex; + flex-direction: column; + gap: 6px; + max-height: 200px; + overflow-y: auto; + padding: 8px; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 6px; +} +.condition-requires-item { + margin: 0; +} +.condition-answer-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 12px; +} +.condition-answer-grid select, +.condition-editor .form-group > select { + width: 100%; +} +.condition-anyof-rule { + margin-bottom: 12px; + padding: 12px; +} +.condition-json-fallback { + width: 100%; + font-family: ui-monospace, monospace; + font-size: 0.85rem; + padding: 8px 12px; + border: 1px solid var(--border); + border-radius: 6px; + background: var(--surface); + color: var(--text); +} +.condition-locked-message .meta-form { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; +} +@media (max-width: 720px) { + .condition-locked-message .meta-form { + grid-template-columns: 1fr; + } +} + +/* Questionnaire preview tab (all-in-one) */ +.qp-shell { + display: flex; + flex-direction: column; + gap: 12px; +} +.qp-banner { + padding: 10px 14px; + background: var(--primary-subtle); + border: 1px solid var(--primary-border); + border-radius: var(--radius); + font-size: .875rem; + color: var(--primary-text); +} +.qp-toolbar { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 8px; +} +.qp-visible-count { + font-size: .875rem; + color: var(--text-secondary); +} +.qp-scroll { + padding: 0; + max-height: min(75vh, 900px); + overflow-y: auto; +} +.qp-form { + display: flex; + flex-direction: column; +} +.qp-section { + padding: 20px 24px; + border-bottom: 1px solid var(--border); +} +.qp-section:last-child { + border-bottom: none; +} +.qp-section[hidden] { + display: none !important; +} +.qp-section-header { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 14px; +} +.qp-section-num { + display: flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + border-radius: 50%; + background: var(--primary-subtle); + color: var(--primary-text); + font-size: .8rem; + font-weight: 600; + flex-shrink: 0; +} +.qp-section-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; + font-size: .8rem; + color: var(--text-secondary); +} +.qp-id { + font-size: .8rem; + padding: 2px 6px; + background: var(--surface-muted); + border-radius: 4px; +} +.qp-type { + font-size: .8rem; +} +.qp-question-text { + font-size: 1rem; + font-weight: 500; + margin-bottom: 14px; + line-height: 1.45; +} +.qp-note { + font-size: .875rem; + color: var(--text-secondary); + margin-bottom: 12px; + padding: 8px 12px; + background: var(--surface-muted); + border-radius: 6px; +} +.qp-note-after { margin-top: 12px; margin-bottom: 0; } +.qp-hint { + font-size: .85rem; + color: var(--text-secondary); + margin-top: 8px; +} +.qp-options { + display: flex; + flex-direction: column; + gap: 8px; +} +.qp-option { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 12px; + border: 1px solid var(--border); + border-radius: 6px; + cursor: pointer; + transition: border-color .15s, background .15s; +} +.qp-option:hover { + border-color: var(--primary-border); + background: var(--surface-muted); +} +.qp-option input { flex-shrink: 0; } +.qp-select { + width: 100%; + max-width: 320px; + padding: 10px 12px; + border: 1px solid var(--border); + border-radius: 6px; + font-size: .9rem; + background: var(--surface); + color: var(--text); +} +.qp-select-sm { max-width: 140px; } +.qp-date-row { + display: flex; + flex-wrap: wrap; + gap: 8px; +} +.qp-text-input { + width: 100%; + max-width: 480px; + padding: 10px 12px; + border: 1px solid var(--border); + border-radius: 6px; + font-size: .9rem; + background: var(--surface); + color: var(--text); +} +.qp-slider { + display: flex; + align-items: center; + gap: 16px; +} +.qp-slider input[type=range] { flex: 1; max-width: 400px; } +.qp-slider-value { + min-width: 3rem; + font-weight: 600; + font-variant-numeric: tabular-nums; +} +.qp-form-stack { + display: flex; + flex-direction: column; + gap: 12px; + max-width: 360px; +} +.qp-form-stack label { + display: flex; + flex-direction: column; + gap: 4px; + font-size: .875rem; + font-weight: 500; +} +.qp-form-stack input { + padding: 10px 12px; + border: 1px solid var(--border); + border-radius: 6px; + font-size: .9rem; + background: var(--surface); + color: var(--text); +} +.qp-review-note { + margin-bottom: 4px; +} +.qp-review-codes .qp-readonly-field { + background: var(--bg, #f4f4f5); + color: var(--text-secondary); + cursor: default; + border-style: dashed; + user-select: all; +} +.qp-glass-table { + overflow-x: auto; + margin-top: 8px; +} +.qp-glass-table table { + width: 100%; + border-collapse: collapse; + font-size: .85rem; +} +.qp-glass-table th, +.qp-glass-table td { + border: 1px solid var(--border); + padding: 8px 10px; + text-align: center; +} +.qp-glass-table th:first-child, +.qp-glass-table td:first-child { + text-align: left; + font-weight: 500; +} +.qp-empty { + padding: 40px 20px; + text-align: center; + color: var(--text-secondary); +} + +/* Glass-scale symptom rows in editor */ +.glass-symptoms-list { + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-direction: column; + gap: 8px; +} +.glass-symptom-row { + padding: 10px 12px; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 6px; +} +.glass-symptoms-empty { + padding: 12px; + color: var(--text-secondary); + font-size: .85rem; + font-style: italic; +} +.glass-score-grid { + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: 8px; + margin-top: 10px; + padding-top: 10px; + border-top: 1px dashed var(--border); +} +.glass-score-cell label { + display: block; + font-size: .7rem; + color: var(--text-secondary); + margin-bottom: 2px; +} +.glass-score-cell input { + width: 100%; + padding: 6px 8px; + border: 1px solid var(--border); + border-radius: 4px; +} +.value-score-grid { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 8px; + max-height: 200px; + overflow-y: auto; +} +.value-score-row { + display: flex; + align-items: center; + gap: 6px; + padding: 4px 8px; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 4px; + font-size: .85rem; +} +.value-score-label { + min-width: 2rem; + font-weight: 600; + font-variant-numeric: tabular-nums; +} +.value-score-pts { + width: 4rem; + padding: 4px 6px; + border: 1px solid var(--border); + border-radius: 4px; +} + +.modal-dialog-wide { + max-width: 640px; +} + +/* Scoring profiles on questionnaire dashboard */ +#scoringProfilesPanel { + width: 100%; + margin-top: 28px; +} +.scoring-profiles-section { + margin-top: 0; +} +.scoring-profiles-card { + padding: 24px; +} +.scoring-section-title { + margin: 0 0 6px; + font-size: 1.05rem; + font-weight: 600; + color: var(--text); +} +.scoring-section-desc, +.scoring-empty-hint { + margin: 0; + font-size: .875rem; + line-height: 1.5; + color: var(--text-secondary); +} +.scoring-empty-hint { + padding: 8px 0; +} +.scoring-profiles-list { + margin-top: 18px; + display: flex; + flex-direction: column; + gap: 12px; +} +.scoring-profile-card { + border: 1px solid var(--border); + border-radius: var(--radius-lg); + padding: 16px 18px; + background: var(--surface-muted); + color: var(--text); + box-shadow: var(--shadow), var(--shadow-inset); +} +.scoring-profile-card-top { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 16px; +} +.scoring-profile-card-main { + min-width: 0; + flex: 1; +} +.scoring-profile-name-row { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; + margin-bottom: 6px; +} +.scoring-profile-name-row strong { + font-size: 1rem; + color: var(--text); +} +.scoring-profile-desc { + margin: 0 0 8px; + font-size: .875rem; + line-height: 1.45; + color: var(--text-secondary); +} +.scoring-profile-bands { + margin: 0; + font-size: .85rem; + color: var(--primary-text); + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; +} +.scoring-profile-member-count { + margin: 12px 0 8px; + font-size: .8rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: .04em; + color: var(--text-secondary); +} +.scoring-profile-members { + border-top: 1px solid var(--border); + padding-top: 4px; +} +.scoring-profile-card-actions { + display: flex; + gap: 6px; + flex-shrink: 0; +} +.scoring-profile-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 12px; +} +.scoring-modal-section-title { + margin: 18px 0 0; + font-size: .95rem; + color: var(--text); +} +.scoring-profile-active-row { + margin-bottom: 12px; +} +.checkbox-inline { + display: inline-flex; + align-items: center; + gap: 8px; + font-size: .9rem; + color: var(--text); + cursor: pointer; +} +.scoring-profile-picker { + border: 1px solid var(--border); + border-radius: var(--radius-lg); + padding: 12px; + background: var(--surface-muted); + box-shadow: var(--shadow), var(--shadow-inset); +} +.scoring-profile-picker input, +.scoring-profile-picker select { + padding: 6px 10px; + border: 1px solid var(--border); + border-radius: 6px; + font-size: .875rem; + font-family: var(--font); + background: var(--surface); + color: var(--text); +} +.scoring-profile-picker input:focus, +.scoring-profile-picker select:focus { + outline: none; + border-color: var(--primary); + box-shadow: 0 0 0 3px var(--focus-ring); +} +.scoring-profile-add-row { + display: flex; + gap: 8px; + align-items: center; + margin-top: 10px; + padding-top: 10px; + border-top: 1px solid var(--border); +} +.scoring-profile-add-row select { + flex: 1; + min-width: 0; +} +.scoring-band-labels { + margin: 4px 0 8px; + min-height: 1.5rem; +} +.scoring-band-ranges-table { + display: flex; + flex-direction: column; + gap: 8px; + margin-bottom: 8px; +} +.scoring-band-range-row { + display: grid; + grid-template-columns: 100px 1fr 1fr; + gap: 10px; + align-items: center; +} +.scoring-band-range-row.scoring-band-range-header { + font-size: .75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: .04em; + color: var(--text-secondary); + padding-bottom: 4px; + border-bottom: 1px solid var(--border); +} +.scoring-band-range-row input { + width: 100%; + padding: 8px 10px; + border: 1px solid var(--border); + border-radius: 6px; + font-size: .875rem; + font-family: var(--font); + background: var(--surface); + color: var(--text); +} +.scoring-band-range-row input:focus { + outline: none; + border-color: var(--primary); + box-shadow: 0 0 0 3px var(--focus-ring); +} +.scoring-band-max-placeholder { + color: var(--text-secondary); + font-size: 1.1rem; + padding: 8px 10px; +} +.scoring-band-strips { + display: flex; + flex-wrap: wrap; + gap: 8px; +} +.scoring-profile-q-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 10px 0; + border-bottom: 1px solid var(--border); + color: var(--text); +} +.scoring-profile-q-row:last-child { border-bottom: none; } +.scoring-profile-q-name { + flex: 1; + min-width: 120px; + font-weight: 500; + color: var(--text); +} +.scoring-profile-q-weight { + color: var(--text-secondary); + font-variant-numeric: tabular-nums; +} +.scoring-profile-q-actions { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; + justify-content: flex-end; +} +.sp-weight-wrap { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: .8rem; + color: var(--text-secondary); +} +.sp-weight-wrap input { + width: 72px; +} +.band-badge { + display: inline-block; + padding: 4px 10px; + border-radius: 999px; + font-size: .75rem; + font-weight: 600; + letter-spacing: .02em; +} +.band-badge.band-green { background: #edf8f2; color: #1b5e20; border: 1px solid #b8dfc8; } +.band-badge.band-yellow { background: #fff6e7; color: #6a4a00; border: 1px solid #f5d08a; } +.band-badge.band-red { background: #fdeeee; color: #b71c1c; border: 1px solid #f5b4b8; } +.band-badge.band-none { background: #eef8f5; color: #4f5f5c; border: 1px solid #cfe1dd; } + +/* Client list — scoring profiles */ +.client-scoring-legend { + margin: 0 0 14px; + padding: 12px 14px; + border-radius: 8px; + border: 1px solid var(--border); + background: var(--surface-elevated); +} +.client-scoring-legend-title { + margin: 0 0 6px; +} +.client-scoring-legend-body { + margin: 0 0 10px; + line-height: 1.45; +} +.client-scoring-legend-body .band-badge { + margin: 0 2px; + vertical-align: middle; +} +.client-scoring-legend-dl { + display: grid; + gap: 8px 16px; + margin: 0; + font-size: .8125rem; +} +@media (min-width: 640px) { + .client-scoring-legend-dl { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} +.client-scoring-legend-dl dt { + font-weight: 600; + color: var(--text); + margin: 0; +} +.client-scoring-legend-dl dd { + margin: 2px 0 0; + color: var(--text-secondary); + line-height: 1.4; +} +.client-profile-scores { + display: flex; + flex-direction: column; + align-items: stretch; + gap: 8px; +} +.client-profile-score-block { + padding: 8px 10px; + border-radius: 8px; + border: 1px solid var(--border); + background: var(--surface-elevated); + font-size: .8125rem; + line-height: 1.35; +} +.client-profile-score-block.is-incomplete { + opacity: 0.85; +} +.client-profile-score-block.has-coach-override { + border-color: rgba(251, 191, 36, 0.45); +} +.client-profile-score-block-title { + font-weight: 600; + color: var(--text); + margin-bottom: 6px; +} +.client-profile-score-row { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 6px 8px; + margin-top: 4px; +} +.client-profile-score-row:first-of-type { + margin-top: 0; +} +.client-profile-score-label { + flex: 0 0 4.75rem; + color: var(--text-secondary); + font-size: .75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: .03em; +} +.client-profile-score-total { + color: var(--text-secondary); + font-variant-numeric: tabular-nums; + font-size: .75rem; +} +.client-profile-score-total::before { + content: "total "; + opacity: 0.75; +} +.client-profile-override-hint { + font-size: .7rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: .04em; + color: #ca8a04; +} + +/* Coach category picker (clients list) */ +.coach-band-picker { + position: relative; + display: inline-flex; + max-width: 100%; +} +.coach-band-trigger { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 2px 6px 2px 4px; + border: 1px solid var(--border); + border-radius: 999px; + background: var(--surface); + color: inherit; + cursor: pointer; + transition: border-color 0.15s, background 0.15s, box-shadow 0.15s; + max-width: 100%; +} +.coach-band-trigger:hover { + border-color: color-mix(in srgb, var(--primary) 45%, var(--border)); + background: var(--surface-hover); +} +.coach-band-picker.is-open .coach-band-trigger, +.coach-band-trigger:focus-visible { + outline: none; + border-color: var(--primary); + box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary) 22%, transparent); +} +.coach-band-trigger-badge .band-badge { + margin: 0; +} +.coach-band-trigger-caret { + font-size: .65rem; + color: var(--text-secondary); + line-height: 1; + flex-shrink: 0; +} +.coach-band-picker.is-saving .coach-band-trigger { + opacity: 0.6; + pointer-events: none; +} +.coach-band-menu { + min-width: 11.5rem; + padding: 10px; + border-radius: 10px; + border: 1px solid var(--border); + background: var(--surface); + box-shadow: var(--shadow-lg); +} +.coach-band-menu-label { + margin: 0 0 8px; + font-size: .7rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: .04em; + color: var(--text-secondary); +} +.coach-band-menu-options { + display: flex; + flex-direction: column; + gap: 4px; +} +.coach-band-option { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + width: 100%; + padding: 8px 10px; + border: 1px solid transparent; + border-radius: 8px; + background: transparent; + color: inherit; + cursor: pointer; + text-align: left; + transition: background 0.12s, border-color 0.12s; +} +.coach-band-option:hover { + background: var(--surface-hover); +} +.coach-band-option.is-selected { + border-color: color-mix(in srgb, var(--primary) 35%, var(--border)); + background: color-mix(in srgb, var(--primary) 8%, var(--surface)); +} +.coach-band-option-agree { + margin-top: 6px; + padding-top: 10px; + border-top: 1px solid var(--border); + border-radius: 0 0 8px 8px; +} +.coach-band-option-agree-label { + font-size: .75rem; + font-weight: 600; + color: var(--text-secondary); +} +.coach-band-confirm-compare { + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + gap: 12px 16px; + margin: 8px 0 4px; + padding: 16px; + border-radius: 10px; + border: 1px solid var(--border); + background: var(--surface-elevated); +} +.coach-band-confirm-col { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + min-width: 5.5rem; +} +.coach-band-confirm-label { + font-size: .7rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: .04em; + color: var(--text-secondary); +} +.coach-band-confirm-arrow { + font-size: 1.25rem; + color: var(--text-secondary); + line-height: 1; +} +.client-profile-score-hint, +.client-profile-score-meta { + margin: 6px 0 0; + font-size: .75rem; + color: var(--text-secondary); + line-height: 1.4; +} +.client-scoring-profiles-detail { + margin-bottom: 12px; +} +.scoring-profile-detail-card { + margin-top: 8px; +} +.client-profile-dots-cell { + min-width: 200px; + max-width: 320px; + vertical-align: top; +} +.client-profile-dots-empty { + color: var(--text-secondary); +} diff --git a/website/index.html b/website/index.html new file mode 100644 index 0000000..853cf32 --- /dev/null +++ b/website/index.html @@ -0,0 +1,99 @@ + + + + + + + + NAT-AS Ressourcenbarometer + + + + +

+ +
+ + + + + + diff --git a/website/js/api.js b/website/js/api.js new file mode 100644 index 0000000..b9ff151 --- /dev/null +++ b/website/js/api.js @@ -0,0 +1,214 @@ +/** + * Resolve API base for sibling (/website + /api) or subpath (/prefix/ + /prefix/api) layouts. + * Override per deploy: + */ +function resolveApiBase() { + if (typeof window !== 'undefined' && window.QDB_API_BASE) { + return String(window.QDB_API_BASE).replace(/\/$/, ''); + } + const fromRelative = new URL('../api', window.location.href); + const relPath = fromRelative.pathname.replace(/\/$/, ''); + const pageDir = window.location.pathname.replace(/\/[^/]*$/, '/') || '/'; + const mount = pageDir.split('/').filter(Boolean)[0]; + // Subpath deploy: /prefix/ is aliased to website/, so ../api wrongly becomes /api + if (mount && mount !== 'api' && mount !== 'website' && relPath === '/api') { + return `/${mount}/api`; + } + return relPath; +} + +const API_BASE = resolveApiBase(); + +/** Build a URL under the API base (path may include query string). */ +export function apiUrl(endpoint) { + const clean = String(endpoint).replace(/^\//, '').replace(/\.php(\?|$)/, '$1'); + return `${API_BASE}/${clean}`; +} + +function getToken() { + return localStorage.getItem('qdb_token'); +} + +const WEBSITE_ROLES = ['admin', 'supervisor']; + +let sessionCheckPromise = null; +let sessionCheckToken = null; + +/** Clear cached session validation (after logout or token change). */ +export function invalidateSessionCheck() { + sessionCheckPromise = null; + sessionCheckToken = null; +} + +/** Clear stored session and open the login page. */ +export function redirectToLogin() { + invalidateSessionCheck(); + localStorage.removeItem('qdb_token'); + localStorage.removeItem('qdb_user'); + localStorage.removeItem('qdb_role'); + window.location.hash = '#/login'; +} + +/** @returns {boolean} true if the response means the session is no longer valid */ +function isSessionInvalidResponse(res, json) { + if (res.status === 401) return true; + const errMsg = json.error?.message || (typeof json.error === 'string' ? json.error : ''); + const errCode = json.error?.code || ''; + return res.status === 401 + || res.status === 403 && ( + errCode === 'UNAUTHORIZED' + || errCode === 'PASSWORD_CHANGE_REQUIRED' + || errCode === 'FORBIDDEN' + || errMsg === 'Invalid or expired token' + || errMsg === 'Missing Bearer token' + || errMsg === 'Password change required before access' + ); +} + +/** + * Verify the stored token is still valid on the server. Redirects to login when not. + * @returns {Promise} + */ +export async function ensureValidSession() { + const token = getToken(); + if (!token) { + redirectToLogin(); + return false; + } + + if (sessionCheckPromise && sessionCheckToken === token) { + return sessionCheckPromise; + } + + sessionCheckToken = token; + sessionCheckPromise = (async () => { + try { + const data = await apiGet('session'); + if (!data.success || !data.valid) { + redirectToLogin(); + return false; + } + if (data.mustChangePassword) { + redirectToLogin(); + return false; + } + if (!WEBSITE_ROLES.includes(data.role)) { + redirectToLogin(); + return false; + } + if (data.user) localStorage.setItem('qdb_user', data.user); + if (data.role) localStorage.setItem('qdb_role', data.role); + return true; + } catch (e) { + if (e.message !== 'Session expired') { + redirectToLogin(); + } + return false; + } + })(); + + return sessionCheckPromise; +} + +async function handleAuthResponse(res) { + if (res.status !== 401 && res.status !== 403) return; + const json = await res.json().catch(() => ({})); + if (isSessionInvalidResponse(res, json)) { + redirectToLogin(); + throw new Error('Session expired'); + } + const errMsg = json.error?.message || json.error || ''; + throw new Error(errMsg || 'Permission denied'); +} + +async function apiFetch(endpoint, options = {}) { + const token = getToken(); + const headers = options.headers || {}; + headers['X-QDB-Client'] = 'web'; + if (token) headers['Authorization'] = `Bearer ${token}`; + if (!(options.body instanceof FormData)) { + headers['Content-Type'] = 'application/json; charset=UTF-8'; + } + + // Strip .php suffix for clean URLs + const cleanEndpoint = endpoint.replace(/\.php(\?|$)/, '$1'); + const res = await fetch(apiUrl(cleanEndpoint), { ...options, headers }); + + if (res.status === 401 || res.status === 403) { + await handleAuthResponse(res); + } + + return res; +} + +/** + * Unwrap the unified response envelope: + * {"ok": true, "data": ...} -> returns { success: true, ...data } + * {"ok": false, "error": ...} -> returns { success: false, error: message } + * Also supports legacy format for backward compat. + */ +function unwrap(json) { + if (typeof json.ok === 'boolean') { + if (json.ok) { + const d = json.data || {}; + return { success: true, ...(Array.isArray(d) ? { items: d } : d) }; + } + const err = json.error; + const msg = typeof err === 'string' ? err : (err?.message || 'Unknown error'); + return { success: false, error: msg, errorCode: typeof err === 'object' ? err?.code : null }; + } + return json; +} + +export async function apiGet(endpoint) { + const res = await apiFetch(endpoint); + return unwrap(await res.json()); +} + +export async function apiPost(endpoint, body) { + const res = await apiFetch(endpoint, { + method: 'POST', + body: JSON.stringify(body), + }); + return unwrap(await res.json()); +} + +export async function apiPut(endpoint, body) { + const res = await apiFetch(endpoint, { + method: 'PUT', + body: JSON.stringify(body), + }); + return unwrap(await res.json()); +} + +export async function apiPatch(endpoint, body) { + const res = await apiFetch(endpoint, { + method: 'PATCH', + body: JSON.stringify(body), + }); + return unwrap(await res.json()); +} + +export async function apiDelete(endpoint, body) { + const res = await apiFetch(endpoint, { + method: 'DELETE', + body: JSON.stringify(body), + }); + return unwrap(await res.json()); +} + +/** Authenticated file/download fetch (marks request as website for activity logging). */ +export async function apiDownloadFetch(url, options = {}) { + const token = getToken(); + const headers = { ...(options.headers || {}), 'X-QDB-Client': 'web' }; + if (token) headers['Authorization'] = `Bearer ${token}`; + const res = await fetch(url, { ...options, headers }); + const ct = res.headers.get('Content-Type') || ''; + if (ct.includes('application/json')) { + await handleAuthResponse(res); + } else if (res.status === 401 || res.status === 403) { + redirectToLogin(); + throw new Error('Session expired'); + } + return res; +} diff --git a/website/js/app.js b/website/js/app.js new file mode 100644 index 0000000..c5fb1a5 --- /dev/null +++ b/website/js/app.js @@ -0,0 +1,192 @@ +import { addRoute, startRouter, navigate } from './router.js'; +import { ensureValidSession, invalidateSessionCheck } from './api.js'; +import { loginPage } from './pages/login.js'; +import { homeDashboardPage } from './pages/home.js'; +import { questionnairesPage } from './pages/questionnaires.js'; +import { editorPage } from './pages/editor.js'; +import { resultsPage } from './pages/results.js'; +import { exportPage } from './pages/export.js'; +import { usersPage } from './pages/users.js'; +import { assignmentsPage } from './pages/assignments.js'; +import { clientsPage } from './pages/clients.js'; +import { translationsPage } from './pages/translations.js'; +import { devPage } from './pages/dev.js'; +import { insightsPage } from './pages/insights.js'; +import { coachesPage } from './pages/coaches.js'; +import { openChangeOwnPasswordModal } from './password-modal.js'; + +// Auth state +export function isLoggedIn() { + return !!localStorage.getItem('qdb_token'); +} +export function getRole() { + return localStorage.getItem('qdb_role') || ''; +} +export function getUser() { + return localStorage.getItem('qdb_user') || ''; +} +const WEBSITE_ROLES = ['admin', 'supervisor']; + +export function canAccessWebsite() { + return WEBSITE_ROLES.includes(getRole()); +} + +export function canEdit() { + return canAccessWebsite(); +} + +/** User-facing label for the coach role (internal key remains `coach`). */ +export function formatRoleLabel(role) { + if (role === 'coach') return 'Counselor'; + if (!role) return ''; + return role.charAt(0).toUpperCase() + role.slice(1); +} + +function clearSession() { + invalidateSessionCheck(); + localStorage.removeItem('qdb_token'); + localStorage.removeItem('qdb_role'); + localStorage.removeItem('qdb_user'); +} + +function logout() { + clearSession(); + navigate('#/login'); +} + +/** Link to home dashboard (#/). */ +export const HOME_HREF = '#/'; + +const HOME_ICON_SVG = ``; + +/** House icon button — returns to dashboard. */ +export function homeNavButton(title = 'Overview') { + return `${HOME_ICON_SVG}`; +} + +/** + * Standard page header with home button. + * @param {string} title HTML-safe title text + * @param {string} [actionsHTML] optional buttons inside .actions + * @param {{ subtitle?: string }} [opts] + */ +export function pageHeaderHTML(title, actionsHTML = '', opts = {}) { + const subtitle = opts.subtitle + ? `

${opts.subtitle}

` + : ''; + return ` + `; +} + +export function showToast(message, type = 'info') { + const container = document.getElementById('toastContainer'); + const toast = document.createElement('div'); + toast.className = `toast toast-${type}`; + toast.textContent = message; + container.appendChild(toast); + requestAnimationFrame(() => toast.classList.add('show')); + setTimeout(() => { + toast.classList.remove('show'); + setTimeout(() => toast.remove(), 300); + }, 3000); +} + +function authGuard(handler) { + return async (params) => { + if (!(await ensureValidSession())) return; + return handler(params); + }; +} + +function updateNav() { + const nav = document.getElementById('mainNav'); + const userInfo = document.getElementById('userInfo'); + document.body.classList.toggle('logged-in', isLoggedIn()); + const hash = window.location.hash.slice(1) || '/'; + document.body.classList.toggle('login-active', hash === '/login' || hash.startsWith('/login?')); + if (isLoggedIn()) { + nav.style.display = ''; + const role = getRole(); + const roleLabel = formatRoleLabel(role); + userInfo.textContent = `${getUser()} (${roleLabel})`; + + // Show/hide role-gated nav items + document.querySelectorAll('[data-nav-roles]').forEach(li => { + const allowed = li.dataset.navRoles.split(' '); + li.style.display = allowed.includes(role) ? '' : 'none'; + }); + + // Highlight active nav link + document.querySelectorAll('.sidebar-nav a').forEach(a => { + const href = a.getAttribute('href').slice(1); + let active = hash === href; + if (!active && href === '/questionnaires' && hash.startsWith('/questionnaire')) { + active = true; + } + if (!active && href !== '/' && hash.startsWith(href)) { + active = true; + } + a.classList.toggle('active', active); + }); + } else { + nav.style.display = 'none'; + userInfo.textContent = ''; + } +} + +function roleGuard(roles, handler) { + return async (params) => { + if (!(await ensureValidSession())) return; + if (!roles.includes(getRole())) { navigate('#/'); return; } + return handler(params); + }; +} + +// Routes +addRoute('/login', loginPage); +addRoute('/', roleGuard(['admin', 'supervisor'], homeDashboardPage)); +addRoute('/questionnaires', authGuard(questionnairesPage)); +addRoute('/questionnaire/new', authGuard(editorPage)); +addRoute('/questionnaire/:id/results', authGuard(resultsPage)); +addRoute('/questionnaire/:id', authGuard(editorPage)); +addRoute('/export', authGuard(exportPage)); +addRoute('/users', roleGuard(['admin', 'supervisor'], usersPage)); +addRoute('/assignments', roleGuard(['admin', 'supervisor'], assignmentsPage)); +addRoute('/clients', roleGuard(['admin', 'supervisor'], clientsPage)); +addRoute('/translations', roleGuard(['admin', 'supervisor'], translationsPage)); +addRoute('/insights', roleGuard(['admin', 'supervisor'], insightsPage)); +addRoute('/coaches', roleGuard(['admin', 'supervisor'], coachesPage)); +addRoute('/dev', roleGuard(['admin'], devPage)); + +// Nav link handling & logout +document.addEventListener('DOMContentLoaded', () => { + const bindLogout = (el) => { + if (!el) return; + el.addEventListener('click', (e) => { + e.preventDefault(); + logout(); + }); + }; + bindLogout(document.getElementById('logoutBtn')); + bindLogout(document.getElementById('mobileLogoutBtn')); + + const openOwnPassword = async () => { + if (!(await ensureValidSession())) return; + openChangeOwnPasswordModal(); + }; + document.getElementById('changePasswordBtn')?.addEventListener('click', openOwnPassword); + document.getElementById('mobileChangePasswordBtn')?.addEventListener('click', openOwnPassword); + + window.addEventListener('hashchange', updateNav); + updateNav(); + startRouter(); +}); diff --git a/website/js/client-archive.js b/website/js/client-archive.js new file mode 100644 index 0000000..35bbd66 --- /dev/null +++ b/website/js/client-archive.js @@ -0,0 +1,48 @@ +import { apiPatch } from './api.js'; +import { showToast } from './app.js'; +import { confirmAction } from './confirm-modal.js'; + +export function isClientArchived(c) { + return Number(c.archived) === 1; +} + +export async function confirmAndPatchClientArchive(clientCode, archived) { + if (!(await confirmAction({ + title: archived ? 'Archive client' : 'Restore client', + message: archived + ? `Archive client "${clientCode}"? They will be hidden from assignments, follow-up, and the mobile app. You can restore them later from the Clients page.` + : `Restore client "${clientCode}" to active lists?`, + confirmLabel: archived ? 'Archive' : 'Restore', + variant: archived ? 'danger' : 'default', + }))) return false; + + try { + await apiPatch('clients.php', { clientCode, archived }); + showToast( + archived ? `Client "${clientCode}" archived` : `Client "${clientCode}" restored`, + 'success' + ); + return true; + } catch (e) { + showToast(e.message, 'error'); + return false; + } +} + +/** + * @param {object} opts + * @param {string} opts.clientCode Escaped client code for data-code attributes + * @param {boolean} opts.archived + * @param {boolean} [opts.showDelete] + * @param {string} [opts.rawCode] Unescaped code for button labels (optional) + */ +export function clientTableActionsHTML({ clientCode, archived, showDelete = false }) { + const archiveBtn = archived + ? `` + : ``; + const deleteBtn = showDelete + ? `` + : ''; + + return `
${archiveBtn}${deleteBtn}
`; +} diff --git a/website/js/condition-editor.js b/website/js/condition-editor.js new file mode 100644 index 0000000..e900cea --- /dev/null +++ b/website/js/condition-editor.js @@ -0,0 +1,662 @@ +/** + * Questionnaire availability condition UI (same JSON schema as the Android app). + */ + +const CONDITION_MODES = [ + { value: 'always', label: 'Always available' }, + { value: 'requires', label: 'Requires other questionnaires completed' }, + { value: 'requires_answer', label: 'Requires completions + answer check' }, + { value: 'any_of', label: 'Any of these rules (OR)' }, + { value: 'custom', label: 'Custom JSON (advanced)' }, +]; + +const CHOICE_LAYOUT_TYPES = new Set([ + 'radio_question', + 'multi_check_box_question', + 'glass_scale_question', +]); + +export function defaultConditionMessageKey(questionnaireId) { + const id = (questionnaireId || 'new').replace(/^questionnaire_/, ''); + const slug = id.replace(/[^a-zA-Z0-9_]+/g, '_').replace(/^_+|_+$/g, '') || 'new'; + const normalized = /^[a-zA-Z]/.test(slug) ? slug : `k_${slug}`; + return `questionnaire_unlock_${normalized}`; +} + +function esc(s) { + const d = document.createElement('div'); + d.textContent = s ?? ''; + return d.innerHTML; +} + +function parseQuestionConfig(q) { + try { + return JSON.parse(q?.configJson || '{}'); + } catch { + return {}; + } +} + +/** Stable id stored in conditionJson.questionId (matches app endsWith check). */ +export function conditionQuestionId(q) { + const cfg = parseQuestionConfig(q); + const key = (q?.questionKey || cfg.questionKey || '').trim(); + if (key) return key; + if (q?.localId) return q.localId; + const parts = (q?.questionID || '').split('__'); + return parts[parts.length - 1] || ''; +} + +function conditionQuestionLabel(q) { + const id = conditionQuestionId(q); + const text = (q?.defaultText || '').trim(); + if (!text) return id; + const short = text.length > 48 ? `${text.slice(0, 45)}…` : text; + return `${id} — ${short}`; +} + +function questionsForQuestionnaire(questionsByQuestionnaire, questionnaireId) { + if (!questionnaireId) return []; + return questionsByQuestionnaire?.[questionnaireId] || []; +} + +function findQuestion(questionsByQuestionnaire, questionnaireId, questionId) { + if (!questionnaireId || !questionId) return null; + return questionsForQuestionnaire(questionsByQuestionnaire, questionnaireId) + .find(q => { + const cid = conditionQuestionId(q); + const local = q.localId || (q.questionID || '').split('__').pop(); + return questionId === cid || questionId === local || questionId === q.questionID; + }) || null; +} + +function questionMatchesSelection(q, selectedQuestionId) { + if (!selectedQuestionId) return false; + const cid = conditionQuestionId(q); + const local = q.localId || (q.questionID || '').split('__').pop(); + return selectedQuestionId === cid + || selectedQuestionId === local + || selectedQuestionId === q.questionID; +} + +function asRequiresList(obj) { + if (!obj) return []; + if (Array.isArray(obj.requiresCompleted)) { + return obj.requiresCompleted.filter(Boolean).map(String); + } + if (typeof obj.requiresCompleted === 'string' && obj.requiresCompleted) { + return [obj.requiresCompleted]; + } + return []; +} + +function hasQuestionCheck(obj) { + return Boolean( + obj?.questionnaire?.trim() && + obj?.questionId?.trim() && + obj?.operator?.trim() && + obj?.value != null && String(obj.value).trim() !== '' + ); +} + +function filterRequires(requires, selfId) { + if (!requires?.length) return []; + if (!selfId) return [...requires]; + return requires.filter(id => id !== selfId); +} + +/** Answer checks cannot reference the questionnaire being edited. */ +function sanitizeAnswerRule(rule, selfId) { + if (!selfId || rule.questionnaire !== selfId) { + return { ...rule, requires: filterRequires(rule.requires, selfId) }; + } + return { + ...rule, + requires: filterRequires(rule.requires, selfId), + questionnaire: '', + questionId: '', + value: '', + }; +} + +function parseRule(obj, selfId = '') { + if (!obj || typeof obj !== 'object') { + return { type: 'requires', requires: [], questionnaire: '', questionId: '', operator: '==', value: '' }; + } + if (obj.alwaysAvailable) { + return { type: 'always' }; + } + const rule = { + type: hasQuestionCheck(obj) ? 'requires_answer' : 'requires', + requires: asRequiresList(obj), + questionnaire: obj.questionnaire || '', + questionId: obj.questionId || '', + operator: obj.operator === '!=' ? '!=' : '==', + value: obj.value != null ? String(obj.value) : '', + }; + return sanitizeAnswerRule(rule, selfId); +} + +export function parseConditionForm(conditionJson, questionnaireId) { + const raw = (conditionJson || '{}').trim() || '{}'; + let obj; + try { + obj = JSON.parse(raw); + } catch { + return { + mode: 'custom', + customJson: raw, + messageKey: defaultConditionMessageKey(questionnaireId), + germanLabel: '', + }; + } + + const messageKey = (obj.messageKey || '').trim() || defaultConditionMessageKey(questionnaireId); + + if (obj.alwaysAvailable === true) { + return { mode: 'always', messageKey: '', germanLabel: '', customJson: '' }; + } + if (Array.isArray(obj.anyOf) && obj.anyOf.length) { + return { + mode: 'any_of', + rules: obj.anyOf.map(r => parseRule(r, questionnaireId)).filter(r => r.type !== 'always'), + messageKey, + germanLabel: '', + customJson: '', + }; + } + + const rule = parseRule(obj, questionnaireId); + return { + mode: rule.type === 'always' ? 'always' : rule.type, + requires: rule.requires, + questionnaire: rule.questionnaire, + questionId: rule.questionId, + operator: rule.operator, + value: rule.value, + messageKey, + germanLabel: '', + customJson: '', + }; +} + +function buildRule(rule) { + if (rule.type === 'always') { + return { alwaysAvailable: true }; + } + const out = {}; + if (rule.requires?.length) { + out.requiresCompleted = [...rule.requires]; + } + if (rule.type === 'requires_answer') { + out.questionnaire = (rule.questionnaire || '').trim(); + out.questionId = (rule.questionId || '').trim(); + out.operator = rule.operator === '!=' ? '!=' : '=='; + out.value = String(rule.value ?? '').trim(); + } + return out; +} + +function assertAnswerNotSelf(questionnaireId, selfId) { + if (selfId && questionnaireId === selfId) { + throw new Error('Answer check must use a different questionnaire (not this one)'); + } +} + +export function buildConditionJson(form, selfQuestionnaireId = '') { + if (form.mode === 'custom') { + const raw = (form.customJson || '{}').trim() || '{}'; + JSON.parse(raw); + return raw; + } + + let obj; + if (form.mode === 'always') { + obj = { alwaysAvailable: true }; + } else if (form.mode === 'any_of') { + const rules = (form.rules || []).map(buildRule).filter(r => Object.keys(r).length); + if (!rules.length) { + throw new Error('Add at least one rule for "Any of these rules"'); + } + obj = { anyOf: rules }; + } else { + obj = buildRule({ + type: form.mode, + requires: form.requires || [], + questionnaire: form.questionnaire, + questionId: form.questionId, + operator: form.operator, + value: form.value, + }); + if (form.mode === 'requires' && !obj.requiresCompleted?.length) { + throw new Error('Select at least one required questionnaire'); + } + if (form.mode === 'requires_answer') { + if (!obj.questionnaire) { + throw new Error('Select the questionnaire for the answer check'); + } + if (!obj.questionId) { + throw new Error('Select the question for the answer check'); + } + if (!obj.value) { + throw new Error('Enter or select the expected answer value'); + } + } + } + + const messageKey = (form.messageKey || '').trim(); + if (form.mode !== 'always' && messageKey) { + obj.messageKey = messageKey; + } + return JSON.stringify(obj); +} + +function requiresCheckboxes(id, selected, allQuestionnaires, selfId, editable) { + const list = (allQuestionnaires || []).filter(q => q.questionnaireID !== selfId); + if (!list.length) { + return '

No other questionnaires to require.

'; + } + return `
+ ${list.map(q => { + const checked = selected.includes(q.questionnaireID) ? ' checked' : ''; + return ``; + }).join('')} +
`; +} + +function questionnaireSelectHTML(prefix, selectedId, allQuestionnaires, selfId, editable) { + const dis = editable ? '' : ' disabled'; + const list = (allQuestionnaires || []).filter( + q => q.questionnaireID && q.questionnaireID !== selfId + ); + const options = [''] + .concat(list.map(q => { + const id = q.questionnaireID; + const sel = selectedId === id ? ' selected' : ''; + const label = q.name ? `${q.name}` : id; + const suffix = q.name && q.name !== id ? ` (${id})` : ''; + return ``; + })); + return ``; +} + +function questionSelectHTML(prefix, questionnaireId, selectedQuestionId, questionsByQuestionnaire, editable) { + const dis = editable ? '' : ' disabled'; + const questions = questionsForQuestionnaire(questionsByQuestionnaire, questionnaireId); + if (!questionnaireId) { + return ``; + } + const options = ['']; + for (const q of questions) { + const val = conditionQuestionId(q); + const sel = questionMatchesSelection(q, selectedQuestionId) ? ' selected' : ''; + options.push(``); + } + const known = questions.some(q => questionMatchesSelection(q, selectedQuestionId)); + if (selectedQuestionId && !known) { + options.push( + `` + ); + } + if (!questions.length) { + options.push(''); + } + return ``; +} + +function answerValueFieldHTML(prefix, rule, questionsByQuestionnaire, editable) { + const dis = editable ? '' : ' disabled'; + const q = findQuestion(questionsByQuestionnaire, rule.questionnaire, rule.questionId); + const options = q?.answerOptions || []; + const hasChoices = q && CHOICE_LAYOUT_TYPES.has(q.type) && options.length > 0; + + if (hasChoices) { + const opts = ['']; + for (const opt of options) { + const val = (opt.optionKey || opt.defaultText || '').trim(); + if (!val) continue; + const label = opt.labelGerman || opt.defaultText || val; + const sel = rule.value === val ? ' selected' : ''; + opts.push(``); + } + if (rule.value && !options.some(o => (o.optionKey || o.defaultText || '').trim() === rule.value)) { + opts.push(``); + } + return ``; + } + + return ``; +} + +function answerFieldsHTML(prefix, rule, allQuestionnaires, questionsByQuestionnaire, selfId, editable) { + const dis = editable ? '' : ' disabled'; + return ` +
+
+ + ${questionnaireSelectHTML(prefix, rule.questionnaire, allQuestionnaires, selfId, editable)} +

Must be a different questionnaire than this one.

+
+
+ + ${questionSelectHTML(prefix, rule.questionnaire, rule.questionId, questionsByQuestionnaire, editable)} +
+
+ + +
+
+ + ${answerValueFieldHTML(prefix, rule, questionsByQuestionnaire, editable)} +
+
`; +} + +function singleRuleHTML(prefix, rule, allQuestionnaires, questionsByQuestionnaire, selfId, editable) { + const showAnswer = rule.type === 'requires_answer'; + return ` +
+ + ${requiresCheckboxes(`${prefix}Requires`, rule.requires || [], allQuestionnaires, selfId, editable)} + ${showAnswer ? ` + + ${answerFieldsHTML(prefix, rule, allQuestionnaires, questionsByQuestionnaire, selfId, editable)} + ` : ''} +
`; +} + +export function renderConditionEditorHTML(form, { + questionnaireId, + allQuestionnaires, + questionsByQuestionnaire, + germanLabel, + editable, +}) { + const dis = editable ? '' : ' disabled'; + const modeOptions = CONDITION_MODES.map(m => + `` + ).join(''); + + const rules = form.rules?.length ? form.rules : [{ + type: 'requires', requires: [], questionnaire: '', questionId: '', operator: '==', value: '', + }]; + + return ` +
+

Availability

+
+ + +
+ +
+ ${singleRuleHTML('cond', { type: 'requires', requires: form.requires || [] }, allQuestionnaires, questionsByQuestionnaire, questionnaireId, editable)} +
+ +
+ ${singleRuleHTML('cond', { + type: 'requires_answer', + requires: form.requires || [], + questionnaire: form.questionnaire || '', + questionId: form.questionId || '', + operator: form.operator || '==', + value: form.value || '', + }, allQuestionnaires, questionsByQuestionnaire, questionnaireId, editable)} +
+ +
+

Unlock if any one rule matches.

+
+ ${rules.map((rule, i) => ` +
+
+ Rule ${i + 1} + ${editable && rules.length > 1 ? `` : ''} +
+
+ + +
+ ${singleRuleHTML(`anyof${i}`, rule, allQuestionnaires, questionsByQuestionnaire, questionnaireId, editable)} +
+ `).join('')} +
+ ${editable ? '' : ''} +
+ +
+ +
+ +
+

Shown on the app when locked. Also listed under Translations → App strings.

+
+
+ + +
+
+ + +
+
+
+
`; +} + +function readRequires(containerId) { + const root = document.getElementById(containerId); + if (!root) return []; + return [...root.querySelectorAll('input[type=checkbox]:checked')].map(cb => cb.value); +} + +function readAnswerPrefix(prefix) { + return { + questionnaire: document.getElementById(`${prefix}Questionnaire`)?.value?.trim() || '', + questionId: document.getElementById(`${prefix}QuestionId`)?.value?.trim() || '', + operator: document.getElementById(`${prefix}Operator`)?.value || '==', + value: document.getElementById(`${prefix}Value`)?.value?.trim() ?? '', + }; +} + +export function readConditionFormFromDOM() { + const mode = document.getElementById('conditionMode')?.value || 'always'; + const form = { mode, messageKey: '', germanLabel: '', customJson: '' }; + + if (mode === 'custom') { + form.customJson = document.getElementById('conditionCustomJson')?.value || '{}'; + try { + const obj = JSON.parse(form.customJson); + form.messageKey = (obj.messageKey || '').trim(); + } catch { /* validated on save */ } + form.germanLabel = document.getElementById('conditionGermanLabel')?.value?.trim() || ''; + return form; + } + + if (mode === 'always') { + return form; + } + + form.messageKey = document.getElementById('conditionMessageKey')?.value?.trim() || ''; + form.germanLabel = document.getElementById('conditionGermanLabel')?.value?.trim() || ''; + + if (mode === 'requires') { + form.requires = readRequires('condRequires'); + return form; + } + + if (mode === 'requires_answer') { + form.requires = readRequires('condRequires'); + Object.assign(form, readAnswerPrefix('cond')); + return form; + } + + if (mode === 'any_of') { + form.rules = []; + document.querySelectorAll('#conditionAnyOfRules .condition-anyof-rule').forEach((el, i) => { + const type = el.querySelector('.anyof-rule-type')?.value || 'requires'; + const requires = readRequires(`anyof${i}Requires`); + const rule = { type, requires, ...readAnswerPrefix(`anyof${i}`) }; + form.rules.push(rule); + }); + } + + return form; +} + +function wireConditionSelects(container, { questionnaireId, onChange }) { + container.querySelectorAll('.condition-qn-select').forEach(sel => { + sel.addEventListener('change', () => { + const prefix = sel.id.replace(/Questionnaire$/, ''); + const form = readConditionFormFromDOM(); + if (prefix === 'cond' && form.mode === 'requires_answer') { + const answer = readAnswerPrefix('cond'); + answer.questionnaire = sel.value; + if (answer.questionnaire !== form.questionnaire) { + answer.questionId = ''; + answer.value = ''; + } + form.questionnaire = answer.questionnaire; + form.questionId = answer.questionId; + form.value = answer.value; + } else if (form.rules) { + const idx = parseInt(prefix.replace(/^anyof/, ''), 10); + if (!Number.isNaN(idx) && form.rules[idx]) { + const prev = form.rules[idx].questionnaire; + form.rules[idx].questionnaire = sel.value; + if (sel.value !== prev) { + form.rules[idx].questionId = ''; + form.rules[idx].value = ''; + } + } + } + onChange(form); + }); + }); + + container.querySelectorAll('.condition-question-select').forEach(sel => { + sel.addEventListener('change', () => { + const prefix = sel.id.replace(/QuestionId$/, ''); + const form = readConditionFormFromDOM(); + const qId = sel.value; + if (prefix === 'cond' && form.mode === 'requires_answer') { + form.questionId = qId; + form.value = ''; + } else if (form.rules) { + const idx = parseInt(prefix.replace(/^anyof/, ''), 10); + if (!Number.isNaN(idx) && form.rules[idx]) { + form.rules[idx].questionId = qId; + form.rules[idx].value = ''; + } + } + onChange(form); + }); + }); +} + +/** + * Mount editor into container; returns { readForm, buildJson }. + */ +export function mountConditionEditor(container, { + questionnaireId, + allQuestionnaires, + questionsByQuestionnaire = {}, + conditionJson, + germanLabel, + editable, +}) { + let form = parseConditionForm(conditionJson, questionnaireId); + let label = germanLabel || ''; + + const render = () => { + container.innerHTML = renderConditionEditorHTML( + { ...form, germanLabel: label }, + { + questionnaireId, + allQuestionnaires, + questionsByQuestionnaire, + germanLabel: label, + editable, + } + ); + wire(); + }; + + const wire = () => { + const modeEl = document.getElementById('conditionMode'); + modeEl?.addEventListener('change', () => { + form = readConditionFormFromDOM(); + form.mode = modeEl.value; + if (form.mode === 'always') { + form.messageKey = ''; + } else if (!form.messageKey) { + form.messageKey = defaultConditionMessageKey(questionnaireId); + } + render(); + }); + + document.getElementById('addAnyOfRule')?.addEventListener('click', () => { + form = readConditionFormFromDOM(); + form.mode = 'any_of'; + form.rules = form.rules || []; + form.rules.push({ + type: 'requires', requires: [], questionnaire: '', questionId: '', operator: '==', value: '', + }); + label = document.getElementById('conditionGermanLabel')?.value || label; + render(); + }); + + container.querySelectorAll('.remove-anyof-rule').forEach(btn => { + btn.addEventListener('click', () => { + form = readConditionFormFromDOM(); + label = document.getElementById('conditionGermanLabel')?.value || label; + const idx = parseInt(btn.closest('.condition-anyof-rule')?.dataset.ruleIndex || '-1', 10); + form.rules.splice(idx, 1); + render(); + }); + }); + + container.querySelectorAll('.anyof-rule-type').forEach(sel => { + sel.addEventListener('change', () => { + form = readConditionFormFromDOM(); + label = document.getElementById('conditionGermanLabel')?.value || label; + const idx = parseInt(sel.dataset.ruleIndex || '0', 10); + if (form.rules?.[idx]) { + form.rules[idx].type = sel.value; + } + render(); + }); + }); + + wireConditionSelects(container, { + questionnaireId, + onChange: (updated) => { + form = updated; + label = document.getElementById('conditionGermanLabel')?.value || label; + render(); + }, + }); + }; + + render(); + + return { + readForm: () => { + form = readConditionFormFromDOM(); + label = document.getElementById('conditionGermanLabel')?.value?.trim() || label; + return { ...form, germanLabel: label }; + }, + buildJson: () => buildConditionJson(readConditionFormFromDOM(), questionnaireId), + }; +} diff --git a/website/js/confirm-modal.js b/website/js/confirm-modal.js new file mode 100644 index 0000000..cac0ad2 --- /dev/null +++ b/website/js/confirm-modal.js @@ -0,0 +1,148 @@ +function esc(s) { + const d = document.createElement('div'); + d.textContent = s ?? ''; + return d.innerHTML; +} + +function formatMessageHTML(message) { + return String(message) + .split(/\n\n+/) + .map((block) => `

${esc(block).replace(/\n/g, '
')}

`) + .join(''); +} + +function renderChoicesHTML(choices) { + const name = 'confirm_modal_choice'; + return ` +
+
+ ${choices.map((choice, i) => ` + + `).join('')} +
+
`; +} + +/** @type {((value: boolean | string | null) => void) | null} */ +let resolveConfirm = null; + +function ensureConfirmModal() { + let modal = document.getElementById('confirmModal'); + if (modal) return modal; + + modal = document.createElement('div'); + modal.id = 'confirmModal'; + modal.className = 'modal-overlay'; + modal.hidden = true; + modal.setAttribute('role', 'dialog'); + modal.setAttribute('aria-modal', 'true'); + modal.setAttribute('aria-labelledby', 'confirmModalTitle'); + modal.innerHTML = ` + + `; + document.body.appendChild(modal); + + modal.addEventListener('click', (e) => { + if (e.target === modal) finishConfirm(null); + }); + modal.querySelector('[data-confirm-cancel]').addEventListener('click', () => finishConfirm(null)); + modal.querySelector('[data-confirm-submit]').addEventListener('click', () => { + const choicesEl = modal.querySelector('#confirmModalChoices'); + const hasChoices = choicesEl.childElementCount > 0; + if (hasChoices) { + const selected = modal.querySelector('input[name="confirm_modal_choice"]:checked'); + finishConfirm(selected ? selected.value : null); + } else { + finishConfirm(true); + } + }); + + document.addEventListener('keydown', (e) => { + const el = document.getElementById('confirmModal'); + if (!el || el.hidden) return; + if (e.key === 'Escape') finishConfirm(null); + }); + + return modal; +} + +function finishConfirm(value) { + const modal = document.getElementById('confirmModal'); + if (!modal || modal.hidden) return; + modal.hidden = true; + document.body.classList.remove('modal-open'); + const resolve = resolveConfirm; + resolveConfirm = null; + if (resolve) resolve(value); +} + +/** + * Show a confirmation dialog. + * + * Without `choices`: resolves to `true` (confirmed) or `false` (cancelled). + * With `choices`: resolves to the selected choice value (string) or `null` (cancelled). + * + * @param {Object} options + * @param {string} options.title + * @param {string} options.message + * @param {string} [options.confirmLabel='Confirm'] + * @param {string} [options.cancelLabel='Cancel'] + * @param {'default'|'danger'} [options.variant='default'] + * @param {{ value: string, label: string, description?: string }[]} [options.choices] + * @returns {Promise} + */ +export function confirmAction(options) { + return new Promise((resolve) => { + if (resolveConfirm) { + resolve(false); + return; + } + + const modal = ensureConfirmModal(); + const hasChoices = Array.isArray(options.choices) && options.choices.length > 0; + const variant = options.variant === 'danger' ? 'danger' : 'default'; + + modal.querySelector('#confirmModalTitle').textContent = options.title || 'Confirm'; + modal.querySelector('#confirmModalMessage').innerHTML = formatMessageHTML(options.message || ''); + + const choicesEl = modal.querySelector('#confirmModalChoices'); + choicesEl.innerHTML = hasChoices ? renderChoicesHTML(options.choices) : ''; + + const cancelBtn = modal.querySelector('[data-confirm-cancel]'); + const submitBtn = modal.querySelector('[data-confirm-submit]'); + cancelBtn.textContent = options.cancelLabel || 'Cancel'; + submitBtn.textContent = options.confirmLabel || (hasChoices ? 'Continue' : 'Confirm'); + submitBtn.className = `btn btn-sm ${variant === 'danger' ? 'btn-danger' : 'btn-primary'}`; + + modal.classList.toggle('confirm-modal--danger', variant === 'danger'); + + resolveConfirm = (value) => { + if (hasChoices) { + resolve(value); + } else { + resolve(value === true); + } + }; + + modal.hidden = false; + document.body.classList.add('modal-open'); + (hasChoices + ? modal.querySelector('input[name="confirm_modal_choice"]') + : submitBtn + )?.focus(); + }); +} diff --git a/website/js/pages/assignments.js b/website/js/pages/assignments.js new file mode 100644 index 0000000..978c350 --- /dev/null +++ b/website/js/pages/assignments.js @@ -0,0 +1,461 @@ +import { apiGet, apiPost } from '../api.js'; +import { pageHeaderHTML, showToast } from '../app.js'; +import { confirmAction } from '../confirm-modal.js'; + +const CLIENT_PAGE_SIZE = 50; + +let coaches = []; +let clients = []; +let selectedCoachID = null; +let filterCoachSearch = ''; +let clientsByCoach = {}; +let clientPage = 0; +let selectedClientCodes = new Set(); + +export async function assignmentsPage() { + selectedCoachID = null; + filterCoachSearch = ''; + clientPage = 0; + selectedClientCodes = new Set(); + const app = document.getElementById('app'); + + app.innerHTML = ` + ${pageHeaderHTML('Assign Clients')} +
+ `; + + try { + const data = await apiGet('assignments.php'); + coaches = data.coaches || []; + clients = data.clients || []; + rebuildClientsByCoach(); + renderAssignments(); + } catch (e) { + showToast(e.message, 'error'); + document.getElementById('assignContent').innerHTML = + `

${esc(e.message)}

`; + } +} + +function rebuildClientsByCoach() { + clientsByCoach = {}; + clients.forEach(c => { + if (c.coachID) { + (clientsByCoach[c.coachID] = clientsByCoach[c.coachID] || []).push(c); + } + }); +} + +function renderAssignments() { + const container = document.getElementById('assignContent'); + + if (!coaches.length) { + container.innerHTML = ` +
+

No counselors found

+

Create counselors first before assigning clients.

+
`; + return; + } + + const unassignedCount = clients.filter(c => !c.coachID).length; + + container.innerHTML = ` +
+
+ + + +
+
+
+

Clients

+
+ + +
+
+
+ + +
+
+
+
+ + + + + + + + + +
+ + Client CodeCurrent counselor
+
+
+ +
+
+ + +
+ `; + + document.getElementById('coachSearch').addEventListener('input', (e) => { + filterCoachSearch = e.target.value; + renderCoachList(); + }); + + document.getElementById('coachList').addEventListener('click', (e) => { + const item = e.target.closest('.coach-list-item'); + if (!item || item.dataset.action === 'clear-filter') return; + document.querySelectorAll('.coach-list-item').forEach(x => x.classList.remove('active')); + item.classList.add('active'); + selectedCoachID = item.dataset.id; + document.getElementById('filterByCoach').value = selectedCoachID; + clientPage = 0; + renderClientRows(); + }); + + document.getElementById('filterByCoach').addEventListener('change', (e) => { + const value = e.target.value; + clientPage = 0; + if (value && value !== '__unassigned__') { + selectedCoachID = value; + highlightCoachInList(value); + } else { + selectedCoachID = null; + document.querySelectorAll('.coach-list-item').forEach(x => x.classList.remove('active')); + } + renderClientRows(); + }); + + document.getElementById('clientSearch').addEventListener('input', () => { + clientPage = 0; + renderClientRows(); + }); + + document.getElementById('selectAllBtn').addEventListener('click', () => { + document.querySelectorAll('#clientTableBody .client-cb').forEach(cb => { + cb.checked = true; + selectedClientCodes.add(cb.value); + }); + updateSelectionInfo(); + }); + document.getElementById('clearSelBtn').addEventListener('click', () => { + selectedClientCodes.clear(); + document.querySelectorAll('#clientTableBody .client-cb').forEach(cb => { cb.checked = false; }); + syncSelectAllCb(); + updateSelectionInfo(); + }); + document.getElementById('selectAllCb').addEventListener('change', (e) => { + document.querySelectorAll('#clientTableBody .client-cb').forEach(cb => { + cb.checked = e.target.checked; + if (e.target.checked) selectedClientCodes.add(cb.value); + else selectedClientCodes.delete(cb.value); + }); + updateSelectionInfo(); + }); + + document.getElementById('assignBtn').addEventListener('click', doAssign); + + renderCoachList(); + renderClientRows(); +} + +function getFilteredCoaches() { + const q = filterCoachSearch.trim().toLowerCase(); + if (!q) return coaches; + return coaches.filter(c => { + const hay = `${c.username} ${c.supervisorUsername || ''}`.toLowerCase(); + return hay.includes(q); + }); +} + +function renderCoachList() { + const list = document.getElementById('coachList'); + if (!list) return; + + const filtered = getFilteredCoaches(); + const q = filterCoachSearch.trim(); + + if (!filtered.length) { + list.innerHTML = `
  • ${q ? 'No counselors match' : 'No counselors'}
  • `; + return; + } + + list.innerHTML = filtered.map(c => coachListItemHTML(c)).join(''); + + if (selectedCoachID) { + highlightCoachInList(selectedCoachID); + } +} + +function coachListItemHTML(c) { + const count = (clientsByCoach[c.coachID] || []).length; + const active = selectedCoachID === c.coachID ? ' active' : ''; + return ` +
  • +
    ${esc(c.username)}
    + ${c.supervisorUsername + ? `
    ${esc(c.supervisorUsername)}
    ` + : ''} + ${count} client${count === 1 ? '' : 's'} +
  • `; +} + +function highlightCoachInList(coachID) { + document.querySelectorAll('.coach-list-item').forEach(li => { + li.classList.toggle('active', li.dataset.id === coachID); + }); + const active = document.querySelector(`.coach-list-item[data-id="${CSS.escape(coachID)}"]`); + active?.scrollIntoView({ block: 'nearest', behavior: 'smooth' }); +} + +function getVisibleClients() { + const coachFilter = document.getElementById('filterByCoach')?.value ?? ''; + const search = document.getElementById('clientSearch')?.value.trim().toLowerCase() ?? ''; + let visible = clients; + if (coachFilter === '__unassigned__') { + visible = clients.filter(c => !c.coachID); + } else if (coachFilter) { + visible = clients.filter(c => c.coachID === coachFilter); + } + if (search) { + visible = visible.filter(c => { + const hay = `${c.clientCode} ${c.coachUsername || ''}`.toLowerCase(); + return hay.includes(search); + }); + } + return visible; +} + +function updateClientSummary(filteredCount, pageSlice) { + const el = document.getElementById('clientListSummary'); + if (!el) return; + const search = document.getElementById('clientSearch')?.value.trim() ?? ''; + const coachFilter = document.getElementById('filterByCoach')?.value ?? ''; + const hasFilter = search || coachFilter; + const parts = []; + if (hasFilter && filteredCount !== clients.length) { + parts.push(`${filteredCount} of ${clients.length} match`); + } else { + parts.push(String(clients.length)); + } + if (pageSlice && filteredCount > CLIENT_PAGE_SIZE) { + const from = clientPage * CLIENT_PAGE_SIZE + 1; + const to = Math.min((clientPage + 1) * CLIENT_PAGE_SIZE, filteredCount); + parts.push(`rows ${from}–${to}`); + } + el.textContent = `(${parts.join(', ')})`; +} + +function renderPaginationBar(barId, totalRows, currentPage, pageSize, onPageChange) { + const pag = document.getElementById(barId); + if (!pag) return; + + if (!totalRows) { + pag.innerHTML = ''; + return; + } + + const totalPages = Math.max(1, Math.ceil(totalRows / pageSize)); + if (totalRows <= pageSize) { + pag.innerHTML = `${totalRows} row${totalRows === 1 ? '' : 's'}`; + return; + } + + const from = currentPage * pageSize + 1; + const to = Math.min((currentPage + 1) * pageSize, totalRows); + const prevId = `${barId}Prev`; + const nextId = `${barId}Next`; + pag.innerHTML = ` + + Rows ${from}–${to} of ${totalRows} (page ${currentPage + 1}/${totalPages}) + + `; + document.getElementById(prevId)?.addEventListener('click', () => onPageChange(currentPage - 1)); + document.getElementById(nextId)?.addEventListener('click', () => onPageChange(currentPage + 1)); +} + +function syncSelectAllCb(codesOnPage = []) { + const allCb = document.getElementById('selectAllCb'); + if (!allCb) return; + if (!codesOnPage.length) { + allCb.checked = false; + allCb.indeterminate = false; + return; + } + const allSelected = codesOnPage.every(code => selectedClientCodes.has(code)); + const someSelected = codesOnPage.some(code => selectedClientCodes.has(code)); + allCb.checked = allSelected; + allCb.indeterminate = !allSelected && someSelected; +} + +function renderClientRows() { + const body = document.getElementById('clientTableBody'); + if (!body) return; + + const visible = getVisibleClients(); + const totalPages = Math.max(1, Math.ceil(visible.length / CLIENT_PAGE_SIZE)); + if (clientPage >= totalPages) clientPage = totalPages - 1; + + if (!visible.length) { + updateClientSummary(0, false); + body.innerHTML = ` + + + No clients match the current filters + + `; + renderPaginationBar('clientPagination', 0, clientPage, CLIENT_PAGE_SIZE, (p) => { + clientPage = p; + renderClientRows(); + }); + syncSelectAllCb(); + updateSelectionInfo(); + return; + } + + const slice = visible.slice(clientPage * CLIENT_PAGE_SIZE, (clientPage + 1) * CLIENT_PAGE_SIZE); + updateClientSummary(visible.length, visible.length > CLIENT_PAGE_SIZE); + + body.innerHTML = slice.map(c => { + const checked = selectedClientCodes.has(c.clientCode) ? ' checked' : ''; + return ` + + + + + ${esc(c.clientCode)} + ${c.coachUsername + ? esc(c.coachUsername) + : 'Unassigned'} + `; + }).join(''); + + body.querySelectorAll('.client-cb').forEach(cb => { + cb.addEventListener('change', () => { + if (cb.checked) selectedClientCodes.add(cb.value); + else selectedClientCodes.delete(cb.value); + syncSelectAllCb(slice.map(c => c.clientCode)); + updateSelectionInfo(); + }); + }); + + renderPaginationBar('clientPagination', visible.length, clientPage, CLIENT_PAGE_SIZE, (p) => { + clientPage = p; + renderClientRows(); + }); + syncSelectAllCb(slice.map(c => c.clientCode)); + updateSelectionInfo(); +} + +function getSelectedCodes() { + return [...selectedClientCodes]; +} + +function updateSelectionInfo() { + const selected = getSelectedCodes(); + const info = document.getElementById('selectionInfo'); + const card = document.getElementById('assignActionCard'); + const onPage = document.querySelectorAll('#clientTableBody .client-cb:checked').length; + + if (!selected.length) { + info.style.display = 'none'; + card.style.display = 'none'; + return; + } + info.style.display = ''; + const extra = selected.length > onPage + ? ` (${onPage} on this page)` + : ''; + info.innerHTML = `${selected.length} client${selected.length === 1 ? '' : 's'} selected${extra}`; + card.style.display = ''; + if (selectedCoachID) { + const sel = document.getElementById('targetCoachSelect'); + if (sel) sel.value = selectedCoachID; + } +} + +async function doAssign() { + const selected = getSelectedCodes(); + const coachID = document.getElementById('targetCoachSelect').value; + + if (!selected.length) { showToast('Select at least one client', 'error'); return; } + if (!coachID) { showToast('Select a target counselor', 'error'); return; } + + const coachName = coaches.find(c => c.coachID === coachID)?.username || coachID; + if (!(await confirmAction({ + title: 'Assign clients', + message: `Assign ${selected.length} client(s) to ${coachName}?`, + confirmLabel: 'Assign', + }))) return; + + const btn = document.getElementById('assignBtn'); + btn.disabled = true; + btn.textContent = 'Assigning...'; + + try { + const data = await apiPost('assignments.php', { clientCodes: selected, coachID }); + if (data.success) { + selected.forEach(code => { + const c = clients.find(x => x.clientCode === code); + if (c) { + c.coachID = coachID; + c.coachUsername = coachName; + } + }); + selectedClientCodes.clear(); + rebuildClientsByCoach(); + showToast(`${data.assigned} client(s) assigned to ${coachName}`, 'success'); + renderAssignments(); + } + } catch (e) { + showToast(e.message, 'error'); + btn.disabled = false; + btn.textContent = 'Assign'; + } +} + +function esc(s) { + const d = document.createElement('div'); + d.textContent = s ?? ''; + return d.innerHTML; +} diff --git a/website/js/pages/clients.js b/website/js/pages/clients.js new file mode 100644 index 0000000..aba5d0b --- /dev/null +++ b/website/js/pages/clients.js @@ -0,0 +1,938 @@ +import { apiGet, apiPost, apiPut, apiDelete } from '../api.js'; +import { pageHeaderHTML, showToast } from '../app.js'; +import { confirmAction } from '../confirm-modal.js'; +import { + isClientArchived, + confirmAndPatchClientArchive, + clientTableActionsHTML, +} from '../client-archive.js'; + +const PAGE_SIZE = 50; +const ARCHIVE_FILTER_KEY = 'clientsArchiveFilter'; + +let clientsList = []; +let coachesList = []; +let scoringProfileCatalog = []; +let filterSearch = ''; +let archiveFilter = localStorage.getItem(ARCHIVE_FILTER_KEY) || 'active'; +let page = 0; +let expandedClientCode = null; +let expandedQnKey = null; +let expandedVersionKey = null; +let detailByClient = {}; + +export async function clientsPage() { + const app = document.getElementById('app'); + + app.innerHTML = ` + ${pageHeaderHTML('Clients', '')} + +
    + `; + + document.getElementById('showCreateFormBtn').addEventListener('click', () => { + const wrapper = document.getElementById('createClientWrapper'); + if (wrapper.style.display === 'none') { + showCreateForm(); + } else { + hideCreateForm(); + } + }); + + await loadClients(); +} + +async function loadClients() { + try { + const archiveQuery = archiveFilter !== 'active' + ? `?archiveFilter=${encodeURIComponent(archiveFilter)}` + : ''; + const [clientsData, assignData] = await Promise.all([ + apiGet(`clients.php${archiveQuery}`), + apiGet('assignments.php'), + ]); + clientsList = clientsData.clients || []; + scoringProfileCatalog = clientsData.scoringProfiles || []; + coachesList = assignData.coaches || []; + renderClients(); + } catch (e) { + showToast(e.message, 'error'); + document.getElementById('clientsContent').innerHTML = + `

    ${esc(e.message)}

    `; + } +} + +function renderClients() { + const container = document.getElementById('clientsContent'); + + if (!clientsList.length) { + const emptyMsg = archiveFilter === 'archived' + ? '

    No archived clients

    Archived clients are hidden from assignments and follow-up.

    ' + : archiveFilter === 'all' + ? '

    No clients found

    Create the first client above.

    ' + : '

    No active clients

    Create a client above or switch the filter to view archived clients.

    '; + container.innerHTML = ` +
    +
    + ${archiveFilterSelectHTML()} + +
    +
    ${emptyMsg}
    +
    `; + bindArchiveFilterControl(); + document.getElementById('clientListSearch')?.addEventListener('input', (e) => { + filterSearch = e.target.value.trim(); + }); + return; + } + + const showProfileColumn = scoringProfileCatalog.length > 0; + const profileLegend = showProfileColumn ? scoringLegendHTML() : ''; + + container.innerHTML = ` +
    +
    + ${archiveFilterSelectHTML()} + + +
    + ${profileLegend} +

    + Expand a row to view questionnaire responses and upload version history. +

    +
    + + + + + + ${showProfileColumn ? '' : ''} + + + + +
    Client CodeCurrent counselorProfile scoresActions
    +
    +
    +
    `; + + bindArchiveFilterControl(); + document.getElementById('clientListSearch').addEventListener('input', (e) => { + filterSearch = e.target.value.trim(); + page = 0; + clearExpandIfHidden(); + renderClientTableBody(); + }); + ensureCoachBandGlobalClick(); + renderClientTableBody(); +} + +function archiveFilterSelectHTML() { + return ` + `; +} + +function bindArchiveFilterControl() { + const sel = document.getElementById('clientArchiveFilter'); + if (!sel) return; + sel.addEventListener('change', async (e) => { + archiveFilter = e.target.value; + localStorage.setItem(ARCHIVE_FILTER_KEY, archiveFilter); + page = 0; + expandedClientCode = null; + expandedQnKey = null; + expandedVersionKey = null; + detailByClient = {}; + document.getElementById('clientsContent').innerHTML = '
    '; + await loadClients(); + }); +} + +function getFilteredClientsList() { + let list = clientsList; + if (filterSearch) { + const q = filterSearch.toLowerCase(); + list = list.filter(c => { + const hay = `${c.clientCode || ''} ${c.coachUsername || ''}`.toLowerCase(); + return hay.includes(q); + }); + } + return list; +} + +function renderClientTableBody() { + const body = document.getElementById('clientsTableBody'); + const countEl = document.getElementById('clientListCount'); + const filtered = getFilteredClientsList(); + const totalPages = Math.max(1, Math.ceil(filtered.length / PAGE_SIZE)); + const colCount = scoringProfileCatalog.length > 0 ? 4 : 3; + if (page >= totalPages) page = totalPages - 1; + const slice = filtered.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE); + + if (countEl) { + countEl.textContent = `${filtered.length} client${filtered.length === 1 ? '' : 's'}`; + } + + if (!slice.length) { + body.innerHTML = `No clients match`; + } else { + body.innerHTML = slice.map(c => clientRowHTML(c)).join(''); + body.querySelectorAll('.client-expand-btn').forEach(btn => { + btn.addEventListener('click', () => toggleClient(btn.dataset.code)); + }); + body.querySelectorAll('.client-qn-expand-btn').forEach(btn => { + btn.addEventListener('click', () => toggleQn(btn.dataset.client, btn.dataset.qn)); + }); + body.querySelectorAll('.client-version-expand-btn').forEach(btn => { + btn.addEventListener('click', () => toggleVersion( + btn.dataset.client, + btn.dataset.qn, + btn.dataset.submission + )); + }); + body.querySelectorAll('.delete-client-btn').forEach(btn => { + btn.addEventListener('click', () => deleteClient(btn.dataset.code)); + }); + body.querySelectorAll('.archive-client-btn').forEach(btn => { + btn.addEventListener('click', () => setClientArchived(btn.dataset.code, true)); + }); + body.querySelectorAll('.restore-client-btn').forEach(btn => { + btn.addEventListener('click', () => setClientArchived(btn.dataset.code, false)); + }); + bindCoachBandPickerEvents(body); + } + + const pag = document.getElementById('clientsPagination'); + if (!pag) return; + if (filtered.length <= PAGE_SIZE) { + pag.innerHTML = ''; + return; + } + const from = page * PAGE_SIZE + 1; + const to = Math.min((page + 1) * PAGE_SIZE, filtered.length); + pag.innerHTML = ` + + Rows ${from}–${to} of ${filtered.length} + + `; + document.getElementById('clientsPagePrev')?.addEventListener('click', () => { + page--; + clearExpandIfHidden(); + renderClientTableBody(); + }); + document.getElementById('clientsPageNext')?.addEventListener('click', () => { + page++; + clearExpandIfHidden(); + renderClientTableBody(); + }); +} + +function qnKey(clientCode, questionnaireID) { + return `${clientCode}|${questionnaireID}`; +} + +function versionKey(clientCode, questionnaireID, submissionID) { + return `${clientCode}|${questionnaireID}|${submissionID}`; +} + +function clearExpandIfHidden() { + const filtered = getFilteredClientsList(); + const slice = filtered.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE); + if (expandedClientCode && !slice.some(c => c.clientCode === expandedClientCode)) { + expandedClientCode = null; + expandedQnKey = null; + expandedVersionKey = null; + } +} + +function answerTableHTML(rows, { highlightLiveDiff = false } = {}) { + if (!rows?.length) { + return '

    No answers recorded

    '; + } + return ` + + + + ${rows.map(r => ` + + + + + `).join('')} + +
    QuestionAnswer
    ${esc(r.label)}${esc(r.value || '—')}
    `; +} + +function clientDetailPanelHTML(clientCode) { + const detail = detailByClient[clientCode]; + if (!detail) { + return '

    Loading…

    '; + } + const cl = detail.client || {}; + const questionnaires = detail.questionnaires || []; + if (!questionnaires.length) { + return ` +

    + Counselor: ${esc(cl.coachUsername || '—')} + ${cl.supervisorUsername ? ` · Supervisor: ${esc(cl.supervisorUsername)}` : ''} +

    +

    No questionnaire data for this client yet.

    `; + } + + return ` +

    + Counselor: ${esc(cl.coachUsername || '—')} + ${cl.supervisorUsername ? ` · Supervisor: ${esc(cl.supervisorUsername)}` : ''} +

    + ${renderClientScoringProfiles(detail.scoringProfiles || [], clientCode)} + ${questionnaires.map(q => clientQnBlockHTML(clientCode, q)).join('')}`; +} + +function scoringLegendHTML() { + return ` +
    +

    How profile scores work

    +

    + Each profile adds up questionnaire points (× weight) into a weighted total, + then maps that total to green + yellow + red using the thresholds on the Session measures page. +

    +
    +
    +
    Calculated
    +
    Automatic category from answers and profile rules (shown before Counselor review).
    +
    +
    +
    Counselor
    +
    Category confirmed or changed by the counselor in the app (“Review scores”).
    +
    +
    +
    Incomplete
    +
    Not all questionnaires in that profile are completed yet.
    +
    +
    +
    `; +} + +function bandBadgeHTML(band, { pending = false, incomplete = false, label = '' } = {}) { + const text = label || band || ''; + if (incomplete) { + return 'incomplete'; + } + if (pending) { + return 'awaiting review'; + } + if (!band) { + return ''; + } + return `${esc(text || band)}`; +} + +const COACH_BANDS = ['green', 'yellow', 'red']; + +function bandLabel(band) { + if (!band) return '—'; + return band.charAt(0).toUpperCase() + band.slice(1); +} + +function coachBandPickerHTML(p, clientCode) { + const calcBand = p.calculatedBand || p.band; + if (!calcBand || !clientCode || !p.profileID) { + return ''; + } + const current = p.coachBand || ''; + const coachPending = p.pendingReview !== false && !current; + const triggerBadge = coachPending + ? bandBadgeHTML(null, { pending: true }) + : bandBadgeHTML(current); + const showAgree = coachPending || (current && current !== calcBand); + const options = COACH_BANDS.map(b => ` + `).join(''); + const agreeBtn = showAgree ? ` + ` : ''; + return ` +
    + + +
    `; +} + +function profileScoreBlockHTML(p, clientCode = '') { + const calcBand = p.calculatedBand || p.band; + if (!calcBand) { + return ` +
    +
    ${esc(p.name)}
    +
    + Calculated + ${bandBadgeHTML(null, { incomplete: true })} +
    +

    Not all questionnaires in this profile are completed yet.

    +
    `; + } + const coachPending = p.pendingReview !== false && !p.coachBand; + const coachDiffers = p.coachBand && p.coachBand !== calcBand; + const coachControl = clientCode + ? coachBandPickerHTML(p, clientCode) + : (coachPending ? bandBadgeHTML(null, { pending: true }) : bandBadgeHTML(p.coachBand)); + return ` +
    +
    ${esc(p.name)}
    +
    + Calculated + ${bandBadgeHTML(calcBand)} + ${esc(String(p.weightedTotal))} +
    +
    + Counselor + ${coachControl} + ${coachDiffers ? 'override' : ''} +
    +
    `; +} + +function renderClientScoringProfiles(profiles, clientCode = '') { + if (!profiles.length) return ''; + return ` +
    + Scoring profiles +

    + Weighted totals and calculated vs Counselor categories for this client. +

    +
    ${profiles.map(p => { + const calc = p.calculatedBand || p.band; + if (!calc) return profileScoreBlockHTML(p, clientCode); + const coachPending = p.pendingReview !== false && !p.coachBand; + const computedAt = p.computedAt ? `Last computed ${esc(p.computedAt)}` : ''; + const reviewedAt = p.coachReviewedAt ? `Counselor reviewed ${esc(p.coachReviewedAt)}` : ''; + const meta = [computedAt, reviewedAt].filter(Boolean).join(' · '); + return ` +
    + ${profileScoreBlockHTML(p, clientCode)} + ${meta ? `

    ${meta}

    ` : ''} + ${coachPending ? '

    Counselor has not reviewed this profile in the app yet.

    ' : ''} +
    `; + }).join('')}
    +
    `; +} + +function clientQnBlockHTML(clientCode, q) { + const key = qnKey(clientCode, q.questionnaireID); + const qnExpanded = expandedQnKey === key; + const meta = [ + q.status ? `Status: ${q.status}` : '', + q.sumPoints != null ? `Points: ${q.sumPoints}` : '', + q.completedAt ? `Completed: ${q.completedAt}` : '', + q.submissionCount ? `${q.submissionCount} upload(s)` : '', + ].filter(Boolean).join(' · '); + + const versionsHTML = (q.submissions || []).map(s => { + const vKey = versionKey(clientCode, q.questionnaireID, s.submissionID); + const vExpanded = expandedVersionKey === vKey; + const vMeta = [ + s.submittedAt ? `Uploaded: ${s.submittedAt}` : '', + s.status ? `Status: ${s.status}` : '', + s.sumPoints != null ? `Points: ${s.sumPoints}` : '', + ].filter(Boolean).join(' · '); + return ` +
    +
    + + Version ${s.version} + ${s.structureRevision ? `struct rev ${s.structureRevision}` : ''} + ${s.changedCount > 0 + ? `${s.changedCount} diff vs live` + : 'Same as live'} + ${vMeta ? `${esc(vMeta)}` : ''} +
    + ${vExpanded ? ` +
    + ${s.changedCount > 0 + ? '

    Highlighted rows differ from current live data.

    ' + : ''} + ${answerTableHTML(s.answers, { highlightLiveDiff: true })} +
    + ` : ''} +
    `; + }).join(''); + + return ` +
    +
    + + ${esc(q.name)} + ${q.questionnaireVersion ? `v${esc(q.questionnaireVersion)}` : ''} + ${meta ? `${esc(meta)}` : ''} +
    + ${qnExpanded ? ` +
    +

    Current data (live)

    + ${answerTableHTML(q.currentAnswers)} + ${(q.submissions || []).length ? ` +

    Upload versions

    + ${versionsHTML} + ` : '

    No archived upload versions.

    '} +
    + ` : ''} +
    `; +} + +function profileDotsHTML(profiles, clientCode) { + if (!profiles?.length) { + return 'No scoring profiles'; + } + return `
    ${profiles.map(p => profileScoreBlockHTML(p, clientCode)).join('')}
    `; +} + +function patchClientScoringBand(clientCode, profileID, coachBand) { + const client = clientsList.find(c => c.clientCode === clientCode); + if (client?.scoringProfiles) { + const profile = client.scoringProfiles.find(p => p.profileID === profileID); + if (profile) { + profile.coachBand = coachBand; + profile.pendingReview = false; + } + } + const detail = detailByClient[clientCode]; + if (detail?.scoringProfiles) { + const profile = detail.scoringProfiles.find(p => p.profileID === profileID); + if (profile) { + profile.coachBand = coachBand; + profile.pendingReview = false; + } + } +} + +function resetCoachBandMenuPosition(menu) { + if (!menu) return; + menu.style.position = ''; + menu.style.left = ''; + menu.style.top = ''; + menu.style.minWidth = ''; + menu.style.zIndex = ''; + menu.style.visibility = ''; +} + +function positionCoachBandMenu(picker) { + const trigger = picker.querySelector('.coach-band-trigger'); + const menu = picker.querySelector('.coach-band-menu'); + if (!trigger || !menu) return; + menu.hidden = false; + menu.style.visibility = 'hidden'; + const rect = trigger.getBoundingClientRect(); + const menuWidth = Math.max(rect.width, 184); + const left = Math.min(rect.left, window.innerWidth - menuWidth - 8); + menu.style.position = 'fixed'; + menu.style.left = `${Math.max(8, left)}px`; + menu.style.minWidth = `${menuWidth}px`; + menu.style.zIndex = '10001'; + const menuHeight = menu.offsetHeight; + let top = rect.bottom + 6; + if (top + menuHeight > window.innerHeight - 8) { + top = rect.top - menuHeight - 6; + } + menu.style.top = `${Math.max(8, top)}px`; + menu.style.visibility = ''; +} + +function closeAllCoachBandMenus() { + document.querySelectorAll('.coach-band-picker.is-open').forEach(picker => { + picker.classList.remove('is-open'); + const menu = picker.querySelector('.coach-band-menu'); + const trigger = picker.querySelector('.coach-band-trigger'); + resetCoachBandMenuPosition(menu); + if (menu) menu.hidden = true; + if (trigger) trigger.setAttribute('aria-expanded', 'false'); + }); +} + +function toggleCoachBandMenu(picker) { + const isOpen = picker.classList.contains('is-open'); + closeAllCoachBandMenus(); + if (isOpen) return; + picker.classList.add('is-open'); + const trigger = picker.querySelector('.coach-band-trigger'); + positionCoachBandMenu(picker); + if (trigger) trigger.setAttribute('aria-expanded', 'true'); +} + +function openCoachBandConfirmModal({ clientCode, profileName, calculatedBand, coachBand }) { + return new Promise(resolve => { + const overlay = document.createElement('div'); + overlay.className = 'modal-overlay coach-band-confirm-modal'; + overlay.setAttribute('role', 'dialog'); + overlay.setAttribute('aria-modal', 'true'); + overlay.innerHTML = ` + `; + document.body.appendChild(overlay); + document.body.classList.add('modal-open'); + + const close = (confirmed) => { + overlay.remove(); + document.body.classList.remove('modal-open'); + resolve(confirmed); + }; + overlay.querySelector('[data-coach-cancel]').addEventListener('click', () => close(false)); + overlay.querySelector('[data-coach-confirm]').addEventListener('click', () => close(true)); + overlay.addEventListener('click', (e) => { + if (e.target === overlay) close(false); + }); + overlay.querySelector('.modal-dialog').addEventListener('click', (e) => e.stopPropagation()); + const onKey = (e) => { + if (e.key === 'Escape') close(false); + }; + document.addEventListener('keydown', onKey, { once: true }); + overlay.querySelector('[data-coach-confirm]').focus(); + }); +} + +async function promptCoachBandSave(picker, coachBand) { + const clientCode = picker.dataset.client; + const profileID = picker.dataset.profile; + const profileName = picker.dataset.profileName || profileID; + const calculatedBand = picker.dataset.calculated; + const current = picker.dataset.coach || ''; + + if (!coachBand || coachBand === current) return; + + const confirmed = await openCoachBandConfirmModal({ + clientCode, + profileName, + calculatedBand, + coachBand, + }); + if (!confirmed) return; + + const weightedRaw = picker.dataset.weighted; + const weightedTotal = weightedRaw !== '' ? Number(weightedRaw) : null; + picker.classList.add('is-saving'); + try { + await apiPut('clients.php', { + clientCode, + profileID, + coachBand, + calculatedBand, + weightedTotal, + }); + patchClientScoringBand(clientCode, profileID, coachBand); + showToast(`Counselor category saved for ${profileName}`, 'success'); + renderClientTableBody(); + } catch (e) { + showToast(e.message, 'error'); + } finally { + picker.classList.remove('is-saving'); + } +} + +function bindCoachBandPickerEvents(root = document) { + root.querySelectorAll('.coach-band-trigger').forEach(btn => { + btn.addEventListener('click', (e) => { + e.stopPropagation(); + toggleCoachBandMenu(btn.closest('.coach-band-picker')); + }); + }); + root.querySelectorAll('.coach-band-option').forEach(btn => { + btn.addEventListener('click', (e) => { + e.stopPropagation(); + const picker = btn.closest('.coach-band-picker'); + closeAllCoachBandMenus(); + promptCoachBandSave(picker, btn.dataset.band); + }); + }); +} + +let coachBandGlobalClickBound = false; +function ensureCoachBandGlobalClick() { + if (coachBandGlobalClickBound) return; + coachBandGlobalClickBound = true; + document.addEventListener('click', () => closeAllCoachBandMenus()); + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape') closeAllCoachBandMenus(); + }); +} + +function clientHasResponseData(c) { + return Number(c.hasResponseData) === 1; +} + +function clientArchivedLabel(c) { + if (!isClientArchived(c) || !c.archivedAt) return ''; + const d = new Date(Number(c.archivedAt) * 1000); + const when = Number.isNaN(d.getTime()) ? '' : d.toLocaleDateString(); + return when ? ` Archived` : ''; +} + +function clientRowHTML(c) { + const coach = c.coachUsername + ? `${esc(c.coachUsername)}` + : `Unassigned`; + const archived = isClientArchived(c); + const canExpand = clientHasResponseData(c); + const expanded = canExpand && expandedClientCode === c.clientCode; + const expandControl = canExpand + ? ` ` + : ''; + const profileCol = scoringProfileCatalog.length > 0 + ? `${profileDotsHTML(c.scoringProfiles || [], c.clientCode)}` + : ''; + const colCount = scoringProfileCatalog.length > 0 ? 4 : 3; + const actions = clientTableActionsHTML({ + clientCode: esc(c.clientCode), + archived, + showDelete: true, + }); + + return ` + + + ${expandControl}${esc(c.clientCode)}${clientArchivedLabel(c)} + + ${coach} + ${profileCol} + ${actions} + + ${expanded ? ` + + +
    ${clientDetailPanelHTML(c.clientCode)}
    + + ` : ''}`; +} + +async function toggleClient(clientCode) { + const row = clientsList.find(c => c.clientCode === clientCode); + if (!row || !clientHasResponseData(row)) { + return; + } + if (expandedClientCode === clientCode) { + expandedClientCode = null; + expandedQnKey = null; + expandedVersionKey = null; + renderClientTableBody(); + return; + } + expandedClientCode = clientCode; + expandedQnKey = null; + expandedVersionKey = null; + renderClientTableBody(); + if (!detailByClient[clientCode]) { + try { + const data = await apiGet( + `clients.php?clientCode=${encodeURIComponent(clientCode)}&detail=1` + ); + detailByClient[clientCode] = data; + } catch (e) { + showToast(e.message, 'error'); + detailByClient[clientCode] = { client: {}, questionnaires: [] }; + } + renderClientTableBody(); + } +} + +function toggleQn(clientCode, questionnaireID) { + const key = qnKey(clientCode, questionnaireID); + expandedQnKey = expandedQnKey === key ? null : key; + expandedVersionKey = null; + renderClientTableBody(); +} + +function toggleVersion(clientCode, questionnaireID, submissionID) { + const key = versionKey(clientCode, questionnaireID, submissionID); + expandedVersionKey = expandedVersionKey === key ? null : key; + renderClientTableBody(); +} + +async function setClientArchived(clientCode, archived) { + const ok = await confirmAndPatchClientArchive(clientCode, archived); + if (!ok) return; + + if (archived && archiveFilter === 'active') { + clientsList = clientsList.filter(c => c.clientCode !== clientCode); + } else if (!archived && archiveFilter === 'archived') { + clientsList = clientsList.filter(c => c.clientCode !== clientCode); + } else { + const row = clientsList.find(c => c.clientCode === clientCode); + if (row) { + row.archived = archived ? 1 : 0; + row.archivedAt = archived ? Math.floor(Date.now() / 1000) : 0; + } + } + if (expandedClientCode === clientCode && archived) { + expandedClientCode = null; + expandedQnKey = null; + expandedVersionKey = null; + } + if (!clientsList.length) renderClients(); + else renderClientTableBody(); +} + +async function deleteClient(clientCode) { + if (!(await confirmAction({ + title: 'Delete client', + message: `Delete client "${clientCode}"? This will also remove all their answers and results. This cannot be undone.`, + confirmLabel: 'Delete', + variant: 'danger', + }))) return; + try { + await apiDelete('clients.php', { clientCode }); + clientsList = clientsList.filter(c => c.clientCode !== clientCode); + delete detailByClient[clientCode]; + if (expandedClientCode === clientCode) { + expandedClientCode = null; + expandedQnKey = null; + expandedVersionKey = null; + } + showToast(`Client "${clientCode}" deleted`, 'success'); + if (!clientsList.length) renderClients(); + else renderClientTableBody(); + } catch (e) { + showToast(e.message, 'error'); + } +} + +function showCreateForm() { + if (!coachesList.length) { + showToast('No counselors available. Create a counselor first.', 'error'); + return; + } + + const wrapper = document.getElementById('createClientWrapper'); + wrapper.style.display = ''; + wrapper.innerHTML = ` +
    +

    Create New Client

    +
    +
    + + +
    +
    + + +
    +
    +
    + + +
    + +
    + `; + + const input = document.getElementById('cc_code'); + input.focus(); + wrapper.querySelectorAll('input').forEach(inp => { + inp.addEventListener('keydown', (e) => { + if (e.key === 'Enter') submitCreateClient(); + if (e.key === 'Escape') hideCreateForm(); + }); + }); + + document.getElementById('cc_submit').addEventListener('click', submitCreateClient); + document.getElementById('cc_cancel').addEventListener('click', hideCreateForm); +} + +function hideCreateForm() { + const wrapper = document.getElementById('createClientWrapper'); + if (wrapper) { wrapper.style.display = 'none'; wrapper.innerHTML = ''; } +} + +async function submitCreateClient() { + const errEl = document.getElementById('cc_error'); + errEl.style.display = 'none'; + + const clientCode = document.getElementById('cc_code').value.trim(); + const coachID = document.getElementById('cc_coach').value; + + if (!clientCode) { showError(errEl, 'Client code is required'); return; } + if (!coachID) { showError(errEl, 'Please select a counselor'); return; } + + const btn = document.getElementById('cc_submit'); + btn.disabled = true; + btn.textContent = 'Creating...'; + + try { + const data = await apiPost('clients.php', { clientCode, coachID }); + const coach = coachesList.find(c => c.coachID === coachID); + clientsList.push({ + clientCode: data.clientCode, + coachID: coachID, + coachUsername: coach?.username ?? null, + }); + showToast(`Client "${data.clientCode}" created`, 'success'); + hideCreateForm(); + renderClients(); + } catch (e) { + showError(errEl, e.message); + btn.disabled = false; + btn.textContent = 'Create Client'; + } +} + +function showError(el, msg) { + el.textContent = msg; + el.style.display = ''; +} + +function esc(s) { + const d = document.createElement('div'); + d.textContent = s ?? ''; + return d.innerHTML; +} diff --git a/website/js/pages/coaches.js b/website/js/pages/coaches.js new file mode 100644 index 0000000..0f8acfa --- /dev/null +++ b/website/js/pages/coaches.js @@ -0,0 +1,423 @@ +import { apiGet } from '../api.js'; +import { pageHeaderHTML, showToast } from '../app.js'; + +const FETCH_LIMIT = 100; +const CLIENT_GROUP_PAGE = 10; +const UPLOADS_VISIBLE = 5; + +let coachesList = []; +let expandedCoachId = null; +/** @type {Record} */ +let recentByCoach = {}; +/** @type {Record, showAllUploads: Set, filter: string }>} */ +let coachDetailUi = {}; +let filterSearch = ''; + +export async function coachesPage() { + const app = document.getElementById('app'); + app.innerHTML = ` + ${pageHeaderHTML('Counselor activity')} +
    + `; + + try { + const data = await apiGet('coaches.php'); + coachesList = data.coaches || []; + renderCoaches(); + } catch (e) { + showToast(e.message, 'error'); + document.getElementById('coachesContent').innerHTML = + `

    ${esc(e.message)}

    `; + } +} + +function coachSearchText(c) { + return [c.username, c.coachID, c.supervisorUsername].filter(Boolean).join(' '); +} + +function coachHasUploads(c) { + return (c.totalSubmissions ?? 0) > 0; +} + +function filteredCoaches() { + const q = filterSearch.trim().toLowerCase(); + if (!q) return coachesList; + return coachesList.filter(c => coachSearchText(c).toLowerCase().includes(q)); +} + +function getCoachUi(coachID) { + if (!coachDetailUi[coachID]) { + coachDetailUi[coachID] = { + clientLimit: CLIENT_GROUP_PAGE, + expandedClients: new Set(), + showAllUploads: new Set(), + filter: '', + }; + } + return coachDetailUi[coachID]; +} + +function groupSubmissionsByClient(submissions) { + const map = new Map(); + for (const s of submissions) { + const code = s.clientCode || ''; + if (!map.has(code)) { + map.set(code, []); + } + map.get(code).push(s); + } + const groups = []; + for (const [clientCode, uploads] of map) { + uploads.sort((a, b) => String(b.submittedAt).localeCompare(String(a.submittedAt))); + groups.push({ + clientCode, + uploads, + count: uploads.length, + lastAt: uploads[0]?.submittedAt || '', + }); + } + groups.sort((a, b) => String(b.lastAt).localeCompare(String(a.lastAt))); + return groups; +} + +function filterClientGroups(groups, q) { + if (!q) return groups; + const needle = q.toLowerCase(); + return groups.filter(g => { + if (g.clientCode.toLowerCase().includes(needle)) return true; + return g.uploads.some(u => + (u.questionnaireName || '').toLowerCase().includes(needle) + || (u.questionnaireID || '').toLowerCase().includes(needle) + ); + }); +} + +function uploadsTableHTML(uploads) { + if (!uploads.length) { + return '

    No uploads

    '; + } + return ` + + + + + + ${uploads.map(s => ` + + + + + + `).join('')} + +
    WhenQuestionnaireVer.
    ${esc(s.submittedAt)}${esc(s.questionnaireName)}${s.version}
    `; +} + +function coachRecentPanelHTML(coachID) { + const data = recentByCoach[coachID]; + if (!data) { + return '

    Loading…

    '; + } + const ui = getCoachUi(coachID); + const allGroups = groupSubmissionsByClient(data.submissions || []); + const groups = filterClientGroups(allGroups, ui.filter.trim()); + const visibleGroups = groups.slice(0, ui.clientLimit); + const hasMoreClients = groups.length > ui.clientLimit; + const hasMoreUploads = (data.submissions?.length ?? 0) < (data.total ?? 0); + + const summary = data.total + ? `${data.total} upload${data.total === 1 ? '' : 's'} · ${allGroups.length} client${allGroups.length === 1 ? '' : 's'}` + : 'No uploads'; + + let groupsHTML = ''; + if (!groups.length) { + groupsHTML = `

    ${ui.filter.trim() ? 'No matches' : 'No uploads'}

    `; + } else { + groupsHTML = visibleGroups.map(g => { + const expanded = ui.expandedClients.has(g.clientCode); + const showAll = ui.showAllUploads.has(g.clientCode); + const visibleUploads = showAll ? g.uploads : g.uploads.slice(0, UPLOADS_VISIBLE); + const hiddenCount = g.uploads.length - UPLOADS_VISIBLE; + return ` +
    + + ${expanded ? ` +
    + ${uploadsTableHTML(visibleUploads)} + ${!showAll && hiddenCount > 0 ? ` + + ` : ''} +
    + ` : ''} +
    `; + }).join(''); + } + + return ` +
    +
    + + ${esc(summary)}${ui.filter.trim() && groups.length !== allGroups.length + ? ` · ${groups.length} shown` + : ''} +
    +
    ${groupsHTML}
    +
    + ${hasMoreClients ? ` + + ` : ''} + ${hasMoreUploads ? ` + + ` : ''} +
    +
    `; +} + +function renderCoaches() { + const el = document.getElementById('coachesContent'); + + if (!coachesList.length) { + el.innerHTML = `

    No counselors

    `; + return; + } + + el.innerHTML = ` +
    +

    + Track uploads and client load per counselor. Expand a row to browse uploads by client. +

    +
    + + +
    +
    + + + + + + + + +
    CounselorSupervisorClientsTotal uploads7d30dLast upload
    +
    +
    + `; + + document.getElementById('coachListSearch').addEventListener('input', (e) => { + filterSearch = e.target.value; + const filtered = filteredCoaches(); + if ( + expandedCoachId && + !filtered.some(c => c.coachID === expandedCoachId && coachHasUploads(c)) + ) { + expandedCoachId = null; + } + renderCoachTableBody(); + }); + + renderCoachTableBody(); +} + +function coachRowHTML(c) { + const canExpand = coachHasUploads(c); + const expanded = canExpand && expandedCoachId === c.coachID; + const expandControl = canExpand + ? ` ` + : ''; + return ` + + ${expandControl}${esc(c.username)} + ${esc(c.supervisorUsername || '—')} + ${c.clientCount ?? 0} + ${c.totalSubmissions ?? 0} + ${c.submissionsLast7d ?? 0} + ${c.submissionsLast30d ?? 0} + ${esc(c.lastSubmissionAt || '—')} + + ${expanded ? ` + + ${coachRecentPanelHTML(c.coachID)} + ` : ''}`; +} + +function wireCoachDetailPanel(coachID) { + const panel = document.querySelector(`[data-coach-panel="${CSS.escape(coachID)}"]`); + if (!panel) return; + + panel.querySelector('.coach-detail-search')?.addEventListener('input', (e) => { + getCoachUi(coachID).filter = e.target.value; + getCoachUi(coachID).clientLimit = CLIENT_GROUP_PAGE; + renderCoachTableBody(); + }); + + panel.querySelectorAll('.coach-client-toggle').forEach(btn => { + btn.addEventListener('click', () => { + const ui = getCoachUi(coachID); + const client = btn.dataset.client; + if (ui.expandedClients.has(client)) { + ui.expandedClients.delete(client); + } else { + ui.expandedClients.add(client); + } + renderCoachTableBody(); + }); + }); + + panel.querySelectorAll('.coach-show-all-uploads').forEach(btn => { + btn.addEventListener('click', () => { + getCoachUi(coachID).showAllUploads.add(btn.dataset.client); + renderCoachTableBody(); + }); + }); + + panel.querySelector('.coach-more-clients')?.addEventListener('click', () => { + getCoachUi(coachID).clientLimit += CLIENT_GROUP_PAGE; + renderCoachTableBody(); + }); + + panel.querySelector('.coach-more-uploads')?.addEventListener('click', () => { + loadMoreCoachUploads(coachID); + }); +} + +function renderCoachTableBody() { + const body = document.getElementById('coachTableBody'); + const countEl = document.getElementById('coachListCount'); + if (!body) return; + + const filtered = filteredCoaches(); + + if (!filtered.length) { + body.innerHTML = ` + + + No counselors match + + `; + if (countEl) { + countEl.textContent = filterSearch.trim() + ? `0 of ${coachesList.length} counselors` + : ''; + } + return; + } + + body.innerHTML = filtered.map(c => coachRowHTML(c)).join(''); + + if (countEl) { + const q = filterSearch.trim(); + countEl.textContent = q + ? `${filtered.length} of ${coachesList.length} counselors` + : `${coachesList.length} counselors${coachesList.length === 1 ? '' : ''}`; + } + + body.querySelectorAll('.coach-expand-btn').forEach(btn => { + btn.addEventListener('click', () => toggleCoach(btn.dataset.coach)); + }); + + if (expandedCoachId) { + wireCoachDetailPanel(expandedCoachId); + } +} + +async function fetchCoachRecent(coachID, offset = 0, append = false) { + const data = await apiGet( + `coaches.php?coachID=${encodeURIComponent(coachID)}&recent=1` + + `&limit=${FETCH_LIMIT}&offset=${offset}` + ); + const submissions = data.submissions || []; + if (append && recentByCoach[coachID]) { + const existing = recentByCoach[coachID].submissions || []; + const seen = new Set(existing.map(s => s.submissionID)); + for (const s of submissions) { + if (!seen.has(s.submissionID)) { + existing.push(s); + } + } + recentByCoach[coachID] = { + submissions: existing, + total: data.total ?? existing.length, + }; + } else { + recentByCoach[coachID] = { + submissions, + total: data.total ?? submissions.length, + }; + } +} + +async function loadMoreCoachUploads(coachID) { + const data = recentByCoach[coachID]; + if (!data) return; + const btn = document.querySelector( + `[data-coach-panel="${CSS.escape(coachID)}"] .coach-more-uploads` + ); + if (btn) { + btn.disabled = true; + btn.textContent = 'Loading…'; + } + try { + await fetchCoachRecent(coachID, data.submissions.length, true); + renderCoachTableBody(); + } catch (e) { + showToast(e.message, 'error'); + if (btn) { + btn.disabled = false; + btn.textContent = 'Load more uploads'; + } + } +} + +async function toggleCoach(coachID) { + const coach = coachesList.find(c => c.coachID === coachID); + if (!coach || !coachHasUploads(coach)) { + return; + } + if (expandedCoachId === coachID) { + expandedCoachId = null; + renderCoachTableBody(); + return; + } + expandedCoachId = coachID; + if (!coachDetailUi[coachID]) { + coachDetailUi[coachID] = { + clientLimit: CLIENT_GROUP_PAGE, + expandedClients: new Set(), + showAllUploads: new Set(), + filter: '', + }; + } + renderCoachTableBody(); + if (!recentByCoach[coachID]) { + try { + await fetchCoachRecent(coachID, 0, false); + } catch (e) { + showToast(e.message, 'error'); + recentByCoach[coachID] = { submissions: [], total: 0 }; + } + renderCoachTableBody(); + } +} + +function esc(s) { + const d = document.createElement('div'); + d.textContent = s ?? ''; + return d.innerHTML; +} diff --git a/website/js/pages/dev.js b/website/js/pages/dev.js new file mode 100644 index 0000000..3613bee --- /dev/null +++ b/website/js/pages/dev.js @@ -0,0 +1,643 @@ +import { apiGet, apiPost, apiPut, apiDownloadFetch, redirectToLogin, apiUrl } from '../api.js'; +import { getRole, pageHeaderHTML, showToast } from '../app.js'; +import { confirmAction } from '../confirm-modal.js'; +import { navigate } from '../router.js'; + +const REVOKE_ALL_CONFIRM = 'REVOKE ALL SESSIONS'; + +const ACTIVITY_LABELS = { + app_sync: 'App sync', + app_change: 'App change', + web_change: 'Website change', + web_export: 'Website export', + api_error: 'API error', +}; + +export function devPage() { + if (getRole() !== 'admin') { + navigate('#/'); + return; + } + + const app = document.getElementById('app'); + const today = new Date().toISOString().slice(0, 10); + app.innerHTML = ` + ${pageHeaderHTML('Admin Settings')} +
    +

    Security & sessions

    +

    + Login rate limits apply per username and IP. Session length applies to new logins + (website and mobile). Password-change tokens use the temporary session length. +

    +
    +
    +
    +

    +
    +

    Revoke all sessions

    +

    + Signs out everyone — all admins, supervisors, and counselors on every device. + You will be logged out too and must sign in again. Use after a suspected compromise or policy change. +

    + + + +
    +
    +
    +

    Export all response data (ZIP)

    +

    + Download one CSV per questionnaire in a single ZIP (full server data). + Current data uses live answers; + All versions includes every upload (one row per submission). +

    +
    + + +
    +
    +
    +

    Dev import

    +

    + Local / test only. Imports prefixed dev users (dev_*), + clients (DEV-CL-*), and completed questionnaire runs. + Existing real users are not modified. Conflicts are skipped (usernames, client codes, completions). + Default password: socialvrlab. Questionnaires must already exist in the database. +

    +
    + + + Use dev-test-fixture.json from the repo root (regenerate with barometer-server/scripts/generate_dev_fixture.py after importing the latest questionnaires_bundle_*.json; uneven counselor load, multi-version uploads, natural timestamps). +
    + +
    + + +
    + +
    +
    +

    Reset database

    +

    + Destructive. Deletes all clients, completions, counselors, and supervisors + (including non-dev accounts). Admin logins and questionnaire definitions are kept. +

    + +
    +
    +

    API activity log

    +

    + App syncs, website edits (translations, users, questionnaires, …), + and exports/downloads. Routine page loads (lists, dashboards) are not logged. +

    +
    + + + + + +
    + +

    +
    + + + + + + + + + + + + + + + + + +
    Time (UTC)KindSubjectMethodRouteStatusDurationValuesPerformed by
    Loading…
    +
    +
    + `; + + let parsedFixture = null; + + document.getElementById('devFixtureFile').addEventListener('change', async (e) => { + const file = e.target.files?.[0]; + const summary = document.getElementById('devFixtureSummary'); + const btn = document.getElementById('devImportBtn'); + parsedFixture = null; + btn.disabled = true; + summary.style.display = 'none'; + + if (!file) return; + + try { + const text = await file.text(); + parsedFixture = JSON.parse(text); + const st = parsedFixture.stats || {}; + summary.innerHTML = ` + Preview: + ${parsedFixture.admins?.length ?? st.admins ?? 0} admins, + ${parsedFixture.supervisors?.length ?? st.supervisors ?? 0} supervisors, + ${parsedFixture.coaches?.length ?? st.coaches ?? 0} counselors, + ${parsedFixture.clients?.length ?? st.clients ?? 0} clients, + ${parsedFixture.completions?.length ?? st.completionRecords ?? st.completions ?? 0} completions, + ${st.totalUploads ?? '—'} uploads (${st.multiVersionPairs ?? '—'} re-uploaded) + `; + summary.style.display = ''; + btn.disabled = false; + } catch (err) { + showToast('Invalid JSON: ' + err.message, 'error'); + } + }); + + document.getElementById('devRemoveBtn').addEventListener('click', async () => { + if (!(await confirmAction({ + title: 'Remove dev test data', + message: 'Remove all dev test data?\n\nDeletes users matching dev_*, clients DEV-CL-*, and their answers/completions.\nReal accounts are not affected.', + confirmLabel: 'Remove', + variant: 'danger', + }))) { + return; + } + const btn = document.getElementById('devRemoveBtn'); + const pre = document.getElementById('devImportResult'); + btn.disabled = true; + btn.textContent = 'Removing…'; + pre.style.display = 'none'; + + try { + const res = await apiPost('dev', { + action: 'remove', + usernamePrefix: 'dev_', + clientCodePrefix: 'DEV-CL-', + }); + if (!res.success) { + showToast(res.error || res.message || 'Remove failed', 'error'); + return; + } + pre.textContent = JSON.stringify(res, null, 2); + pre.style.display = ''; + const d = res.deleted || {}; + showToast( + `Removed ${d.clients ?? 0} clients, ${d.users ?? 0} users, ` + + `${d.completed_questionnaires ?? 0} completions`, + 'success' + ); + } catch (e) { + showToast(e.message, 'error'); + } finally { + btn.disabled = false; + btn.textContent = 'Remove test data'; + } + }); + + document.getElementById('devWipeBtn').addEventListener('click', async () => { + if (!(await confirmAction({ + title: 'Wipe operational data', + message: 'Remove ALL clients, completions, counselors, and supervisors?\n\nAdmin accounts and questionnaires will NOT be deleted.\nThis cannot be undone.', + confirmLabel: 'Remove all', + variant: 'danger', + }))) { + return; + } + const btn = document.getElementById('devWipeBtn'); + const pre = document.getElementById('devImportResult'); + btn.disabled = true; + btn.textContent = 'Removing…'; + pre.style.display = 'none'; + + try { + const res = await apiPost('dev', { action: 'wipeExceptAdmins' }); + if (!res.success) { + showToast(res.error || res.message || 'Wipe failed', 'error'); + return; + } + pre.textContent = JSON.stringify(res, null, 2); + pre.style.display = ''; + const d = res.deleted || {}; + const kept = res.kept?.admins ?? '?'; + showToast( + `Wiped ${d.clients ?? 0} clients, ${d.users ?? 0} users, ` + + `${d.completed_questionnaires ?? 0} completions (${kept} admins kept)`, + 'success' + ); + } catch (e) { + showToast(e.message, 'error'); + } finally { + btn.disabled = false; + btn.textContent = 'Remove all data (keep admins)'; + } + }); + + wireAdminZipExport(); + wireActivityLog(); + wireSecuritySettings(); + + document.getElementById('devImportBtn').addEventListener('click', async () => { + if (!parsedFixture) return; + const btn = document.getElementById('devImportBtn'); + const pre = document.getElementById('devImportResult'); + btn.disabled = true; + btn.textContent = 'Importing…'; + pre.style.display = 'none'; + + try { + const res = await apiPost('dev', { fixture: parsedFixture }); + if (!res.success) { + showToast(res.error || 'Import failed', 'error'); + return; + } + pre.textContent = JSON.stringify(res, null, 2); + pre.style.display = ''; + const imp = res.imported || {}; + const sk = res.skipped || {}; + showToast( + `Imported: ${imp.completions ?? 0} completions, ${imp.submissions ?? 0} uploads, ` + + `${imp.clients ?? 0} clients, ${imp.coaches ?? 0} counselors ` + + `(skipped ${sk.completions ?? 0} existing)`, + 'success' + ); + if (res.errors?.length) { + showToast(`${res.errors.length} warning(s) — see log below`, 'error'); + } + } catch (e) { + showToast(e.message, 'error'); + } finally { + btn.disabled = false; + btn.textContent = 'Import fixture'; + } + }); +} + +function renderActivityLogStatus(status) { + const el = document.getElementById('activityLogStatus'); + if (!el || !status) { + if (el) el.style.display = 'none'; + return; + } + if (!status.enabled) { + el.className = 'field-hint callout-warn'; + el.innerHTML = 'Activity logging is disabled (QDB_API_LOG=0 in .env).'; + el.style.display = ''; + return; + } + if (status.writable) { + el.className = 'field-hint'; + el.innerHTML = `Logging to ${escHtml(status.dir)}` + + (status.php_user ? ` (PHP user: ${escHtml(status.php_user)})` : ''); + el.style.display = ''; + return; + } + el.className = 'field-hint callout-danger'; + el.innerHTML = `Cannot write activity log. ${escHtml(status.error || 'Permission problem.')} +
    Path: ${escHtml(status.dir)}` + + (status.php_user ? `
    PHP runs as: ${escHtml(status.php_user)}` : '') + + (status.fix_hint ? `
    ${escHtml(status.fix_hint)}` : ''); + el.style.display = ''; +} + +function wireActivityLog() { + const dateEl = document.getElementById('activityLogDate'); + const kindEl = document.getElementById('activityLogKind'); + document.getElementById('activityLogRefresh')?.addEventListener('click', loadActivityLog); + dateEl?.addEventListener('change', loadActivityLog); + kindEl?.addEventListener('change', loadActivityLog); + loadActivityLog(); +} + +async function loadActivityLog() { + const tbody = document.getElementById('activityLogBody'); + const meta = document.getElementById('activityLogMeta'); + if (!tbody) return; + + const date = document.getElementById('activityLogDate')?.value || new Date().toISOString().slice(0, 10); + const activity = document.getElementById('activityLogKind')?.value || ''; + tbody.innerHTML = 'Loading…'; + + try { + const q = new URLSearchParams({ date, limit: '300' }); + if (activity) q.set('activity', activity); + const data = await apiGet(`activity-log?${q}`); + if (!data.success) throw new Error(data.error || 'Failed to load activity log'); + + renderActivityLogStatus(data.logStatus); + + const entries = data.entries || []; + const files = (data.files || []).join(', ') || 'none'; + const trunc = data.truncated ? ' (newest 300 shown)' : ''; + if (meta) { + meta.textContent = entries.length + ? `${entries.length} entries from ${files}${trunc}` + : `No entries for this date/filter (files: ${files})`; + } + + if (!entries.length) { + tbody.innerHTML = 'No activity recorded'; + return; + } + + tbody.innerHTML = entries.map((e) => { + const kind = e.activity || ''; + const label = ACTIVITY_LABELS[kind] || kind || '—'; + const badgeClass = kind ? `badge-activity-${kind}` : ''; + const time = formatActivityTime(e.ts); + const statusNum = e.status != null ? Number(e.status) : null; + const status = statusNum != null ? String(statusNum) : '—'; + const statusClass = statusNum != null && statusNum >= 400 ? 'error-text' : ''; + const dur = e.duration_ms != null ? `${e.duration_ms} ms` : '—'; + const subject = formatActivitySubject(e); + const performedBy = formatActivityPerformer(e); + const errPart = e.error + ? `
    ${escHtml(e.error)}
    ` + : ''; + return ` + ${escHtml(time)} + ${escHtml(label)} + ${subject} + ${escHtml(e.method || '')} + ${escHtml(e.route || '')} + ${escHtml(status)} + ${escHtml(dur)} + ${renderActivityChanges(e.changes)} + ${performedBy}${errPart} + `; + }).join(''); + } catch (err) { + if (meta) meta.textContent = ''; + tbody.innerHTML = `${escHtml(err.message)}`; + } +} + +function formatActivitySubject(entry) { + if (entry.target_username) { + return `${escHtml(entry.target_username)}`; + } + const changes = entry.changes || []; + const userChange = changes.find((c) => (c.field || '').toLowerCase() === 'username'); + if (userChange) { + const name = userChange.display || formatActivityChangeValue(userChange.value); + return `${escHtml(name)}`; + } + const client = changes.find((c) => (c.field || '').toLowerCase().includes('clientcode')); + if (client) { + const code = client.display || formatActivityChangeValue(client.value); + return `client ${escHtml(code)}`; + } + const qClient = changes.find((c) => (c.field || '') === 'query.clientCode'); + if (qClient) { + return `client ${escHtml(formatActivityChangeValue(qClient.value))}`; + } + return ''; +} + +function formatActivityPerformer(entry) { + if (entry.actor_username) { + const role = entry.role ? ` (${escHtml(entry.role)})` : ''; + return `${escHtml(entry.actor_username)}${role}`; + } + if (entry.role) { + return escHtml(entry.role); + } + return '—'; +} + +function renderActivityChanges(changes) { + const list = Array.isArray(changes) ? changes : []; + if (!list.length) { + return ''; + } + const rows = list.map((c) => { + const field = c.field ?? ''; + const raw = formatActivityChangeValue(c.value); + const display = (c.display != null && String(c.display) !== '') + ? String(c.display) + : raw; + const showRaw = display !== raw + && !raw.startsWith('[REDACTED]') + && !raw.startsWith('[ENCRYPTED'); + return ` + ${escHtml(field)} + + ${escHtml(display)} + ${showRaw ? `
    ${escHtml(raw)}
    ` : ''} + + `; + }).join(''); + return `${rows}
    `; +} + +function formatActivityChangeValue(value) { + if (value === null || value === undefined) return 'null'; + if (typeof value === 'boolean') return value ? 'true' : 'false'; + return String(value); +} + +function formatActivityTime(iso) { + if (!iso) return '—'; + try { + const d = new Date(iso); + if (Number.isNaN(d.getTime())) return iso; + return d.toISOString().replace('T', ' ').slice(0, 19); + } catch { + return iso; + } +} + +function escHtml(s) { + const d = document.createElement('div'); + d.textContent = s ?? ''; + return d.innerHTML; +} + +function filenameFromContentDisposition(res, fallback) { + const disp = res.headers.get('Content-Disposition'); + if (!disp) return fallback; + const m = /filename="([^"]+)"/i.exec(disp); + return m ? m[1] : fallback; +} + +async function downloadExportZip(url, defaultFilename) { + const res = await apiDownloadFetch(url); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: 'Download failed' })); + throw new Error(err.error?.message || err.error || 'Download failed'); + } + const blob = await res.blob(); + const blobUrl = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = blobUrl; + a.download = filenameFromContentDisposition(res, defaultFilename); + a.click(); + URL.revokeObjectURL(blobUrl); +} + +function wireAdminZipExport() { + document.getElementById('exportAllCurrentZipBtn')?.addEventListener('click', async () => { + const btn = document.getElementById('exportAllCurrentZipBtn'); + btn.disabled = true; + const prev = btn.textContent; + btn.textContent = 'Preparing ZIP…'; + try { + await downloadExportZip(apiUrl('export?exportAll=1'), 'responses_export_current.zip'); + showToast('ZIP download started (current data)', 'success'); + } catch (e) { + showToast(e.message, 'error'); + } finally { + btn.disabled = false; + btn.textContent = prev; + } + }); + + document.getElementById('exportAllVersionsZipBtn')?.addEventListener('click', async () => { + const btn = document.getElementById('exportAllVersionsZipBtn'); + btn.disabled = true; + const prev = btn.textContent; + btn.textContent = 'Preparing ZIP…'; + try { + await downloadExportZip(apiUrl('export?exportAll=1&allVersions=1'), 'responses_export_all_versions.zip'); + showToast('ZIP download started (all versions)', 'success'); + } catch (e) { + showToast(e.message, 'error'); + } finally { + btn.disabled = false; + btn.textContent = prev; + } + }); +} + +function wireSecuritySettings() { + const formEl = document.getElementById('securitySettingsForm'); + const metaEl = document.getElementById('securitySettingsMeta'); + const revokeInput = document.getElementById('revokeAllConfirm'); + const revokeBtn = document.getElementById('revokeAllSessionsBtn'); + if (!formEl) return; + + revokeInput?.addEventListener('input', () => { + if (revokeBtn) { + revokeBtn.disabled = revokeInput.value.trim() !== REVOKE_ALL_CONFIRM; + } + }); + + revokeBtn?.addEventListener('click', async () => { + if (revokeInput.value.trim() !== REVOKE_ALL_CONFIRM) return; + if (!(await confirmAction({ + title: 'Revoke all sessions', + message: 'Revoke every active session?\n\nAll users (including you) will be signed out on website and mobile.', + confirmLabel: 'Revoke all', + variant: 'danger', + }))) { + return; + } + revokeBtn.disabled = true; + try { + const data = await apiPost('settings', { + action: 'revokeAllSessions', + confirmPhrase: REVOKE_ALL_CONFIRM, + }); + if (!data.success) throw new Error(data.error || 'Failed'); + showToast(`Revoked ${data.revokedSessions ?? 0} session(s). Signing you out…`, 'success'); + revokeInput.value = ''; + setTimeout(() => redirectToLogin(), 800); + } catch (err) { + showToast(err.message, 'error'); + revokeBtn.disabled = revokeInput.value.trim() !== REVOKE_ALL_CONFIRM; + } + }); + + loadSecuritySettings(formEl, metaEl); +} + +async function loadSecuritySettings(formEl, metaEl) { + try { + const data = await apiGet('settings'); + if (!data.success) throw new Error(data.error || 'Failed to load settings'); + const s = data.settings || {}; + const active = data.activeSessions ?? 0; + if (metaEl) { + metaEl.textContent = `${active} active session(s) in the database.`; + } + formEl.innerHTML = ` +
    +
    + + + Before lockout (per username + IP) +
    +
    + + +
    +
    + + +
    +
    + + + Website & app after normal login +
    +
    + + + Forced password change flow +
    +
    + + `; + document.getElementById('saveSecuritySettingsBtn')?.addEventListener('click', () => { + saveSecuritySettings(formEl, metaEl); + }); + } catch (err) { + formEl.innerHTML = `

    ${escHtml(err.message)}

    `; + } +} + +async function saveSecuritySettings(formEl, metaEl) { + const btn = document.getElementById('saveSecuritySettingsBtn'); + const payload = { + login_max_attempts: parseInt(document.getElementById('setLoginMax')?.value || '10', 10), + login_window_seconds: parseInt(document.getElementById('setLoginWindow')?.value || '15', 10) * 60, + login_lockout_seconds: parseInt(document.getElementById('setLoginLockout')?.value || '15', 10) * 60, + session_ttl_seconds: parseInt(document.getElementById('setSessionDays')?.value || '30', 10) * 86400, + temp_session_ttl_seconds: parseInt(document.getElementById('setTempSession')?.value || '10', 10) * 60, + }; + if (btn) btn.disabled = true; + try { + const data = await apiPut('settings', payload); + if (!data.success) throw new Error(data.error || 'Save failed'); + showToast('Security settings saved', 'success'); + await loadSecuritySettings(formEl, metaEl); + } catch (err) { + showToast(err.message, 'error'); + } finally { + if (btn) btn.disabled = false; + } +} + +function escAttr(v) { + return String(v ?? '') + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/ +
    + ${homeNavButton()} +
    +

    ${isNew ? 'New Questionnaire' : 'Loading...'}

    +
    +
    +
    + Session measures + ${!isNew ? `View Results` : ''} +
    + +
    + `; + + 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: '{}' }; + questions = []; + renderEditor(); + } else { + try { + const quData = await apiGet( + `questions.php?questionnaireID=${encodeURIComponent(params.id)}&includeRetired=1` + ); + questionnaire = allQuestionnaires.find(q => q.questionnaireID === params.id); + if (!questionnaire) { + showToast('Questionnaire not found', 'error'); + navigate('#/questionnaires'); + return; + } + questionnaire.structureRevision = quData.structureRevision ?? questionnaire.structureRevision ?? 1; + const allQs = quData.questions || []; + questions = allQs.filter(q => !q.retired); + retiredQuestions = allQs.filter(q => q.retired); + document.getElementById('editorTitle').textContent = questionnaire.name; + renderEditor(); + } catch (e) { + showToast(e.message, 'error'); + document.getElementById('editorContent').innerHTML = `

    ${esc(e.message)}

    `; + } + } +} + +// ── Layout helpers ─────────────────────────────────────────────────────── + +function layoutSelectHTML(selected = 'radio_question', id = '') { + return ``; +} + +function layoutLabel(value) { + const t = LAYOUT_TYPES.find(t => t.value === value); + return t ? t.label : value || 'unknown'; +} + +const STABLE_KEY_RE = /^[a-zA-Z][a-zA-Z0-9_]*$/; + +function parseConfig(q) { + try { return JSON.parse(q.configJson || '{}'); } catch { return {}; } +} + +function questionKeyOf(q) { + const cfg = typeof q === 'object' && q.configJson !== undefined ? parseConfig(q) : (q.config || q); + return (q.questionKey || cfg.questionKey || '').trim(); +} + +function questionLocalIds() { + return questions.map(q => { + const parts = q.questionID.split('__'); + const localId = parts[parts.length - 1]; + return { + questionID: q.questionID, + localId, + defaultText: q.defaultText, + questionKey: questionKeyOf(q), + }; + }); +} + +function branchTargetLabel(q) { + const key = questionKeyOf(q) || q.localId; + return `${q.localId} · ${key}`; +} + +function branchTargetOptionsHTML(selected = '') { + return questionLocalIds().map(q => + `` + ).join(''); +} + +function validateStableKey(key, label = 'Key') { + if (!STABLE_KEY_RE.test(key)) { + showToast(`${label} must match [a-zA-Z][a-zA-Z0-9_]*`, 'error'); + return false; + } + return true; +} + +function optionKeyOf(o) { + if (o.optionKey) return o.optionKey; + const dt = (o.defaultText || '').trim(); + return STABLE_KEY_RE.test(dt) ? dt : ''; +} + +function optionGermanLabel(o) { + return (o.labelGerman || '').trim() || (STABLE_KEY_RE.test(o.defaultText || '') ? '' : o.defaultText) || ''; +} + +function notesSectionHTML(config, prefix, questionKey) { + const nbKey = questionKey ? `${questionKey}_note_before` : '—'; + const naKey = questionKey ? `${questionKey}_note_after` : '—'; + return ` +
    +

    Notes (German)

    +
    +
    + + + Key: ${esc(nbKey)} +
    +
    + + + Key: ${esc(naKey)} +
    +
    +
    `; +} + +function readNotesFromForm(prefix) { + const val = id => document.getElementById(`${prefix}_${id}`)?.value?.trim() || ''; + return { noteBefore: val('noteBefore'), noteAfter: val('noteAfter') }; +} + +function glassSymptomsRowsFromQuestion(q) { + if (Array.isArray(q?.glassSymptoms) && q.glassSymptoms.length) { + return q.glassSymptoms.map(r => ({ + key: (r.key || '').trim(), + labelDe: (r.labelDe || '').trim(), + scoreLevels: r.scoreLevels || defaultGlassScoreLevels(), + })); + } + const config = parseConfig(q); + return (config.symptoms || []).map(key => ({ + key: String(key).trim(), + labelDe: '', + scoreLevels: defaultGlassScoreLevels(), + })); +} + +function defaultGlassScoreLevels() { + return { never_glass: 0, little_glass: 1, moderate_glass: 2, much_glass: 3, extreme_glass: 4 }; +} + +function valueScoreRulesSectionHTML(prefix, config, scoreRules = []) { + const min = Number(config.range?.min ?? 0); + const max = Number(config.range?.max ?? 100); + const step = Math.max(1, Number(config.step ?? 1)); + const lo = Math.min(min, max); + const hi = Math.max(min, max); + const ruleMap = {}; + for (const r of scoreRules || []) { + ruleMap[r.levelKey] = r.points; + } + const values = []; + for (let v = lo; v <= hi; v += step) values.push(v); + if (!values.length) values.push(lo); + const rows = values.map(v => ` +
    + ${v} + +
    `).join(''); + return ` +
    + +

    Set points for each selectable value (used when the questionnaire is scored).

    +
    ${rows}
    +
    `; +} + +function readValueScoreRulesFromForm(prefix) { + const container = document.getElementById(`${prefix}_value_scores`); + if (!container) return []; + const rules = []; + container.querySelectorAll('.value-score-pts').forEach(inp => { + rules.push({ + scopeKey: '', + levelKey: String(inp.dataset.value), + points: parseInt(inp.value || '0', 10) || 0, + }); + }); + return rules; +} + +function wireValueScoreRules(prefix) { + const minInp = document.getElementById(`${prefix}_rangeMin`); + const maxInp = document.getElementById(`${prefix}_rangeMax`); + const stepInp = document.getElementById(`${prefix}_step`); + const rerender = () => { + const section = document.getElementById(`${prefix}_value_scores`); + if (!section) return; + const config = { + range: { + min: parseInt(minInp?.value || '0', 10), + max: parseInt(maxInp?.value || '100', 10), + }, + step: parseInt(stepInp?.value || '1', 10) || 1, + }; + const existing = readValueScoreRulesFromForm(prefix); + section.outerHTML = valueScoreRulesSectionHTML(prefix, config, existing); + }; + [minInp, maxInp, stepInp].forEach(el => el?.addEventListener('change', rerender)); +} + +function suggestSymptomKey(label) { + let slug = (label || '').trim().toLowerCase() + .replace(/[^a-z0-9]+/g, '_') + .replace(/^_+|_+$/g, ''); + if (!slug) return ''; + if (!/^[a-z]/.test(slug)) slug = `sym_${slug}`; + if (!STABLE_KEY_RE.test(slug)) slug = `sym_${slug.replace(/^[^a-z]+/, '')}`; + return STABLE_KEY_RE.test(slug) ? slug : ''; +} + +function glassSymptomsSectionHTML(rows, prefix) { + const list = rows.length ? rows : []; + return ` +
    +
    + + +
    +

    Each row is one table line in the app. German text is saved with the question; translate other languages on the Translations tab.

    +
      + ${list.length ? list.map((row, i) => glassSymptomRowHTML(prefix, row, i)).join('') + : '
    • No symptoms yet.
    • '} +
    +
    `; +} + +function glassSymptomRowHTML(prefix, row, idx) { + const levels = row.scoreLevels || defaultGlassScoreLevels(); + const ptsRow = GLASS_SCORE_LEVELS.map(l => ` +
    + + +
    `).join(''); + return ` +
  • +
    +
    + + +
    +
    + + +
    + +
    +
    ${ptsRow}
    +
  • `; +} + +function readGlassSymptomsFromForm(prefix) { + const container = document.getElementById(`${prefix}_glass_symptoms`); + if (!container) return []; + const rows = []; + container.querySelectorAll('.glass-symptom-row').forEach(rowEl => { + const key = rowEl.querySelector('.glass-symptom-key')?.value?.trim() || ''; + const labelDe = rowEl.querySelector('.glass-symptom-label')?.value?.trim() || ''; + const scoreLevels = {}; + rowEl.querySelectorAll('.glass-score-pts').forEach(inp => { + scoreLevels[inp.dataset.level] = parseInt(inp.value || '0', 10) || 0; + }); + if (key || labelDe) rows.push({ key, labelDe, scoreLevels }); + }); + return rows; +} + +function validateGlassSymptoms(rows) { + const seen = new Set(); + for (const row of rows) { + if (!row.key) { + showToast('Each symptom needs a key', 'error'); + return false; + } + if (!validateStableKey(row.key, 'Symptom key')) return false; + if (!row.labelDe) { + showToast(`German label is required for symptom "${row.key}"`, 'error'); + return false; + } + if (seen.has(row.key)) { + showToast(`Duplicate symptom key "${row.key}"`, 'error'); + return false; + } + seen.add(row.key); + } + return true; +} + +function wireGlassSymptomsSection(prefix) { + const container = document.getElementById(`${prefix}_glass_symptoms`); + if (!container) return; + + const renderList = (rows) => { + const list = container.querySelector('.glass-symptoms-list'); + if (!list) return; + if (!rows.length) { + list.innerHTML = '
  • No symptoms yet.
  • '; + return; + } + list.innerHTML = rows.map((row, i) => glassSymptomRowHTML(prefix, row, i)).join(''); + bindRowEvents(); + }; + + const collectRows = () => readGlassSymptomsFromForm(prefix); + + const bindRowEvents = () => { + container.querySelectorAll('.glass-symptom-remove').forEach(btn => { + btn.addEventListener('click', () => { + const rowEl = btn.closest('.glass-symptom-row'); + rowEl?.remove(); + const list = container.querySelector('.glass-symptoms-list'); + if (list && !list.querySelector('.glass-symptom-row')) { + list.innerHTML = '
  • No symptoms yet.
  • '; + } + }); + }); + container.querySelectorAll('.glass-symptom-label').forEach(inp => { + inp.addEventListener('blur', () => { + const rowEl = inp.closest('.glass-symptom-row'); + const keyInp = rowEl?.querySelector('.glass-symptom-key'); + if (!keyInp || keyInp.value.trim()) return; + const suggested = suggestSymptomKey(inp.value); + if (suggested) keyInp.value = suggested; + }); + }); + }; + + container.querySelector('[data-action=add-glass-symptom]')?.addEventListener('click', () => { + const rows = collectRows(); + rows.push({ key: '', labelDe: '', scoreLevels: defaultGlassScoreLevels() }); + renderList(rows); + const lastKey = container.querySelector('.glass-symptom-row:last-child .glass-symptom-label'); + lastKey?.focus(); + }); + + bindRowEvents(); +} + +function stringSpinnerOtherEnabled(config) { + return Boolean(config.otherNextQuestionId || config.otherOptionKey); +} + +/** Question types that use config.nextQuestionId (not per-option branching). */ +const QUESTION_LEVEL_NEXT_LAYOUTS = new Set([ + 'value_spinner', + 'slider_question', + 'free_text', + 'date_spinner', + 'multi_check_box_question', + 'glass_scale_question', + 'client_coach_code_question', + 'last_page', +]); + +function questionNextSectionHTML(config, prefix, { label, hint } = {}) { + const next = config.nextQuestionId || ''; + return ` +
    + + + ${hint || 'Where to go after this question. Leave empty to continue in list order.'} +
    `; +} + +function stringSpinnerNextSectionHTML(config, prefix) { + return questionNextSectionHTML(config, prefix, { + label: 'Next question (list selection)', + hint: 'Where to go after a normal spinner choice. Required when “Other” targets a question that sits next in order (so list picks can skip it).', + }); +} + +function readQuestionNextFromForm(prefix, config) { + const next = document.getElementById(`${prefix}_nextQuestionId`)?.value?.trim() || ''; + if (next) config.nextQuestionId = next; +} + +function stringSpinnerOtherSectionHTML(config, prefix) { + const enabled = stringSpinnerOtherEnabled(config); + const otherKey = config.otherOptionKey || 'other_option'; + const otherNext = config.otherNextQuestionId || ''; + return ` +
    + +
    +
    +
    + + + Appends this label to the spinner. Translate under Questionnaire UI strings (or App strings if listed in catalog). +
    +
    + + + Use layout Free text for the follow-up (e.g. country name). +
    +
    +
    +
    `; +} + +function wireStringSpinnerOtherToggle(prefix) { + const cb = document.getElementById(`${prefix}_otherEnabled`); + const panel = document.getElementById(`${prefix}_otherFields`); + if (!cb || !panel) return; + const sync = () => { panel.style.display = cb.checked ? '' : 'none'; }; + cb.addEventListener('change', sync); + sync(); +} + +function validateStringSpinnerConfig(prefix) { + const defaultNext = document.getElementById(`${prefix}_nextQuestionId`)?.value.trim() || ''; + const enabled = document.getElementById(`${prefix}_otherEnabled`)?.checked; + if (enabled) { + const key = document.getElementById(`${prefix}_otherOptionKey`)?.value.trim() || ''; + const otherNext = document.getElementById(`${prefix}_otherNextQuestionId`)?.value.trim() || ''; + if (!key) { + showToast('Other label translation key is required', 'error'); + return false; + } + if (!validateStableKey(key, 'Other label key')) return false; + if (!otherNext) { + showToast('Select the free-text question for “Other”', 'error'); + return false; + } + const target = questions.find(q => (q.localId || q.questionID.split('__').pop()) === otherNext); + if (target && target.type !== 'free_text') { + showToast('“Other” should branch to a Free text question', 'error'); + return false; + } + if (!defaultNext) { + showToast('Set “Next question (list selection)” so normal choices do not fall through to the free-text step', 'error'); + return false; + } + if (defaultNext === otherNext) { + showToast('“Next question” and “Other” branch cannot target the same question', 'error'); + return false; + } + } + return true; +} + +// ── Config form HTML for each layout type ──────────────────────────────── + +function configFormHTML(layout, config, prefix, opts = {}) { + let html = ''; + switch (layout) { + case 'radio_question': + html = ` +
    + + +
    +

    Branching: set Next question on each answer option below.

    `; + break; + case 'multi_check_box_question': + html = ` +
    + + +
    + ${stringSpinnerOtherSectionHTML(config, prefix)}`; + break; + case 'glass_scale_question': { + const scaleType = config.scaleType || 'glass'; + const rows = opts.glassSymptoms || (config.symptoms || []).map(k => ({ key: k, labelDe: '' })); + html = ` +
    + + +
    + ${glassSymptomsSectionHTML(rows, prefix)}`; + break; + } + case 'value_spinner': + case 'slider_question': { + const scoreRules = opts.scoreRules || []; + html = ` +
    +
    + + +
    +
    + + +
    + ${layout === 'slider_question' ? ` +
    + + +
    +
    + + +
    ` : ''} +
    + ${valueScoreRulesSectionHTML(prefix, config, scoreRules)}`; + break; + } + case 'date_spinner': { + const notBefore = config.constraints?.notBefore || ''; + const notAfter = config.constraints?.notAfter || ''; + const precision = config.precision || 'full'; + html = ` +
    + + +
    +
    +
    + + +
    +
    + + +
    +
    `; + break; + } + case 'string_spinner': + html = ` +
    + + + Do not include the “Other” row here; enable it below. +
    + ${stringSpinnerNextSectionHTML(config, prefix)} + ${stringSpinnerOtherSectionHTML(config, prefix)}`; + break; + case 'free_text': + html = ` +
    +
    + + +
    +
    + + +
    +
    +
    + + +
    `; + break; + case 'client_coach_code_question': + html = ` +
    +
    + + +
    +
    + + +
    +
    `; + break; + case 'last_page': + html = ` +
    + + +
    `; + break; + } + if (QUESTION_LEVEL_NEXT_LAYOUTS.has(layout)) { + html += questionNextSectionHTML(config, prefix); + } + return html ? `
    ${html}
    ` : ''; +} + +function readConfigFromForm(layout, prefix) { + const config = {}; + const val = id => document.getElementById(`${prefix}_${id}`)?.value?.trim() || ''; + + switch (layout) { + case 'radio_question': + if (val('textKey')) config.textKey = val('textKey'); + break; + case 'multi_check_box_question': { + const ms = parseInt(val('minSelection') || '0', 10); + if (ms > 0) config.minSelection = ms; + const otherEnabled = document.getElementById(`${prefix}_otherEnabled`)?.checked; + if (otherEnabled) { + const ok = val('otherOptionKey') || 'other_option'; + const on = val('otherNextQuestionId'); + if (ok) config.otherOptionKey = ok; + if (on) config.otherNextQuestionId = on; + } + break; + } + case 'glass_scale_question': { + const st = val('scaleType') || 'glass'; + if (st && st !== 'glass') config.scaleType = st; + break; + } + case 'value_spinner': + case 'slider_question': + config.range = { + min: parseInt(val('rangeMin') || '0', 10), + max: parseInt(val('rangeMax') || '100', 10), + }; + if (layout === 'slider_question') { + const step = parseInt(val('step') || '1', 10); + if (step > 0) config.step = step; + if (val('unitLabel')) config.unitLabel = val('unitLabel'); + } + break; + case 'date_spinner': { + const prec = val('precision') || 'full'; + if (prec && prec !== 'full') config.precision = prec; + const nb = val('notBefore'); + const na = val('notAfter'); + if (nb || na) { + config.constraints = {}; + if (nb) config.constraints.notBefore = nb; + if (na) config.constraints.notAfter = na; + } + break; + } + case 'string_spinner': { + const raw = val('stringOptions'); + const opts = raw.split('\n').map(s => s.trim()).filter(Boolean); + if (opts.length) config.options = opts; + const otherEnabled = document.getElementById(`${prefix}_otherEnabled`)?.checked; + if (otherEnabled) { + const ok = val('otherOptionKey') || 'other_option'; + const on = val('otherNextQuestionId'); + if (ok) config.otherOptionKey = ok; + if (on) config.otherNextQuestionId = on; + } + break; + } + case 'free_text': { + if (val('textKey')) config.textKey = val('textKey'); + if (val('hint')) config.hint = val('hint'); + const ml = parseInt(val('maxLength') || '500', 10); + if (!Number.isNaN(ml) && ml > 0) config.maxLength = Math.min(ml, 10000); + break; + } + case 'client_coach_code_question': + if (val('hint1')) config.hint1 = val('hint1'); + if (val('hint2')) config.hint2 = val('hint2'); + break; + case 'last_page': + if (val('textKey')) config.textKey = val('textKey'); + break; + } + if (QUESTION_LEVEL_NEXT_LAYOUTS.has(layout) || layout === 'string_spinner') { + readQuestionNextFromForm(prefix, config); + } + return JSON.stringify(config); +} + +// ── Main editor render ─────────────────────────────────────────────────── + +function renderEditor() { + const container = document.getElementById('editorContent'); + const editable = canEdit(); + const condJson = questionnaire.conditionJson || '{}'; + + container.innerHTML = ` +
    +

    Questionnaire Details

    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    + + +
    +
    + + +
    +
    +
    + ${editable ? `` : ''} +
    + + ${!isNew ? ` +
    + + + +
    +
    +
    +
    +

    Questions

    + ${editable ? `` : ''} +
    + +
      + +
      +
      + + + ` : '

      Save the questionnaire first, then add questions.

      '} + `; + + if (editable) { + document.getElementById('saveMetaBtn').addEventListener('click', saveMeta); + } + initConditionEditor(condJson); + + if (!isNew) { + initTabs(); + renderQuestions(); + renderRetiredQuestions(); + if (editable) { + document.getElementById('addQuestionBtn').addEventListener('click', showAddQuestionForm); + initDragReorder(); + } + } +} + +function applyStructureRevision(rev) { + if (rev == null) return; + questionnaire.structureRevision = rev; + const input = document.querySelector('.meta-form input[disabled][title*="Auto-incremented"]'); + if (input) input.value = String(rev); +} + +function notifyStructureBump(data) { + const rev = data?.structureRevision; + if (rev != null) { + applyStructureRevision(rev); + showToast(`Structure revision bumped to ${rev}.`, 'info'); + } +} + +function initTabs() { + const tabBar = document.getElementById('editorTabs'); + if (!tabBar) return; + tabBar.querySelectorAll('button').forEach(btn => { + btn.addEventListener('click', () => { + const tab = btn.dataset.tab; + if (tab === activeTab) return; + activeTab = tab; + tabBar.querySelectorAll('button').forEach(b => b.classList.toggle('active', b.dataset.tab === tab)); + document.getElementById('tabContent-questions').style.display = tab === 'questions' ? '' : 'none'; + document.getElementById('tabContent-translations').style.display = tab === 'translations' ? '' : 'none'; + document.getElementById('tabContent-preview').style.display = tab === 'preview' ? '' : 'none'; + if (tab === 'translations') loadTranslationsTab(); + if (tab === 'preview') loadPreviewTab(); + }); + }); +} + +function loadPreviewTab() { + const mount = document.getElementById('previewContent'); + if (!mount) return; + mountQuestionnairePreview(mount, { + questionnaireName: questionnaire?.name || '', + questions, + }); +} + +async function fetchAppStringGerman(messageKey) { + if (!messageKey) return ''; + try { + 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 ''; + } +} + +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 = '
      '; + 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() { + const name = document.getElementById('metaName').value.trim(); + const version = document.getElementById('metaVersion').value.trim(); + const state = document.getElementById('metaState').value; + const orderIndex = parseInt(document.getElementById('metaOrder').value || '0', 10); + const showPoints = 0; + const categoryKey = (document.getElementById('metaCategoryKey')?.value || '').trim(); + let conditionJson = '{}'; + let conditionForm = null; + if (conditionEditorApi) { + try { + conditionJson = conditionEditorApi.buildJson(); + conditionForm = conditionEditorApi.readForm(); + } catch (e) { + showToast(e.message || 'Invalid availability settings', 'error'); + return; + } + } + if (!name) { showToast('Name is required', 'error'); return; } + + try { + if (isNew) { + const data = await apiPost('questionnaires.php', { name, version, state, orderIndex, showPoints, conditionJson, categoryKey }); + if (data.success) { + questionnaire = data.questionnaire; + isNew = false; + await saveConditionMessageLabel(conditionForm); + showToast('Questionnaire created', 'success'); + navigate(`#/questionnaire/${questionnaire.questionnaireID}`); + } + } else { + const data = await apiPut('questionnaires.php', { + questionnaireID: questionnaire.questionnaireID, + name, version, state, orderIndex, showPoints, conditionJson, categoryKey + }); + if (data.success) { + questionnaire = { ...questionnaire, name, version, state, orderIndex, showPoints, conditionJson, categoryKey }; + document.getElementById('editorTitle').textContent = name; + await saveConditionMessageLabel(conditionForm); + showToast('Saved', 'success'); + } + } + } catch (e) { + showToast(e.message, 'error'); + } +} + +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() { + const wrapper = document.getElementById('addQuestionFormWrapper'); + if (!wrapper) return; + + const pendingOptions = []; + + wrapper.style.display = ''; + wrapper.innerHTML = ` +
      +

      New Question

      +
      +
      + + +
      +
      + + +
      +
      + + ${layoutSelectHTML('radio_question', 'aq_type')} +
      +
      + + +
      +
      ${notesSectionHTML({}, 'aq_cfg', '')}
      + +
      +
      + +
        +
        +
        + +
        +
        + +
        +
        + +
        +
        + + +
        + +
        +
        +
        + +
        + + +
        +
        + `; + + function updateTypeUI() { + const type = document.getElementById('aq_type').value; + const qKey = document.getElementById('aq_questionKey')?.value.trim() || ''; + document.getElementById('aq_options_section').style.display = OPTION_TYPES.has(type) ? '' : 'none'; + document.getElementById('aq_config_section').innerHTML = configFormHTML(type, {}, 'aq_cfg'); + wireStringSpinnerOtherToggle('aq_cfg'); + if (type === 'glass_scale_question') wireGlassSymptomsSection('aq_cfg'); + if (type === 'slider_question' || type === 'value_spinner') wireValueScoreRules('aq_cfg'); + const notesEl = document.getElementById('aq_notes_section'); + if (notesEl) notesEl.innerHTML = notesSectionHTML({}, 'aq_cfg', qKey); + } + + function renderPendingOptions() { + const list = document.getElementById('aq_pending_options'); + if (!list) return; + if (!pendingOptions.length) { + list.innerHTML = `
      • No options yet.
      • `; + return; + } + list.innerHTML = pendingOptions.map((opt, i) => ` +
      • + ${esc(opt.optionKey)} + ${esc(opt.text)} + ${opt.points} pts + ${opt.nextQuestionId ? `→ ${esc(opt.nextQuestionId)}` : ''} + +
      • + `).join(''); + list.querySelectorAll('.remove-pending-opt').forEach(btn => { + btn.addEventListener('click', () => { + pendingOptions.splice(parseInt(btn.dataset.idx, 10), 1); + renderPendingOptions(); + }); + }); + } + + function addPendingOption() { + const optionKey = document.getElementById('aq_opt_key').value.trim(); + const text = document.getElementById('aq_opt_text').value.trim(); + const pts = parseInt(document.getElementById('aq_opt_pts').value || '0', 10); + const next = document.getElementById('aq_opt_next').value; + if (!validateStableKey(optionKey, 'Option key')) return; + if (!text) { showToast('German label is required', 'error'); return; } + pendingOptions.push({ optionKey, text, points: pts, nextQuestionId: next }); + renderPendingOptions(); + document.getElementById('aq_opt_key').value = ''; + document.getElementById('aq_opt_text').value = ''; + document.getElementById('aq_opt_pts').value = '0'; + document.getElementById('aq_opt_next').value = ''; + document.getElementById('aq_opt_text').focus(); + } + + updateTypeUI(); + renderPendingOptions(); + document.getElementById('aq_text').focus(); + + document.getElementById('aq_type').addEventListener('change', updateTypeUI); + document.getElementById('aq_questionKey').addEventListener('input', () => { + const notesEl = document.getElementById('aq_notes_section'); + if (notesEl) { + const nb = document.getElementById('aq_cfg_noteBefore')?.value ?? ''; + const na = document.getElementById('aq_cfg_noteAfter')?.value ?? ''; + notesEl.innerHTML = notesSectionHTML( + { noteBefore: nb, noteAfter: na }, + 'aq_cfg', + document.getElementById('aq_questionKey').value.trim() + ); + } + }); + document.getElementById('aq_opt_add').addEventListener('click', addPendingOption); + document.getElementById('aq_opt_text').addEventListener('keydown', (e) => { + if (e.key === 'Enter') { e.preventDefault(); addPendingOption(); } + }); + + document.getElementById('aq_submit').addEventListener('click', async () => { + const questionKey = document.getElementById('aq_questionKey').value.trim(); + const text = document.getElementById('aq_text').value.trim(); + const type = document.getElementById('aq_type').value; + const isRequired = document.getElementById('aq_required').checked ? 1 : 0; + const btn = document.getElementById('aq_submit'); + const notes = readNotesFromForm('aq_cfg'); + const configJson = readConfigFromForm(type, 'aq_cfg'); + const postBody = { + questionnaireID: questionnaire.questionnaireID, + questionKey, + defaultText: text, + type, + isRequired, + configJson, + noteBefore: notes.noteBefore, + noteAfter: notes.noteAfter, + }; + if (type === 'glass_scale_question') { + const glassSymptoms = readGlassSymptomsFromForm('aq_cfg'); + if (glassSymptoms.length && !validateGlassSymptoms(glassSymptoms)) return; + postBody.glassSymptoms = glassSymptoms; + } + if (type === 'slider_question' || type === 'value_spinner') { + postBody.scoreRules = readValueScoreRulesFromForm('aq_cfg'); + } + if (!validateStableKey(questionKey, 'Question key')) return; + if (!text) { showToast('German text is required', 'error'); return; } + if (type === 'string_spinner' && !validateStringSpinnerConfig('aq_cfg')) return; + if (OPTION_TYPES.has(type) && pendingOptions.length === 0) { + showToast('Add at least one answer option', 'error'); + return; + } + + btn.disabled = true; + btn.textContent = 'Adding...'; + try { + const qData = await apiPost('questions.php', postBody); + if (!qData.success) throw new Error(qData.error || 'Failed'); + + const newQ = qData.question; + newQ.answerOptions = []; + + for (const opt of pendingOptions) { + const oData = await apiPost('answer_options.php', { + questionID: newQ.questionID, + optionKey: opt.optionKey, + defaultText: opt.text, + points: opt.points, + nextQuestionId: opt.nextQuestionId || '', + }); + if (oData.success) newQ.answerOptions.push(oData.answerOption); + } + + questions.push(newQ); + renderQuestions(); + notifyStructureBump(qData); + showToast('Question added', 'success'); + + document.getElementById('aq_text').value = ''; + pendingOptions.length = 0; + renderPendingOptions(); + updateTypeUI(); + document.getElementById('aq_text').focus(); + } catch (e) { + showToast(e.message, 'error'); + } finally { + if (document.getElementById('aq_submit')) { + btn.disabled = false; + btn.textContent = 'Add Question'; + } + } + }); + + document.getElementById('aq_text').addEventListener('keydown', (e) => { + if (e.key === 'Escape') hideAddQuestionForm(); + }); + document.getElementById('aq_cancel').addEventListener('click', hideAddQuestionForm); +} + +function hideAddQuestionForm() { + const wrapper = document.getElementById('addQuestionFormWrapper'); + if (wrapper) { wrapper.style.display = 'none'; wrapper.innerHTML = ''; } +} + +// ── Questions list ─────────────────────────────────────────────────────── + +function renderRetiredQuestions() { + const section = document.getElementById('retiredQuestionsSection'); + const list = document.getElementById('retiredQuestionList'); + const countEl = document.getElementById('retiredCount'); + if (!section || !list) return; + if (!retiredQuestions.length) { + section.style.display = 'none'; + list.innerHTML = ''; + if (countEl) countEl.textContent = '0'; + return; + } + section.style.display = ''; + if (countEl) countEl.textContent = String(retiredQuestions.length); + list.innerHTML = retiredQuestions.map((q, idx) => ` +
      • +
        + ${idx + 1}. ${esc(questionKeyOf(q) || '?')} · ${esc((q.defaultText || '').slice(0, 48))} + Retired + ${q.retiredInRevision ? `rev ${q.retiredInRevision}` : ''} +
        +
      • + `).join(''); +} + +function renderQuestions() { + const list = document.getElementById('questionList'); + if (!list) return; + const editable = canEdit(); + + // update tab label + const tabBtn = document.querySelector('#editorTabs button[data-tab="questions"]'); + if (tabBtn) tabBtn.textContent = `Questions (${questions.length})`; + renderRetiredQuestions(); + + if (!questions.length) { + list.innerHTML = `
      • No questions yet

        Add your first question above.

      • `; + return; + } + + list.innerHTML = questions.map((q, idx) => ` +
      • +
        + ${editable ? '' : ''} + + ${idx + 1}. ${esc(questionKeyOf(q) || '?')} · ${esc((q.defaultText || '').slice(0, 48))}${(q.defaultText || '').length > 48 ? '…' : ''} + ${esc(layoutLabel(q.type))} + ${!questionKeyOf(q) ? 'No key' : ''} + ${q.isRequired ? 'Required' : ''} + ${(() => { + const c = parseConfig(q); + const parts = []; + if (c.nextQuestionId) parts.push(`→ ${esc(c.nextQuestionId)}`); + if (c.otherNextQuestionId) parts.push(`Other→${esc(c.otherNextQuestionId)}`); + return parts.length + ? `${esc(parts.join(' · '))}` : ''; + })()} +
        +
        +
      • + `).join(''); + + list.querySelectorAll('.question-header').forEach((hdr, idx) => { + hdr.addEventListener('click', (e) => { + if (e.target.closest('.drag-handle')) return; + const item = hdr.closest('.question-item'); + const wasOpen = item.classList.contains('open'); + item.classList.toggle('open'); + if (!wasOpen) renderQuestionBody(questions[idx]); + }); + }); +} + +// ── Question body (expanded) ───────────────────────────────────────────── + +function renderQuestionBody(q) { + const body = document.getElementById(`qbody-${q.questionID}`); + if (!body) return; + const editable = canEdit(); + const options = q.answerOptions || []; + const config = parseConfig(q); + const layout = q.type || 'radio_question'; + const qKey = questionKeyOf(q); + const localId = q.localId || q.questionID.split('__').pop(); + + body.innerHTML = ` + ${editable ? ` +
        +
        +

        Local ID: ${esc(localId)}

        +
        +
        + + +
        +
        + + +
        +
        + + ${layoutSelectHTML(layout, `eq_type_${q.questionID}`)} +
        +
        + + ${notesSectionHTML(config, `eq_cfg_${q.questionID}`, qKey)} +
        + ${configFormHTML(layout, config, `eq_cfg_${q.questionID}`, { + glassSymptoms: glassSymptomsRowsFromQuestion(q), + scoreRules: q.scoreRules || [], + })} +
        +
        + + +
        +
        +
        + ` : ` +
        +

        Key: ${esc(qKey || '—')} · ID: ${esc(localId)}

        +

        German: ${esc(q.defaultText)}

        +

        Layout: ${esc(layoutLabel(layout))}

        + ${config.nextQuestionId || config.otherNextQuestionId + ? `

        Branches: + ${config.nextQuestionId ? `→ ${esc(config.nextQuestionId)}` : ''} + ${config.otherNextQuestionId ? `${config.nextQuestionId ? ' · ' : ''}Other (${esc(config.otherOptionKey || 'other_option')}) → ${esc(config.otherNextQuestionId)}` : ''} +

        ` + : ''} + ${Object.keys(config).length ? `

        Config: ${esc(JSON.stringify(config))}

        ` : ''} + ${layout === 'glass_scale_question' && glassSymptomsRowsFromQuestion(q).length ? ` +

        Symptoms:

        +
          + ${glassSymptomsRowsFromQuestion(q).map(r => + `
        • ${esc(r.key)} · ${esc(r.labelDe || '—')}
        • ` + ).join('')} +
        ` : ''} +
        + `} + + ${OPTION_TYPES.has(layout) ? ` +

        Answer Options (${options.length})

        +
          + ${options.map(o => optionItemHTML(q, o, editable)).join('') + || '
        • No answer options
        • '} +
        + ${editable ? ` + + + ` : ''} + ` : ''} + `; + + if (!editable) return; + + const cfgPrefix = `eq_cfg_${q.questionID}`; + + body.querySelector('.save-q-btn').addEventListener('click', () => { + const questionKey = document.getElementById(`eq_key_${q.questionID}`).value.trim(); + const text = document.getElementById(`eq_text_${q.questionID}`).value.trim(); + const type = document.getElementById(`eq_type_${q.questionID}`).value; + const isReq = document.getElementById(`eq_req_${q.questionID}`).checked ? 1 : 0; + const notes = readNotesFromForm(cfgPrefix); + const cj = readConfigFromForm(type, cfgPrefix); + const payload = { + questionKey, + defaultText: text, + type, + isRequired: isReq, + configJson: cj, + noteBefore: notes.noteBefore, + noteAfter: notes.noteAfter, + }; + if (type === 'glass_scale_question') { + const glassSymptoms = readGlassSymptomsFromForm(cfgPrefix); + if (glassSymptoms.length && !validateGlassSymptoms(glassSymptoms)) return; + payload.glassSymptoms = glassSymptoms; + } + if (type === 'slider_question' || type === 'value_spinner') { + payload.scoreRules = readValueScoreRulesFromForm(cfgPrefix); + } + if (!validateStableKey(questionKey, 'Question key')) return; + if (!text) { showToast('German text is required', 'error'); return; } + if (type === 'string_spinner' && !validateStringSpinnerConfig(cfgPrefix)) return; + updateQuestion(q.questionID, payload); + }); + + document.getElementById(`eq_key_${q.questionID}`).addEventListener('input', () => { + const nb = document.getElementById(`${cfgPrefix}_noteBefore`)?.value ?? ''; + const na = document.getElementById(`${cfgPrefix}_noteAfter`)?.value ?? ''; + const key = document.getElementById(`eq_key_${q.questionID}`).value.trim(); + const notesBlock = body.querySelector('.notes-section'); + if (notesBlock) { + notesBlock.outerHTML = notesSectionHTML({ noteBefore: nb, noteAfter: na }, cfgPrefix, key); + } + }); + + document.getElementById(`eq_type_${q.questionID}`).addEventListener('change', () => { + const newLayout = document.getElementById(`eq_type_${q.questionID}`).value; + const opts = newLayout === 'glass_scale_question' + ? { glassSymptoms: glassSymptomsRowsFromQuestion(q) } + : (newLayout === 'slider_question' || newLayout === 'value_spinner') + ? { scoreRules: q.scoreRules || [] } + : {}; + document.getElementById(`eq_config_${q.questionID}`).innerHTML = + configFormHTML(newLayout, {}, cfgPrefix, opts); + wireStringSpinnerOtherToggle(cfgPrefix); + if (newLayout === 'glass_scale_question') wireGlassSymptomsSection(cfgPrefix); + if (newLayout === 'slider_question' || newLayout === 'value_spinner') wireValueScoreRules(cfgPrefix); + }); + + wireStringSpinnerOtherToggle(cfgPrefix); + if (layout === 'glass_scale_question') wireGlassSymptomsSection(cfgPrefix); + if (layout === 'slider_question' || layout === 'value_spinner') wireValueScoreRules(cfgPrefix); + + body.querySelector('.del-q-btn').addEventListener('click', () => deleteQuestion(q)); + + body.querySelectorAll('.edit-opt-btn').forEach(btn => { + btn.addEventListener('click', () => { + const opt = options.find(o => o.answerOptionID === btn.dataset.id); + if (opt) showEditOptionForm(q, opt); + }); + }); + body.querySelectorAll('.del-opt-btn').forEach(btn => { + btn.addEventListener('click', () => deleteOption(q, btn.dataset.id)); + }); + + const addOptBtn = document.getElementById(`addOpt-${q.questionID}`); + if (addOptBtn) addOptBtn.addEventListener('click', () => showAddOptionForm(q)); + + if (OPTION_TYPES.has(layout)) initOptionDrag(q); +} + +function optionItemHTML(q, o, editable) { + const ok = optionKeyOf(o); + const label = optionGermanLabel(o) || o.defaultText; + return ` +
      • + ${editable ? '' : ''} + ${esc(ok || '?')} + ${esc(label)} + ${o.points} pts + ${o.nextQuestionId ? `→ ${esc(o.nextQuestionId)}` : ''} + ${editable ? ` + + + ` : ''} +
      • + `; +} + +// ── Add option form ────────────────────────────────────────────────────── + +function showAddOptionForm(q) { + const wrapper = document.getElementById(`addOptForm-${q.questionID}`); + const toggleBtn = document.getElementById(`addOpt-${q.questionID}`); + if (!wrapper) return; + toggleBtn.style.display = 'none'; + wrapper.style.display = ''; + + const qIds = questionLocalIds(); + + wrapper.innerHTML = ` +
        +
        +
        + + +
        +
        + + +
        +
        + + +
        +
        + + +
        +
        +
        + + +
        +
        + `; + + document.getElementById(`ao_text_${q.questionID}`).focus(); + + document.getElementById(`ao_submit_${q.questionID}`).addEventListener('click', async () => { + const optionKey = document.getElementById(`ao_key_${q.questionID}`).value.trim(); + const text = document.getElementById(`ao_text_${q.questionID}`).value.trim(); + const points = parseInt(document.getElementById(`ao_pts_${q.questionID}`).value || '0', 10); + const nextQ = document.getElementById(`ao_next_${q.questionID}`).value; + if (!validateStableKey(optionKey, 'Option key')) return; + if (!text) { showToast('German label is required', 'error'); return; } + + const btn = document.getElementById(`ao_submit_${q.questionID}`); + btn.disabled = true; + btn.textContent = 'Adding...'; + try { + const data = await apiPost('answer_options.php', { + questionID: q.questionID, + optionKey, + defaultText: text, + points, + nextQuestionId: nextQ, + }); + if (data.success) { + if (!q.answerOptions) q.answerOptions = []; + q.answerOptions.push(data.answerOption); + renderQuestionBody(q); + showToast('Option added', 'success'); + } + } catch (e) { + showToast(e.message, 'error'); + btn.disabled = false; + btn.textContent = 'Add Option'; + } + }); + + document.getElementById(`ao_text_${q.questionID}`).addEventListener('keydown', (e) => { + if (e.key === 'Enter') document.getElementById(`ao_submit_${q.questionID}`).click(); + if (e.key === 'Escape') hideAddOptionForm(q); + }); + document.getElementById(`ao_cancel_${q.questionID}`).addEventListener('click', () => hideAddOptionForm(q)); +} + +function hideAddOptionForm(q) { + const wrapper = document.getElementById(`addOptForm-${q.questionID}`); + const toggleBtn = document.getElementById(`addOpt-${q.questionID}`); + if (wrapper) { wrapper.style.display = 'none'; wrapper.innerHTML = ''; } + if (toggleBtn) toggleBtn.style.display = ''; +} + +// ── Edit option form ───────────────────────────────────────────────────── + +function showEditOptionForm(q, opt) { + const li = document.querySelector(`#opts-${q.questionID} .option-item[data-id="${opt.answerOptionID}"]`); + if (!li) return; + const qIds = questionLocalIds(); + + li.innerHTML = ` +
        +
        +
        + + +
        +
        + + +
        +
        + +
        +
        + + +
        +
        +
        + + +
        +
        + `; + li.draggable = false; + + document.getElementById(`eo_text_${opt.answerOptionID}`).focus(); + + document.getElementById(`eo_save_${opt.answerOptionID}`).addEventListener('click', async () => { + const optionKey = document.getElementById(`eo_key_${opt.answerOptionID}`).value.trim(); + const text = document.getElementById(`eo_text_${opt.answerOptionID}`).value.trim(); + const points = parseInt(document.getElementById(`eo_pts_${opt.answerOptionID}`).value || '0', 10); + const nextQ = document.getElementById(`eo_next_${opt.answerOptionID}`).value; + if (!validateStableKey(optionKey, 'Option key')) return; + if (!text) { showToast('German label is required', 'error'); return; } + await updateOption(q, opt.answerOptionID, { optionKey, defaultText: text, points, nextQuestionId: nextQ }); + }); + + document.getElementById(`eo_text_${opt.answerOptionID}`).addEventListener('keydown', (e) => { + if (e.key === 'Enter') document.getElementById(`eo_save_${opt.answerOptionID}`).click(); + if (e.key === 'Escape') renderQuestionBody(q); + }); + document.getElementById(`eo_cancel_${opt.answerOptionID}`).addEventListener('click', () => renderQuestionBody(q)); +} + +// ── Question CRUD ──────────────────────────────────────────────────────── + +async function updateQuestion(questionID, fields) { + try { + const data = await apiPut('questions.php', { questionID, ...fields }); + if (data.success) { + const idx = questions.findIndex(q => q.questionID === questionID); + if (idx >= 0) { + questions[idx] = { ...questions[idx], ...data.question, + answerOptions: questions[idx].answerOptions }; + } + renderQuestions(); + showToast('Question updated', 'success'); + } + } catch (e) { + showToast(e.message, 'error'); + } +} + +async function deleteQuestion(q) { + if (q.retired) { + showToast('This question is already retired', 'error'); + return; + } + const msg = 'Remove this question? If client data exists it will be retired (not deleted) so upload history stays intact.'; + if (!(await confirmAction({ + title: 'Remove question', + message: msg, + confirmLabel: 'Remove', + variant: 'danger', + }))) return; + try { + const data = await apiDelete('questions.php', { questionID: q.questionID }); + questions = questions.filter(x => x.questionID !== q.questionID); + if (data.retired) { + retiredQuestions.push({ ...q, retired: true, retiredAt: Date.now() / 1000 | 0 }); + showToast('Question retired', 'success'); + } else { + showToast('Question deleted', 'success'); + } + notifyStructureBump(data); + renderQuestions(); + } catch (e) { + showToast(e.message, 'error'); + } +} + +// ── Answer option CRUD ─────────────────────────────────────────────────── + +async function updateOption(q, answerOptionID, fields) { + try { + const data = await apiPut('answer_options.php', { answerOptionID, ...fields }); + if (data.success) { + const idx = q.answerOptions.findIndex(o => o.answerOptionID === answerOptionID); + if (idx >= 0) q.answerOptions[idx] = { ...q.answerOptions[idx], ...data.answerOption }; + renderQuestionBody(q); + showToast('Option updated', 'success'); + } + } catch (e) { + showToast(e.message, 'error'); + } +} + +async function deleteOption(q, answerOptionID) { + if (!(await confirmAction({ + title: 'Remove answer option', + message: 'Remove this answer option?', + confirmLabel: 'Remove', + variant: 'danger', + }))) return; + try { + const data = await apiDelete('answer_options.php', { answerOptionID }); + if (!data.retired) { + q.answerOptions = (q.answerOptions || []).filter(o => o.answerOptionID !== answerOptionID); + } + renderQuestionBody(q); + notifyStructureBump(data); + showToast(data.retired ? 'Option retired' : 'Option deleted', 'success'); + } catch (e) { + showToast(e.message, 'error'); + } +} + +// ── Translations tab ───────────────────────────────────────────────────── + +let transData = null; +let editorTransLang = ''; +let editorTransSaveTimers = {}; + +async function loadTranslationsTab() { + const container = document.getElementById('translationsContent'); + if (!container) return; + container.innerHTML = '
        '; + + try { + try { + await apiPut('translations.php', { type: 'language', languageCode: SOURCE_LANG, name: 'German' }); + } catch (_) { /* already exists */ } + const data = await apiGet(`translations.php?questionnaireID=${encodeURIComponent(questionnaire.questionnaireID)}`); + transData = data; + transData.entries = (transData.entries || []).map(normalizeEntry); + renderTranslationsTab(); + } catch (e) { + container.innerHTML = `

        ${esc(e.message)}

        `; + } +} + +function renderTranslationsTab() { + const container = document.getElementById('translationsContent'); + if (!container || !transData) return; + const editable = canEdit(); + const languages = transData.languages || []; + const qnEntries = transData.entries || []; + const targets = targetLanguages(languages); + + if (!qnEntries.length) { + container.innerHTML = ` + ${editable ? languageManagerHTML(languages) : ''} +

        No translatable content yet. Add questions with German text first.

        + `; + if (editable) bindLanguageManager(languages); + return; + } + + if (!editorTransLang || !targets.some(l => l.languageCode === editorTransLang)) { + editorTransLang = targets[0]?.languageCode || ''; + } + + const langSelectHtml = targets.length + ? `` + : `

        Add a language below (e.g. English) to translate into.

        `; + + container.innerHTML = ` + ${editable ? languageManagerHTML(languages) : ''} + ${langSelectHtml} + ${targets.length && editorTransLang ? renderEditorTransList(qnEntries, editable) : ''} + `; + + if (editable) bindLanguageManager(languages); + + document.getElementById('editorTransLangSelect')?.addEventListener('change', (e) => { + editorTransLang = e.target.value; + renderTranslationsTab(); + }); + + if (targets.length && editorTransLang) { + bindEditorTransEditing(qnEntries, editable); + bindEditorTransFiltering(qnEntries); + } +} + +function renderEditorTransList(qnEntries, editable) { + const targets = targetLanguages(transData.languages || []); + const langLabel = targets.find(l => l.languageCode === editorTransLang)?.name || editorTransLang.toUpperCase(); + const sourceLabel = sourceLanguageLabel(transData.languages || []); + const missingCount = qnEntries.filter(e => !translationFor(e, editorTransLang).trim()).length; + const pct = qnEntries.length ? Math.round(((qnEntries.length - missingCount) / qnEntries.length) * 100) : 0; + + const qnGroups = buildTranslationGroups(qnEntries, editorTransLang, 0); + const bodyHtml = renderCollapsibleGroupedTranslationsHTML(qnGroups, editorTransLang, sourceLabel, editable, { + openGroupsWithMissing: true, + }); + + return ` +
        + + +
        +
        + ${translationListHeaderHTML(langLabel, sourceLabel)} +
        +
        ${bodyHtml}
        +
        +
        +
        + ${missingCount ? `${missingCount} missing · ` : ''}${qnEntries.length} keys + + + ${pct}% in ${escHtml(editorTransLang.toUpperCase())} + +
        + `; +} + +function bindEditorTransEditing(entries, editable) { + if (!editable) return; + editorTransSaveTimers = {}; + document.querySelectorAll('#transGroupedRoot .trans-cell-input').forEach(inp => { + inp.addEventListener('input', () => { + const idx = parseInt(inp.dataset.idx, 10); + const entry = entries[idx]; + if (!entry) return; + const isGerman = inp.dataset.field === 'german'; + if (!entry.translations || typeof entry.translations !== 'object') entry.translations = {}; + if (isGerman) { + entry.germanText = inp.value; + entry.translations[SOURCE_LANG] = inp.value; + } else { + entry.translations[editorTransLang] = inp.value; + const missing = !inp.value.trim(); + inp.classList.toggle('trans-input-missing', missing); + const row = inp.closest('.trans-list-item'); + if (row) { + row.classList.toggle('trans-list-item-missing', missing); + row.dataset.missing = missing ? '1' : '0'; + } + } + const timerKey = isGerman ? `${idx}_de` : `${idx}_${editorTransLang}`; + clearTimeout(editorTransSaveTimers[timerKey]); + editorTransSaveTimers[timerKey] = setTimeout(() => saveEditorTranslation(entry, inp, isGerman), 500); + }); + }); +} + +async function saveEditorTranslation(entry, inp, isGerman) { + try { + await apiPut('translations.php', { + type: entry.type, + id: entry.entityId, + languageCode: isGerman ? SOURCE_LANG : editorTransLang, + text: inp.value, + }); + inp.classList.add('save-flash'); + setTimeout(() => inp.classList.remove('save-flash'), 400); + } catch (e) { + showToast(e.message, 'error'); + } +} + +function bindEditorTransFiltering(entries) { + const filterInput = document.getElementById('transFilter'); + const typeFilter = document.getElementById('transTypeFilter'); + const root = document.getElementById('transGroupedRoot'); + if (!filterInput || !root) return; + + function applyFilter() { + const text = filterInput.value.toLowerCase(); + const type = typeFilter?.value || ''; + let visible = 0; + let visibleMissing = 0; + root.querySelectorAll('.trans-list-item').forEach(row => { + const key = row.dataset.key?.toLowerCase() || ''; + const label = row.dataset.label?.toLowerCase() || ''; + const rowType = row.dataset.type || ''; + const german = row.querySelector('.trans-source-text')?.textContent?.toLowerCase() + || row.querySelector('[data-field="german"]')?.value?.toLowerCase() || ''; + const val = row.querySelector('[data-field="translation"]')?.value?.toLowerCase() || ''; + const show = (!type || rowType === type) && + (!text || key.includes(text) || label.includes(text) || german.includes(text) || val.includes(text)); + row.classList.toggle('trans-row-hidden', !show); + if (show) { + visible++; + if (row.dataset.missing === '1') visibleMissing++; + } + }); + root.querySelectorAll('.trans-group-details').forEach(group => { + const anyVisible = [...group.querySelectorAll('.trans-list-item')].some(r => !r.classList.contains('trans-row-hidden')); + group.classList.toggle('trans-group-hidden', !anyVisible); + }); + root.querySelectorAll('.trans-scope-block').forEach(block => { + const anyVisible = [...block.querySelectorAll('.trans-list-item')].some(r => !r.classList.contains('trans-row-hidden')); + block.classList.toggle('trans-qn-hidden', !anyVisible); + }); + const stats = document.getElementById('transStats'); + if (stats) { + stats.textContent = text || type + ? `Showing ${visible} (${visibleMissing} missing)` + : `${[...root.querySelectorAll('.trans-list-item')].filter(r => r.dataset.missing === '1').length} missing · ${entries.length} keys`; + } + } + + filterInput.addEventListener('input', applyFilter); + typeFilter?.addEventListener('change', applyFilter); +} + +function languageManagerHTML(languages) { + const others = targetLanguages(languages); + return ` +
        +

        + German source text is edited under Questions. Add other languages here. +

        +
        + + DE + German (source) + + ${others.map(l => ` + + ${esc(l.languageCode.toUpperCase())} + ${l.name ? `${esc(l.name)}` : ''} + + + `).join('')} +
        +
        + + + +
        +
        + `; +} + +function bindLanguageManager(languages) { + document.getElementById('addLangBtn')?.addEventListener('click', async () => { + const code = document.getElementById('newLangCode').value.trim().toLowerCase(); + const name = document.getElementById('newLangName').value.trim(); + if (!code) { showToast('Language code is required', 'error'); return; } + if (code === SOURCE_LANG) { + showToast('German (de) is always available as the source language', 'error'); + return; + } + if (languages.some(l => l.languageCode === code)) { + showToast('Language already exists', 'error'); + return; + } + try { + await apiPut('translations.php', { type: 'language', languageCode: code, name }); + showToast(`Language "${code}" added`, 'success'); + editorTransLang = code; + loadTranslationsTab(); + } catch (e) { + showToast(e.message, 'error'); + } + }); + + document.querySelectorAll('.lang-chip-remove').forEach(btn => { + btn.addEventListener('click', async () => { + const lc = btn.dataset.lc; + if (!(await confirmAction({ + title: 'Remove language', + message: `Remove language "${lc}" and ALL its translations?`, + confirmLabel: 'Remove', + variant: 'danger', + }))) return; + try { + await apiDelete('translations.php', { type: 'language', languageCode: lc }); + showToast(`Language "${lc}" removed`, 'success'); + loadTranslationsTab(); + } catch (e) { + showToast(e.message, 'error'); + } + }); + }); +} + +// ── Drag & drop (questions) ────────────────────────────────────────────── + +function initDragReorder() { + const list = document.getElementById('questionList'); + if (!list) return; + let dragItem = null; + + list.addEventListener('dragstart', (e) => { + const item = e.target.closest('.question-item'); + if (!item || !e.target.closest('.drag-handle')) { e.preventDefault(); return; } + dragItem = item; + item.classList.add('dragging'); + e.dataTransfer.effectAllowed = 'move'; + }); + + list.addEventListener('dragover', (e) => { + e.preventDefault(); + const afterEl = getDragAfterElement(list, e.clientY, '.question-item'); + if (afterEl) list.insertBefore(dragItem, afterEl); + else list.appendChild(dragItem); + }); + + list.addEventListener('dragend', async () => { + if (!dragItem) return; + dragItem.classList.remove('dragging'); + const newOrder = [...list.querySelectorAll('.question-item')].map(el => el.dataset.id); + dragItem = null; + + const map = {}; + questions.forEach(q => map[q.questionID] = q); + questions = newOrder.map(id => map[id]).filter(Boolean); + + try { + await apiPatch('questions.php', { + questionnaireID: questionnaire.questionnaireID, + order: newOrder + }); + showToast('Order saved', 'success'); + } catch (e) { + showToast(e.message, 'error'); + } + }); +} + +function initOptionDrag(q) { + const list = document.getElementById(`opts-${q.questionID}`); + if (!list) return; + let dragItem = null; + + list.addEventListener('dragstart', (e) => { + const item = e.target.closest('.option-item'); + if (!item || !e.target.closest('.drag-handle')) { e.preventDefault(); return; } + dragItem = item; + item.classList.add('dragging'); + e.dataTransfer.effectAllowed = 'move'; + }); + + list.addEventListener('dragover', (e) => { + e.preventDefault(); + const afterEl = getDragAfterElement(list, e.clientY, '.option-item'); + if (afterEl) list.insertBefore(dragItem, afterEl); + else list.appendChild(dragItem); + }); + + list.addEventListener('dragend', async () => { + if (!dragItem) return; + dragItem.classList.remove('dragging'); + const newOrder = [...list.querySelectorAll('.option-item')].map(el => el.dataset.id); + dragItem = null; + + const map = {}; + (q.answerOptions || []).forEach(o => map[o.answerOptionID] = o); + q.answerOptions = newOrder.map(id => map[id]).filter(Boolean); + + try { + await apiPatch('answer_options.php', { + questionID: q.questionID, + order: newOrder + }); + showToast('Option order saved', 'success'); + } catch (e) { + showToast(e.message, 'error'); + } + }); +} + +function getDragAfterElement(container, y, selector) { + const elements = [...container.querySelectorAll(`${selector}:not(.dragging)`)]; + return elements.reduce((closest, child) => { + const box = child.getBoundingClientRect(); + const offset = y - box.top - box.height / 2; + if (offset < 0 && offset > closest.offset) return { offset, element: child }; + return closest; + }, { offset: Number.NEGATIVE_INFINITY }).element; +} + +function esc(s) { + const d = document.createElement('div'); + d.textContent = s ?? ''; + return d.innerHTML; +} diff --git a/website/js/pages/export.js b/website/js/pages/export.js new file mode 100644 index 0000000..314f8d9 --- /dev/null +++ b/website/js/pages/export.js @@ -0,0 +1,233 @@ +import { apiGet, apiDownloadFetch, apiUrl } from '../api.js'; +import { canEdit, pageHeaderHTML, showToast } from '../app.js'; +let questionnairesList = []; +let filterSearch = ''; + +export async function exportPage() { + const app = document.getElementById('app'); + app.innerHTML = ` + ${pageHeaderHTML('Export Data')} +
        + `; + + try { + const data = await apiGet('questionnaires.php'); + questionnairesList = data.questionnaires || []; + renderExport(); + } catch (e) { + showToast(e.message, 'error'); + document.getElementById('exportContent').innerHTML = `

        ${esc(e.message)}

        `; + } +} + +function renderExport() { + const container = document.getElementById('exportContent'); + + if (!questionnairesList.length) { + container.innerHTML = ` +
        +

        No questionnaires

        +

        Create questionnaires first to export data.

        +
        + `; + return; + } + + const bundleBlock = canEdit() ? ` +
        +

        Export questionnaires (JSON)

        +

        + Download all questionnaires with questions, answer options, and every translation. + Use Import questionnaires on the dashboard to restore this file on another server. +

        + +
        + ` : ''; + + container.innerHTML = ` + ${bundleBlock} +
        +

        + Select a questionnaire and click Export to download a CSV file with all client responses (filtered by your role's visibility). +

        +
        + + +
        +
        + + + + + + + + + + + + +
        QuestionnaireVersionStateQuestionsCompletedActions
        +
        +
        + `; + + document.getElementById('exportListSearch').addEventListener('input', (e) => { + filterSearch = e.target.value; + renderExportTableBody(); + }); + + renderExportTableBody(); + wireExportActions(); +} + +function exportRowSearchText(q) { + return [q.name, q.questionnaireID, q.version, q.state].filter(Boolean).join(' '); +} + +function renderExportTableBody() { + const body = document.getElementById('exportTableBody'); + const countEl = document.getElementById('exportListCount'); + if (!body) return; + + const q = filterSearch.trim().toLowerCase(); + const filtered = q + ? questionnairesList.filter(item => exportRowSearchText(item).toLowerCase().includes(q)) + : questionnairesList; + + if (!filtered.length) { + body.innerHTML = ` + + + No questionnaires match + + `; + if (countEl) { + countEl.textContent = q + ? `0 of ${questionnairesList.length} questionnaires` + : ''; + } + return; + } + + body.innerHTML = filtered.map(item => exportRowHTML(item)).join(''); + + if (countEl) { + countEl.textContent = q + ? `${filtered.length} of ${questionnairesList.length} questionnaires` + : `${questionnairesList.length} questionnaire${questionnairesList.length === 1 ? '' : 's'}`; + } + + body.querySelectorAll('.export-btn').forEach(btn => { + btn.addEventListener('click', onExportCsvClick); + }); + body.querySelectorAll('.export-btn-all').forEach(btn => { + btn.addEventListener('click', onExportAllVersionsClick); + }); +} + +function exportRowHTML(q) { + return ` + + ${esc(q.name)} + ${esc(q.version || '—')} + ${esc(q.state || 'draft')} + ${q.questionCount || 0} + ${q.completedCount ?? 0} + + + + Results + + `; +} + +function wireExportActions() { + document.getElementById('exportBundleBtn')?.addEventListener('click', async () => { + const btn = document.getElementById('exportBundleBtn'); + btn.disabled = true; + const prev = btn.textContent; + btn.textContent = 'Preparing…'; + try { + const res = await apiDownloadFetch(apiUrl('export?bundle=1')); + if (!res.ok) { + const err = await res.json().catch(() => ({})); + throw new Error(err.error?.message || err.error || 'Export failed'); + } + const blob = await res.blob(); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + const stamp = new Date().toISOString().slice(0, 10); + a.download = `questionnaires_bundle_${stamp}.json`; + a.click(); + URL.revokeObjectURL(url); + showToast('Questionnaire bundle downloaded', 'success'); + } catch (e) { + showToast(e.message, 'error'); + } finally { + btn.disabled = false; + btn.textContent = prev; + } + }); +} + +async function downloadExportCsv(btn, url, filename) { + const res = await apiDownloadFetch(url); + if (!res.ok) { + const err = await res.json().catch(() => ({ error: 'Download failed' })); + throw new Error(err.error?.message || err.error || 'Download failed'); + } + const blob = await res.blob(); + const blobUrl = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = blobUrl; + a.download = filename; + a.click(); + URL.revokeObjectURL(blobUrl); +} + +async function onExportCsvClick(ev) { + const btn = ev.currentTarget; + btn.disabled = true; + const prev = btn.textContent; + btn.textContent = 'Downloading…'; + try { + const url = apiUrl(`export?questionnaireID=${encodeURIComponent(btn.dataset.id)}`); + await downloadExportCsv(btn, url, `${btn.dataset.name}_v${btn.dataset.version}.csv`); + showToast('Download started', 'success'); + } catch (e) { + showToast(e.message, 'error'); + } finally { + btn.disabled = false; + btn.textContent = prev; + } +} + +async function onExportAllVersionsClick(ev) { + const btn = ev.currentTarget; + btn.disabled = true; + const prev = btn.textContent; + btn.textContent = 'Downloading…'; + try { + const url = apiUrl(`export?questionnaireID=${encodeURIComponent(btn.dataset.id)}&allVersions=1`); + const stamp = new Date().toISOString().slice(0, 10); + await downloadExportCsv(btn, url, `${btn.dataset.name}_all_versions_${stamp}.csv`); + showToast('All versions download started', 'success'); + } catch (e) { + showToast(e.message, 'error'); + } finally { + btn.disabled = false; + btn.textContent = prev; + } +} + +function esc(s) { + const d = document.createElement('div'); + d.textContent = s ?? ''; + return d.innerHTML; +} diff --git a/website/js/pages/home.js b/website/js/pages/home.js new file mode 100644 index 0000000..8d30e2a --- /dev/null +++ b/website/js/pages/home.js @@ -0,0 +1,108 @@ +import { apiGet } from '../api.js'; +import { getRole, pageHeaderHTML, showToast } from '../app.js'; + +export async function homeDashboardPage() { + const app = document.getElementById('app'); + const role = getRole(); + const isAdmin = role === 'admin'; + + app.innerHTML = ` + ${pageHeaderHTML('Overview', '', { + subtitle: `Ressourcenbarometer administration for your ${role === 'admin' ? 'organization' : 'supervised counselors'}`, + })} +
        + `; + + try { + const ov = await apiGet('analytics.php?overview=1'); + renderHomeDashboard(ov, isAdmin); + } catch (e) { + showToast(e.message, 'error'); + document.getElementById('homeDashboardContent').innerHTML = + `

        ${esc(e.message)}

        `; + } +} + +function renderHomeDashboard(ov, isAdmin) { + const el = document.getElementById('homeDashboardContent'); + + const qPreview = (ov.questionnaires || []).slice(0, 6).map(q => ` + + ${esc(q.name)} + ${q.completedCount ?? 0} / ${q.eligibleCount ?? 0} + ${q.completionRatio ?? 0}% + + `).join(''); + + const links = [ + { href: '#/questionnaires', label: 'Session measures', desc: 'Edit instruments, order, and categories' }, + { href: '#/clients', label: 'Clients', desc: 'Client codes, counselors, response history' }, + { href: '#/coaches', label: 'Counselor activity', desc: 'Uploads and workload per counselor' }, + { href: '#/insights', label: 'Insights', desc: 'Charts, completion rates, follow-up' }, + { href: '#/export', label: 'Export data', desc: 'Download CSV and bundles' }, + { href: '#/assignments', label: 'Assign clients', desc: 'Move clients between counselors' }, + { href: '#/users', label: 'Users', desc: 'Admins, supervisors, and counselors' }, + { href: '#/translations', label: 'Translations', desc: 'German source and other languages' }, + ]; + if (isAdmin) { + links.push({ href: '#/dev', label: 'Admin settings', desc: 'Dev import, exports, database tools' }); + } + + el.innerHTML = ` + + +
        +

        Quick links

        + +
        + +
        +
        +

        Measure completion

        + Full report +
        + ${(ov.questionnaires || []).length ? ` +
        + + + + + ${qPreview}${(ov.questionnaires.length > 6) + ? `` + : ''} +
        Session measureCompletedRate
        + ${ov.questionnaires.length - 6} more — see Insights
        +
        + ` : '

        No active session measures.

        '} +
        + `; +} + +function esc(s) { + const d = document.createElement('div'); + d.textContent = s ?? ''; + return d.innerHTML; +} diff --git a/website/js/pages/insights.js b/website/js/pages/insights.js new file mode 100644 index 0000000..790699c --- /dev/null +++ b/website/js/pages/insights.js @@ -0,0 +1,376 @@ +import { apiGet, apiPut } from '../api.js'; +import { pageHeaderHTML, showToast } from '../app.js'; +import { clientTableActionsHTML, confirmAndPatchClientArchive } from '../client-archive.js'; + +let overview = null; +let staleClients = []; +let followupSearch = ''; + +export async function insightsPage() { + const app = document.getElementById('app'); + app.innerHTML = ` + ${pageHeaderHTML('Insights')} +
        + `; + + try { + const [ov, stale] = await Promise.all([ + apiGet('analytics.php?overview=1'), + apiGet('analytics.php?staleClients=1'), + ]); + overview = ov; + staleClients = stale.clients || []; + renderInsights(); + } catch (e) { + showToast(e.message, 'error'); + document.getElementById('insightsContent').innerHTML = + `

        ${esc(e.message)}

        `; + } +} + +function renderInsights() { + const el = document.getElementById('insightsContent'); + const ov = overview || {}; + + const kpiCards = ` +
        +
        +
        ${ov.clientCount ?? 0}
        +
        Clients
        +
        +
        +
        ${ov.clientsWithCompletions ?? 0}
        +
        With completions
        +
        +
        +
        ${ov.submissionsLast7d ?? 0}
        +
        Uploads (7 days)
        +
        +
        +
        ${ov.submissionsLast30d ?? 0}
        +
        Uploads (30 days)
        +
        +
        + `; + + const uploadChart = verticalBarChart( + (ov.submissionsByDay || []).map(d => ({ + label: formatChartDay(d.date), + value: d.count, + title: `${d.date}: ${d.count} upload(s)`, + })), + { emptyLabel: 'No uploads in this period' } + ); + + const withoutCompletions = Math.max(0, (ov.clientCount ?? 0) - (ov.clientsWithCompletions ?? 0)); + const engagementChart = horizontalBarChart([ + { label: 'With completions', value: ov.clientsWithCompletions ?? 0 }, + { label: 'No completions yet', value: withoutCompletions }, + ], { suffix: '' }); + + const idleChart = horizontalBarChart(idleBucketItems(staleClients), { + suffix: '', + variant: 'warn', + }); + + const completionChart = horizontalBarChart( + (ov.questionnaires || []).map(q => ({ + label: q.name, + value: q.completionRatio ?? 0, + title: `${q.name}: ${q.completionRatio ?? 0}%`, + })), + { suffix: '%', max: 100 } + ); + + const scoringProfiles = ov.scoringProfiles || []; + const scoringCharts = scoringProfiles.map(sp => { + const computed = sp.computedBands || sp.bands || {}; + const coach = sp.coachBands || {}; + const computedChart = horizontalBarChart([ + { label: 'Green', value: computed.green ?? 0 }, + { label: 'Yellow', value: computed.yellow ?? 0 }, + { label: 'Red', value: computed.red ?? 0 }, + ], { suffix: '' }); + const coachChart = horizontalBarChart([ + { label: 'Green', value: coach.green ?? 0 }, + { label: 'Yellow', value: coach.yellow ?? 0 }, + { label: 'Red', value: coach.red ?? 0 }, + ], { suffix: '' }); + return ` +
        +

        ${esc(sp.name)}

        +

        + ${sp.clientCount ?? 0} client(s) with complete profile + ${sp.averageTotal != null ? ` · avg ${sp.averageTotal}` : ''} + ${sp.pendingReview ? ` · ${sp.pendingReview} pending Counselor review` : ''} +

        +

        Calculated bands

        + ${computedChart} +

        Counselor decisions

        + ${coachChart} +
        `; + }).join(''); + + const qRows = (ov.questionnaires || []).map(q => ` + + ${esc(q.name)} + ${q.completedCount ?? 0} / ${q.eligibleCount ?? 0} + ${q.completionRatio ?? 0}% + + `).join(''); + + el.innerHTML = ` + ${kpiCards} +
        +
        +

        Uploads per day

        +

        Last 14 days

        + ${uploadChart} +
        +
        +

        Client engagement

        +

        Among clients in your scope

        + ${engagementChart} +
        +
        +

        Days since last activity

        +

        Clients with at least one completion

        + ${idleChart} +
        +
        + ${scoringProfiles.length ? ` +
        +

        Scoring profiles

        +

        Calculated vs Counselor band distribution among clients with a complete weighted score

        +
        ${scoringCharts}
        +
        ` : ''} +
        +

        Completion by questionnaire

        +

        Share of clients who completed each active questionnaire

        + ${completionChart} +
        + + + + + ${qRows || ''} +
        QuestionnaireCompletedRate
        No active questionnaires
        +
        +
        +
        +

        Needs follow-up

        +

        + Clients who completed at least one questionnaire, sorted by longest time since last activity. +

        +
        + + +
        +
        + + + + + + + + +
        ClientCounselorLast questionnaireCompleted atDays idleNote
        +
        +
        + `; + + document.getElementById('followupListSearch').addEventListener('input', (e) => { + followupSearch = e.target.value; + renderFollowupTableBody(); + }); + + renderFollowupTableBody(); +} + +function followupSearchText(c) { + return [ + c.clientCode, + c.coachUsername, + c.lastQuestionnaireName, + c.lastQuestionnaireID, + c.note, + c.lastCompletedAt, + c.daysSinceLastActivity != null ? String(c.daysSinceLastActivity) : '', + ].filter(Boolean).join(' '); +} + +function filteredFollowupClients() { + const q = followupSearch.trim().toLowerCase(); + if (!q) return staleClients; + return staleClients.filter(c => followupSearchText(c).toLowerCase().includes(q)); +} + +function followupRowHTML(c) { + const code = esc(c.clientCode); + return ` + + ${code} + ${esc(c.coachUsername)} + ${esc(c.lastQuestionnaireName || '—')} + ${esc(c.lastCompletedAt || '—')} + ${c.daysSinceLastActivity != null ? c.daysSinceLastActivity : '—'} + + +
        + +
        + + + ${clientTableActionsHTML({ clientCode: code, archived: false, showDelete: false })} + + `; +} + +function renderFollowupTableBody() { + const body = document.getElementById('followupTableBody'); + const countEl = document.getElementById('followupListCount'); + if (!body) return; + + const filtered = filteredFollowupClients(); + + if (!filtered.length) { + body.innerHTML = ` + + + ${staleClients.length ? 'No clients match' : 'No clients'} + + `; + if (countEl) { + countEl.textContent = followupSearch.trim() && staleClients.length + ? `0 of ${staleClients.length} clients` + : ''; + } + return; + } + + body.innerHTML = filtered.map(c => followupRowHTML(c)).join(''); + + if (countEl) { + const q = followupSearch.trim(); + countEl.textContent = q + ? `${filtered.length} of ${staleClients.length} clients` + : `${staleClients.length} client${staleClients.length === 1 ? '' : 's'}`; + } + + body.querySelectorAll('.save-note-btn').forEach(btn => { + btn.addEventListener('click', () => { + const tr = btn.closest('tr'); + const ta = tr?.querySelector('.followup-note-input'); + saveNote(btn.dataset.client, ta); + }); + }); + + body.querySelectorAll('.archive-client-btn').forEach(btn => { + btn.addEventListener('click', () => archiveFromFollowup(btn.dataset.code)); + }); +} + +async function archiveFromFollowup(clientCode) { + const ok = await confirmAndPatchClientArchive(clientCode, true); + if (!ok) return; + staleClients = staleClients.filter(c => c.clientCode !== clientCode); + renderFollowupTableBody(); +} + +async function saveNote(clientCode, ta) { + if (!ta) return; + try { + await apiPut('analytics.php', { clientCode, note: ta.value.trim() }); + const row = staleClients.find(c => c.clientCode === clientCode); + if (row) row.note = ta.value.trim(); + showToast('Note saved', 'success'); + } catch (e) { + showToast(e.message, 'error'); + } +} + +function esc(s) { + const d = document.createElement('div'); + d.textContent = s ?? ''; + return d.innerHTML; +} + +function formatChartDay(isoDate) { + if (!isoDate) return ''; + const d = new Date(isoDate + 'T12:00:00'); + if (Number.isNaN(d.getTime())) return isoDate.slice(5); + return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }); +} + +function chartMax(values, fallbackMax = 1) { + const m = Math.max(0, ...values); + return m > 0 ? m : fallbackMax; +} + +function verticalBarChart(items, { emptyLabel = 'No data' } = {}) { + if (!items.length) { + return `

        ${esc(emptyLabel)}

        `; + } + const max = chartMax(items.map(i => i.value)); + return ` + `; +} + +function horizontalBarChart(items, { suffix = '%', max: fixedMax, variant = '' } = {}) { + if (!items.length) { + return '

        No data

        '; + } + const max = fixedMax ?? chartMax(items.map(i => i.value)); + const fillClass = variant === 'warn' ? 'insights-hchart-fill--warn' + : variant === 'muted' ? 'insights-hchart-fill--muted' + : ''; + return ` + `; +} + +function idleBucketItems(clients) { + const buckets = [ + { label: '0–7 days', value: 0 }, + { label: '8–30 days', value: 0 }, + { label: '31–90 days', value: 0 }, + { label: '90+ days', value: 0 }, + { label: 'No uploads yet', value: 0 }, + ]; + for (const c of clients) { + const d = c.daysSinceLastActivity; + if (d == null) buckets[4].value += 1; + else if (d <= 7) buckets[0].value += 1; + else if (d <= 30) buckets[1].value += 1; + else if (d <= 90) buckets[2].value += 1; + else buckets[3].value += 1; + } + return buckets.filter(b => b.value > 0); +} diff --git a/website/js/pages/login.js b/website/js/pages/login.js new file mode 100644 index 0000000..b6fd4e2 --- /dev/null +++ b/website/js/pages/login.js @@ -0,0 +1,268 @@ +import { navigate } from '../router.js'; +import { isLoggedIn, showToast } from '../app.js'; +import { apiGet, invalidateSessionCheck, apiUrl } from '../api.js'; + +const WEBSITE_ROLES = ['admin', 'supervisor']; + +function setFormBusy(formId, busy, busyLabel) { + const form = document.getElementById(formId); + if (!form) return; + const btn = form.querySelector('button[type="submit"]'); + if (!btn) return; + if (!btn.dataset.defaultLabel) { + btn.dataset.defaultLabel = btn.textContent.trim(); + } + btn.disabled = busy; + btn.textContent = busy ? busyLabel : btn.dataset.defaultLabel; + form.querySelectorAll('input').forEach((el) => { el.disabled = busy; }); +} + +function setButtonBusy(button, busy, busyLabel) { + if (!button) return; + if (!button.dataset.defaultLabel) { + button.dataset.defaultLabel = button.textContent.trim(); + } + button.disabled = busy; + button.textContent = busy ? busyLabel : button.dataset.defaultLabel; +} + +export async function loginPage() { + if (isLoggedIn()) { + try { + const data = await apiGet('session'); + if (data.success && data.valid && !data.mustChangePassword && WEBSITE_ROLES.includes(data.role)) { + if (data.user) localStorage.setItem('qdb_user', data.user); + if (data.role) localStorage.setItem('qdb_role', data.role); + navigate('#/'); + return; + } + } catch { + invalidateSessionCheck(); + } + localStorage.removeItem('qdb_token'); + localStorage.removeItem('qdb_user'); + localStorage.removeItem('qdb_role'); + invalidateSessionCheck(); + } + + const app = document.getElementById('app'); + app.innerHTML = ` + + `; + + let pendingUsername = ''; + let pendingTempToken = ''; + + const keycloakSection = document.getElementById('keycloakLoginSection'); + const keycloakBtn = document.getElementById('keycloakLoginBtn'); + const keycloakErr = document.getElementById('keycloakLoginError'); + let keycloakLoginUrl = ''; + + apiGet('auth/keycloak-config') + .then((data) => { + if (!data.success || !data.enabled) { + keycloakBtn.textContent = 'University login unavailable'; + return; + } + keycloakLoginUrl = data.loginUrl ? apiUrl(data.loginUrl) : apiUrl('auth/keycloak-login'); + keycloakBtn.textContent = `Log in with ${data.displayName || 'University Konstanz'}`; + keycloakBtn.disabled = false; + }) + .catch(() => { + keycloakBtn.textContent = 'University login unavailable'; + }); + + keycloakBtn.addEventListener('click', () => { + keycloakErr.hidden = true; + if (!keycloakLoginUrl) { + keycloakErr.textContent = 'University login is not available.'; + keycloakErr.hidden = false; + return; + } + setButtonBusy(keycloakBtn, true, 'Redirecting...'); + window.location.assign(keycloakLoginUrl); + }); + + document.getElementById('loginForm').addEventListener('submit', async (e) => { + e.preventDefault(); + const errEl = document.getElementById('loginError'); + errEl.hidden = true; + const username = document.getElementById('username').value.trim(); + const password = document.getElementById('password').value; + + setFormBusy('loginForm', true, 'Signing in…'); + try { + const res = await fetch(apiUrl('auth/login'), { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-QDB-Client': 'web', + }, + body: JSON.stringify({ username, password }), + }); + const json = await res.json(); + if (!json.ok) { + const code = json.error?.code || ''; + let msg = json.error?.message || 'Login failed'; + if (res.status === 429 || code === 'RATE_LIMITED') { + const retry = res.headers.get('Retry-After'); + if (retry) { + msg += ` Try again in about ${retry} seconds.`; + } + } + errEl.textContent = msg; + errEl.hidden = false; + return; + } + const d = json.data; + if (d.role === 'coach') { + errEl.textContent = 'Counselor accounts can only sign in through the mobile app.'; + errEl.hidden = false; + return; + } + if (d.mustChangePassword) { + pendingUsername = username; + pendingTempToken = d.token; + document.getElementById('loginForm').hidden = true; + document.getElementById('changePwSection').hidden = false; + keycloakSection.hidden = true; + document.getElementById('login-panel-title').textContent = 'New password'; + document.getElementById('loginPanelSubtitle').textContent = pendingUsername; + document.getElementById('newPw').focus(); + return; + } + localStorage.setItem('qdb_token', d.token); + localStorage.setItem('qdb_user', d.user); + localStorage.setItem('qdb_role', d.role); + invalidateSessionCheck(); + navigate('#/'); + } catch (err) { + errEl.textContent = err.message; + errEl.hidden = false; + } finally { + setFormBusy('loginForm', false, 'Signing in…'); + } + }); + + document.getElementById('changePwForm').addEventListener('submit', async (e) => { + e.preventDefault(); + const errEl = document.getElementById('changePwError'); + errEl.hidden = true; + const newPw = document.getElementById('newPw').value; + const confirm = document.getElementById('newPwConfirm').value; + if (newPw !== confirm) { + errEl.textContent = 'Passwords do not match'; + errEl.hidden = false; + return; + } + const oldPw = document.getElementById('password').value; + + setFormBusy('changePwForm', true, 'Saving…'); + try { + const headers = { + 'Content-Type': 'application/json', + 'X-QDB-Client': 'web', + }; + if (pendingTempToken) { + headers['Authorization'] = `Bearer ${pendingTempToken}`; + } + const res = await fetch(apiUrl('auth/change-password'), { + method: 'POST', + headers, + body: JSON.stringify({ username: pendingUsername, old_password: oldPw, new_password: newPw }), + }); + const json = await res.json(); + if (!json.ok) { + errEl.textContent = json.error?.message || 'Password change failed'; + errEl.hidden = false; + return; + } + const d = json.data; + if (d.role === 'coach') { + errEl.textContent = 'Counselor accounts can only sign in through the mobile app.'; + errEl.hidden = false; + return; + } + localStorage.setItem('qdb_token', d.token); + localStorage.setItem('qdb_user', d.user); + localStorage.setItem('qdb_role', d.role); + invalidateSessionCheck(); + showToast('Password changed successfully', 'success'); + navigate('#/'); + } catch (err) { + errEl.textContent = err.message; + errEl.hidden = false; + } finally { + setFormBusy('changePwForm', false, 'Saving…'); + } + }); +} diff --git a/website/js/pages/questionnaires.js b/website/js/pages/questionnaires.js new file mode 100644 index 0000000..c3db61b --- /dev/null +++ b/website/js/pages/questionnaires.js @@ -0,0 +1,832 @@ +import { apiGet, apiPost, apiPut, apiDelete } from '../api.js'; +import { canEdit, pageHeaderHTML, showToast, getRole } from '../app.js'; +import { confirmAction } from '../confirm-modal.js'; +import { navigate } from '../router.js'; + +export async function questionnairesPage() { + const app = document.getElementById('app'); + app.innerHTML = ` + ${pageHeaderHTML('Session measures', '')} +
        +
        +
        + `; + + if (canEdit()) { + document.getElementById('headerActions').innerHTML = ` + + + + New Questionnaire + `; + bindImportBundle(); + } + + try { + const [data, profilesData] = await Promise.all([ + apiGet('questionnaires.php'), + apiGet('scoring_profiles.php').catch(() => ({ profiles: [] })), + ]); + const categoryKeys = data.categoryKeys || []; + renderGrid(data.questionnaires || [], categoryKeys); + renderScoringProfilesSection(profilesData.profiles || [], data.questionnaires || []); + renderCategoryKeysPanel(categoryKeys); + bindCategoryLabelEditors(); + } catch (e) { + showToast(e.message, 'error'); + document.getElementById('qGrid').innerHTML = `

        ${e.message}

        `; + } +} + +function renderCategoryKeysPanel(categoryKeys) { + const panel = document.getElementById('categoryKeysPanel'); + if (!panel) return; + + if (!canEdit()) { + panel.innerHTML = ''; + return; + } + + if (!categoryKeys.length) { + panel.innerHTML = ` +
        +

        Category keys

        +

        + Set a category key on a questionnaire (editor → settings) to group it on the app opening screen. + German group titles can be edited below once a key is in use. +

        +
        `; + return; + } + + panel.innerHTML = ` +
        +

        Category keys

        +

        + German labels for the app opening screen. Other languages: Translations. + Delete removes the key from questionnaires, all translations, and catalog defaults. +

        +
        + + + + + + + + + + + + ${categoryKeys.map(row => ` + + + + + + + + `).join('')} + +
        Category keyApp string keyGerman labelSession measures
        ${esc(row.categoryKey)}${esc(row.stringKey)} + + ${row.orphaned + ? 'unused' + : esc(String(row.questionnaireCount))} + +
        +
        +
        `; + + panel.querySelectorAll('.delete-cat-key-btn').forEach(btn => { + btn.addEventListener('click', async (e) => { + e.stopPropagation(); + const key = btn.dataset.key; + const count = parseInt(btn.dataset.count || '0', 10); + let msg = `Delete category key "${key}"?\n\nThis removes all translations and catalog defaults for questionnaire_group_${key}.`; + if (count > 0) { + msg += `\n\n${count} questionnaire(s) will have their category key cleared.`; + } + if (!(await confirmAction({ + title: 'Delete category key', + message: msg, + confirmLabel: 'Delete', + variant: 'danger', + }))) return; + btn.disabled = true; + try { + const result = await apiPost('questionnaires.php', { action: 'deleteCategoryKey', categoryKey: key }); + const cleared = result.questionnairesCleared ?? count; + showToast( + cleared + ? `Deleted "${key}" and cleared ${cleared} questionnaire(s)` + : `Deleted "${key}"`, + 'success' + ); + await questionnairesPage(); + } catch (err) { + showToast(err.message, 'error'); + btn.disabled = false; + } + }); + }); +} + +function categoryGermanInputHTML(categoryKey, label) { + if (!canEdit() || !categoryKey) { + return esc(label); + } + return ``; +} + +function bindCategoryLabelEditors() { + if (!canEdit()) return; + + document.querySelectorAll('.category-german-input').forEach(input => { + input.dataset.lastSaved = input.value.trim(); + + input.addEventListener('keydown', (e) => { + if (e.key === 'Enter') { + e.preventDefault(); + input.blur(); + } + }); + + input.addEventListener('blur', () => saveCategoryGermanLabel(input)); + }); +} + +async function saveCategoryGermanLabel(input) { + const categoryKey = input.dataset.categoryKey?.trim(); + const label = input.value.trim(); + if (!categoryKey) return; + + const prev = input.dataset.lastSaved ?? ''; + if (label === prev) return; + + if (!label) { + showToast('German label cannot be empty', 'error'); + input.value = prev; + return; + } + + input.disabled = true; + try { + await apiPost('questionnaires.php', { + action: 'updateCategoryLabel', + categoryKey, + germanLabel: label, + }); + input.dataset.lastSaved = label; + syncCategoryGermanInputs(categoryKey, label); + showToast('Category label saved', 'success'); + } catch (err) { + showToast(err.message, 'error'); + input.value = prev; + } finally { + input.disabled = false; + } +} + +function syncCategoryGermanInputs(categoryKey, label) { + document.querySelectorAll('.category-german-input').forEach(el => { + if (el.dataset.categoryKey === categoryKey) { + el.value = label; + el.dataset.lastSaved = label; + } + }); +} + +const UNCATEGORIZED = '__uncategorized__'; + +function groupQuestionnairesByCategory(questionnaires, categoryKeys) { + const labelByKey = Object.fromEntries( + (categoryKeys || []) + .filter(r => !r.orphaned) + .map(r => [r.categoryKey, r.germanLabel || r.categoryKey]) + ); + + const sorted = [...questionnaires].sort((a, b) => (a.orderIndex ?? 0) - (b.orderIndex ?? 0)); + const buckets = new Map(); + + for (const q of sorted) { + const key = (q.categoryKey || '').trim() || UNCATEGORIZED; + if (!buckets.has(key)) buckets.set(key, []); + buckets.get(key).push(q); + } + + const sections = [...buckets.entries()].map(([key, items]) => ({ + categoryKey: key === UNCATEGORIZED ? '' : key, + title: key === UNCATEGORIZED ? 'Uncategorized' : (labelByKey[key] || key), + items, + sortKey: Math.min(...items.map(q => q.orderIndex ?? 0)), + })); + + sections.sort((a, b) => { + const aUncat = a.categoryKey === ''; + const bUncat = b.categoryKey === ''; + if (aUncat !== bUncat) { + return aUncat ? 1 : -1; + } + return a.sortKey - b.sortKey; + }); + return sections; +} + +function renderQuestionnaireCard(q) { + const stateClass = `badge-${(q.state || 'draft').toLowerCase().replace(/\s+/g, '_')}`; + return ` +
        +
        +
        + ${canEdit() ? '' : ''} + ${esc(q.name)} +
        + ${esc(q.state || 'draft')} +
        +
        + v${esc(q.version || '—')} + ${q.questionCount || 0} question${q.questionCount == 1 ? '' : 's'} + #${q.orderIndex ?? '—'} +
        +
        + Edit + Results + ${getRole() === 'admin' ? `` : ''} +
        +
        `; +} + +function renderGrid(questionnaires, categoryKeys = []) { + const grid = document.getElementById('qGrid'); + + if (!questionnaires.length) { + grid.innerHTML = ` +
        + +

        No questionnaires yet

        +

        Create your first questionnaire to get started.

        +
        + `; + return; + } + + const sections = groupQuestionnairesByCategory(questionnaires, categoryKeys); + + grid.innerHTML = ` +
        + ${sections.map(section => ` +
        +

        ${categoryGermanInputHTML(section.categoryKey, section.title)}

        +
        + ${section.items.map(q => renderQuestionnaireCard(q)).join('')} +
        +
        + `).join('')} +
        `; + + grid.querySelectorAll('.q-card').forEach(card => { + card.addEventListener('click', (e) => { + if (e.target.closest('.q-card-actions') || e.target.closest('.drag-handle')) return; + navigate(`#/questionnaire/${card.dataset.id}`); + }); + }); + + grid.querySelectorAll('.delete-q-btn').forEach(btn => { + btn.addEventListener('click', async (e) => { + e.stopPropagation(); + if (!(await confirmAction({ + title: 'Delete questionnaire', + message: 'Delete this questionnaire and all its data?', + confirmLabel: 'Delete', + variant: 'danger', + }))) return; + try { + await apiDelete('questionnaires.php', { questionnaireID: btn.dataset.id }); + showToast('Questionnaire deleted', 'success'); + btn.closest('.q-card').remove(); + } catch (err) { + showToast(err.message, 'error'); + } + }); + }); + + if (canEdit()) initGridDrag(questionnaires); +} + +function collectDashboardCardOrder() { + const ids = []; + document.querySelectorAll('.q-category-group').forEach(section => { + section.querySelectorAll('.q-category-grid .q-card').forEach(card => { + ids.push(card.dataset.id); + }); + }); + return ids; +} + +function initGridDrag(questionnaires) { + const root = document.getElementById('qGrid'); + if (!root) return; + let dragItem = null; + let dragRow = null; + + root.addEventListener('dragstart', (e) => { + const card = e.target.closest('.q-card'); + if (!card || !e.target.closest('.drag-handle')) { e.preventDefault(); return; } + dragItem = card; + dragRow = card.closest('.q-category-grid'); + card.style.opacity = '0.5'; + e.dataTransfer.effectAllowed = 'move'; + }); + + root.addEventListener('dragover', (e) => { + e.preventDefault(); + if (!dragItem || !dragRow) return; + + const row = e.target.closest('.q-category-grid'); + if (!row || row !== dragRow) return; + + const cards = [...row.querySelectorAll('.q-card')].filter(c => c !== dragItem); + let insertBefore = null; + for (const child of cards) { + const box = child.getBoundingClientRect(); + if (e.clientX < box.left + box.width / 2) { + insertBefore = child; + break; + } + } + if (insertBefore) row.insertBefore(dragItem, insertBefore); + else row.appendChild(dragItem); + }); + + root.addEventListener('dragend', async () => { + if (!dragItem) return; + dragItem.style.opacity = ''; + const newOrder = collectDashboardCardOrder(); + dragItem = null; + dragRow = null; + + for (let i = 0; i < newOrder.length; i++) { + const id = newOrder[i]; + const q = questionnaires.find(x => x.questionnaireID === id); + if (q && parseInt(q.orderIndex, 10) !== i) { + try { + await apiPut('questionnaires.php', { questionnaireID: id, orderIndex: i }); + q.orderIndex = i; + } catch (err) { + showToast(err.message, 'error'); + return; + } + } + } + showToast('Order saved', 'success'); + }); +} + +function normalizeProfileBands(profile) { + const greenMax = profile?.greenMax ?? 12; + const yellowMax = profile?.yellowMax ?? 36; + return { + greenMin: profile?.greenMin ?? 0, + greenMax, + yellowMin: profile?.yellowMin ?? (greenMax + 1), + yellowMax, + redMin: profile?.redMin ?? (yellowMax + 1), + }; +} + +function bandSummary(profile) { + const b = normalizeProfileBands(profile || {}); + return `Green ${b.greenMin}–${b.greenMax} · Yellow ${b.yellowMin}–${b.yellowMax} · Red ${b.redMin}+`; +} + +function readBandsFromModal(overlay) { + return { + greenMin: parseInt(overlay.querySelector('#spGreenMin')?.value || '0', 10), + greenMax: parseInt(overlay.querySelector('#spGreenMax')?.value || '0', 10), + yellowMin: parseInt(overlay.querySelector('#spYellowMin')?.value || '0', 10), + yellowMax: parseInt(overlay.querySelector('#spYellowMax')?.value || '0', 10), + redMin: parseInt(overlay.querySelector('#spRedMin')?.value || '0', 10), + }; +} + +function validateBands(b) { + if (b.greenMin > b.greenMax) return 'Green min must not exceed green max'; + if (b.yellowMin > b.yellowMax) return 'Yellow min must not exceed yellow max'; + if (b.greenMax >= b.yellowMin) return 'Green range must end before yellow range starts'; + if (b.yellowMax >= b.redMin) return 'Yellow range must end before red range starts'; + return null; +} + +function bandPreviewHTML(b) { + return ` +
        + Green: ${b.greenMin} – ${b.greenMax} + Yellow: ${b.yellowMin} – ${b.yellowMax} + Red: ${b.redMin}+ +
        `; +} + +function bandRangesEditorHTML(bands) { + const b = normalizeProfileBands(bands); + return ` +

        Score bands

        +

        Set inclusive min/max for each band. Ranges must not overlap.

        +
        +
        + BandMinMax +
        +
        + Green + + +
        +
        + Yellow + + +
        +
        + Red + + +
        +
        +
        `; +} + +function renderScoringProfilesSection(profiles, questionnaires) { + const panel = document.getElementById('scoringProfilesPanel'); + if (!panel) return; + + panel.innerHTML = ` +
        +
        +
        +
        +

        Scoring profiles

        +

        + Weighted multi-questionnaire scores with Green / Yellow / Red bands for insights. +

        +
        + ${canEdit() ? '' : ''} +
        +
        +
        +
        `; + + const list = panel.querySelector('#scoringProfilesList'); + if (!profiles.length) { + list.innerHTML = '

        No scoring profiles yet. Create one to combine questionnaire scores for insights.

        '; + } else { + list.innerHTML = profiles.map(p => scoringProfileCardHTML(p, questionnaires)).join(''); + } + + if (canEdit()) { + panel.querySelector('#newScoringProfileBtn')?.addEventListener('click', () => { + openScoringProfileModal(null, questionnaires, profiles); + }); + list.querySelectorAll('.edit-profile-btn').forEach(btn => { + btn.addEventListener('click', () => { + const profile = profiles.find(x => x.profileID === btn.dataset.id); + openScoringProfileModal(profile, questionnaires, profiles); + }); + }); + list.querySelectorAll('.delete-profile-btn').forEach(btn => { + btn.addEventListener('click', async () => { + if (!(await confirmAction({ + title: 'Delete scoring profile', + message: `Delete scoring profile "${btn.dataset.name}"?`, + confirmLabel: 'Delete', + variant: 'danger', + }))) return; + try { + await apiDelete('scoring_profiles.php', { profileID: btn.dataset.id }); + showToast('Profile deleted', 'success'); + await questionnairesPage(); + } catch (err) { + showToast(err.message, 'error'); + } + }); + }); + } +} + +function scoringProfileCardHTML(profile, questionnaires) { + const qnById = Object.fromEntries((questionnaires || []).map(q => [q.questionnaireID, q])); + const members = profile.questionnaires || []; + const activeBadge = profile.isActive + ? 'Active' + : 'Inactive'; + const memberRows = members.map(m => { + const q = qnById[m.questionnaireID]; + const name = q?.name || m.questionnaireID; + return `
        ${esc(name)}× ${m.weight}
        `; + }).join(''); + + return ` +
        +
        +
        +
        + ${esc(profile.name)} + ${activeBadge} +
        + ${profile.description ? `

        ${esc(profile.description)}

        ` : ''} +

        ${bandSummary(profile)}

        +
        + ${canEdit() ? ` +
        + + +
        ` : ''} +
        +

        ${members.length} questionnaire(s)

        +
        ${memberRows || '

        No questionnaires linked

        '}
        +
        `; +} + +function openScoringProfileModal(profile, questionnaires, allProfiles) { + const isEdit = !!profile; + const members = profile?.questionnaires?.length + ? [...profile.questionnaires] + : []; + const selectedIds = new Set(members.map(m => m.questionnaireID)); + const initialBands = normalizeProfileBands(profile || {}); + + const overlay = document.createElement('div'); + overlay.className = 'modal-overlay'; + overlay.innerHTML = ` + `; + document.body.appendChild(overlay); + document.body.classList.add('modal-open'); + + const picker = overlay.querySelector('#spQnPicker'); + const sortedQn = [...(questionnaires || [])].sort((a, b) => (a.orderIndex ?? 0) - (b.orderIndex ?? 0)); + + function renderPicker() { + const ordered = members.length + ? members.map(m => ({ ...m, q: sortedQn.find(q => q.questionnaireID === m.questionnaireID) })) + : []; + const available = sortedQn.filter(q => !selectedIds.has(q.questionnaireID)); + + picker.innerHTML = ` + ${ordered.map((m, idx) => ` +
        + ${esc(m.q?.name || m.questionnaireID)} + + + + + + +
        + `).join('')} + ${available.length ? ` +
        + + +
        ` : ''}`; + + picker.querySelectorAll('.sp-weight').forEach((input, idx) => { + input.addEventListener('change', () => { + members[idx].weight = parseFloat(input.value) || 1; + }); + }); + picker.querySelectorAll('.sp-remove').forEach(btn => { + btn.addEventListener('click', () => { + const row = btn.closest('.scoring-profile-q-row'); + const id = row?.dataset.qn; + if (!id) return; + selectedIds.delete(id); + const i = members.findIndex(m => m.questionnaireID === id); + if (i >= 0) members.splice(i, 1); + renderPicker(); + }); + }); + picker.querySelectorAll('.sp-move-up').forEach(btn => { + btn.addEventListener('click', () => { + const row = btn.closest('.scoring-profile-q-row'); + const id = row?.dataset.qn; + const i = members.findIndex(m => m.questionnaireID === id); + if (i > 0) { + [members[i - 1], members[i]] = [members[i], members[i - 1]]; + renderPicker(); + } + }); + }); + picker.querySelectorAll('.sp-move-down').forEach(btn => { + btn.addEventListener('click', () => { + const row = btn.closest('.scoring-profile-q-row'); + const id = row?.dataset.qn; + const i = members.findIndex(m => m.questionnaireID === id); + if (i >= 0 && i < members.length - 1) { + [members[i], members[i + 1]] = [members[i + 1], members[i]]; + renderPicker(); + } + }); + }); + overlay.querySelector('#spAddBtn')?.addEventListener('click', () => { + const sel = overlay.querySelector('#spAddSelect'); + const id = sel?.value; + if (!id || selectedIds.has(id)) return; + selectedIds.add(id); + members.push({ questionnaireID: id, weight: 1, orderIndex: members.length }); + renderPicker(); + }); + } + + function updateBandPreview() { + const el = overlay.querySelector('#spBandPreview'); + if (!el) return; + const b = readBandsFromModal(overlay); + const err = validateBands(b); + if (err) { + el.innerHTML = `

        ${esc(err)}

        `; + return; + } + el.innerHTML = bandPreviewHTML(b); + } + + renderPicker(); + updateBandPreview(); + ['#spGreenMin', '#spGreenMax', '#spYellowMin', '#spYellowMax', '#spRedMin'].forEach(sel => { + overlay.querySelector(sel)?.addEventListener('input', updateBandPreview); + }); + + const close = () => { + overlay.remove(); + document.body.classList.remove('modal-open'); + }; + overlay.querySelector('#spCancel')?.addEventListener('click', close); + overlay.addEventListener('click', (e) => { if (e.target === overlay) close(); }); + + overlay.querySelector('#spSave')?.addEventListener('click', async () => { + const name = overlay.querySelector('#spName')?.value.trim(); + const description = overlay.querySelector('#spDesc')?.value.trim() || ''; + const isActive = overlay.querySelector('#spActive')?.checked; + const bands = readBandsFromModal(overlay); + const bandErr = validateBands(bands); + if (!name) { + showToast('Profile name is required', 'error'); + return; + } + if (bandErr) { + showToast(bandErr, 'error'); + return; + } + if (!members.length) { + showToast('Add at least one questionnaire', 'error'); + return; + } + const payload = { + name, + description, + isActive, + ...bands, + questionnaires: members.map((m, idx) => ({ + questionnaireID: m.questionnaireID, + weight: parseFloat(picker.querySelector(`[data-qn="${m.questionnaireID}"] .sp-weight`)?.value) || m.weight || 1, + orderIndex: idx, + })), + }; + if (isEdit) payload.profileID = profile.profileID; + + const saveBtn = overlay.querySelector('#spSave'); + saveBtn.disabled = true; + try { + if (isEdit) { + await apiPut('scoring_profiles.php', payload); + } else { + await apiPost('scoring_profiles.php', payload); + } + showToast(isEdit ? 'Profile saved' : 'Profile created', 'success'); + close(); + await questionnairesPage(); + } catch (err) { + showToast(err.message, 'error'); + saveBtn.disabled = false; + } + }); +} + +function bindImportBundle() { + const fileInput = document.getElementById('importBundleFile'); + const btn = document.getElementById('importBundleBtn'); + if (!fileInput || !btn) return; + + btn.addEventListener('click', () => fileInput.click()); + + fileInput.addEventListener('change', async () => { + const file = fileInput.files?.[0]; + fileInput.value = ''; + if (!file) return; + + let bundle; + try { + const text = await file.text(); + bundle = JSON.parse(text); + } catch { + showToast('Invalid JSON file', 'error'); + return; + } + + if (!bundle?.questionnaires || !Array.isArray(bundle.questionnaires)) { + showToast('Not a questionnaire bundle (missing questionnaires array)', 'error'); + return; + } + + const count = bundle.questionnaires.length; + if (!(await confirmAction({ + title: 'Import questionnaires', + message: `Import ${count} questionnaire(s) from this file?`, + confirmLabel: 'Import', + }))) return; + + const mode = await confirmAction({ + title: 'Existing questionnaire IDs', + message: 'If a questionnaire ID from the file already exists, how should it be handled?', + choices: [ + { + value: 'replace', + label: 'Replace existing', + description: 'Overwrite the questionnaire with that ID.', + }, + { + value: 'copy', + label: 'Import as new copy', + description: 'Keep the existing questionnaire and import with a new ID.', + }, + ], + confirmLabel: 'Continue', + cancelLabel: 'Cancel import', + }); + if (!mode) return; + const replace = mode === 'replace'; + + btn.disabled = true; + const prev = btn.textContent; + btn.textContent = 'Importing…'; + try { + const result = await apiPost('questionnaires.php', { + action: 'importBundle', + bundle, + replaceIfExists: replace, + }); + const n = result.imported ?? count; + showToast(`Imported ${n} questionnaire(s)`, 'success'); + await questionnairesPage(); + } catch (e) { + showToast(e.message, 'error'); + } finally { + btn.disabled = false; + btn.textContent = prev; + } + }); +} + +function esc(s) { + const d = document.createElement('div'); + d.textContent = s ?? ''; + return d.innerHTML; +} diff --git a/website/js/pages/results.js b/website/js/pages/results.js new file mode 100644 index 0000000..5ad7216 --- /dev/null +++ b/website/js/pages/results.js @@ -0,0 +1,468 @@ +import { apiGet } from '../api.js'; +import { homeNavButton, showToast } from '../app.js'; +import { buildResultColumns, resultColumnCellValue } from '../test-data.js'; + +const PAGE_SIZE = 50; + +let sortCol = null; +let sortDir = 'asc'; +let allClients = []; +let questionsDef = []; +let resultColumns = []; +let questionnaireMeta = null; +let filterCoach = ''; +let filterSupervisor = ''; +let filterStatus = ''; +let filterDateFrom = ''; +let filterDateTo = ''; +let filterSearch = ''; +let filterQuestion = ''; +let page = 0; + +export async function resultsPage(params) { + const app = document.getElementById('app'); + sortCol = null; + sortDir = 'asc'; + filterCoach = ''; + filterSupervisor = ''; + filterStatus = ''; + filterDateFrom = ''; + filterDateTo = ''; + filterSearch = ''; + filterQuestion = ''; + page = 0; + + app.innerHTML = ` + +
        + `; + + try { + const data = await apiGet(`results.php?questionnaireID=${encodeURIComponent(params.id)}`); + questionnaireMeta = data.questionnaire; + questionsDef = data.questions || []; + allClients = data.clients || []; + document.getElementById('resultsTitle').textContent = `Results: ${questionnaireMeta.name} v${questionnaireMeta.version}`; + renderResults(); + } catch (e) { + showToast(e.message, 'error'); + document.getElementById('resultsContent').innerHTML = `

        ${esc(e.message)}

        `; + } +} + +function renderResults() { + const container = document.getElementById('resultsContent'); + resultColumns = buildResultColumns(questionsDef); + + // Collect unique coaches and statuses for filters + const coachOptions = getCoachFilterOptions(); + const supervisorOptions = getSupervisorFilterOptions(); + const statuses = [...new Set(allClients.map(c => c.status))].filter(Boolean).sort(); + + container.innerHTML = ` +
        +
        + + + + + + + + + + + + + Export CSV +
        +

        Each question has its own column; glass-scale symptoms are separate columns. Hover headers for context.

        +
        +
        +
        + + + +
        +
        +
        +
        + `; + + const qKeyList = document.getElementById('questionKeyList'); + qKeyList.innerHTML = resultColumns + .map(col => ``) + .join(''); + + document.getElementById('filterSupervisor').addEventListener('change', (e) => { + filterSupervisor = e.target.value; + page = 0; + renderTableRows(); + }); + document.getElementById('filterCoach').addEventListener('change', (e) => { + filterCoach = e.target.value; + page = 0; + renderTableRows(); + }); + document.getElementById('filterStatus').addEventListener('change', (e) => { + filterStatus = e.target.value; + page = 0; + renderTableRows(); + }); + document.getElementById('filterDateFrom').addEventListener('change', (e) => { + filterDateFrom = e.target.value; + page = 0; + renderTableRows(); + }); + document.getElementById('filterDateTo').addEventListener('change', (e) => { + filterDateTo = e.target.value; + page = 0; + renderTableRows(); + }); + document.getElementById('filterSearch').addEventListener('input', (e) => { + filterSearch = e.target.value.trim(); + page = 0; + renderTableRows(); + }); + document.getElementById('gotoQuestionBtn').addEventListener('click', () => { + filterQuestion = document.getElementById('filterQuestion').value.trim(); + scrollToQuestionColumn(filterQuestion); + }); + document.getElementById('filterQuestion').addEventListener('keydown', (e) => { + if (e.key === 'Enter') { + e.preventDefault(); + filterQuestion = e.target.value.trim(); + scrollToQuestionColumn(filterQuestion); + } + }); + document.getElementById('clearFiltersBtn').addEventListener('click', () => { + filterCoach = ''; + filterSupervisor = ''; + filterStatus = ''; + filterDateFrom = ''; + filterDateTo = ''; + filterSearch = ''; + filterQuestion = ''; + page = 0; + renderResults(); + }); + document.getElementById('exportCsvLink').addEventListener('click', (e) => { + e.preventDefault(); + exportCSV(); + }); + + renderTableHead(); + renderTableRows(); +} + +function renderTableHead() { + const head = document.getElementById('resultsHead'); + const fixedCols = [ + { key: 'clientCode', label: 'Client' }, + { key: 'coachUsername', label: 'Counselor' }, + { key: 'supervisorUsername', label: 'Supervisor' }, + { key: 'status', label: 'Status' }, + { key: 'sumPoints', label: 'Score' }, + { key: 'completedAt', label: 'Completed' }, + ]; + const allCols = [...fixedCols, ...resultColumns.map(col => ({ + key: col.key, + label: col.label, + title: col.title, + }))]; + + head.innerHTML = allCols.map((col, idx) => { + let cls = 'sortable'; + if (sortCol === col.key) cls += sortDir === 'asc' ? ' sort-asc' : ' sort-desc'; + if (idx === 0) cls += ' sticky-col'; + const title = col.title ? ` title="${esc(col.title)}"` : ''; + return `${esc(col.label)}`; + }).join(''); + + head.querySelectorAll('th.sortable').forEach(th => { + th.addEventListener('click', () => { + const key = th.dataset.key; + if (sortCol === key) sortDir = sortDir === 'asc' ? 'desc' : 'asc'; + else { sortCol = key; sortDir = 'asc'; } + page = 0; + renderTableHead(); + renderTableRows(); + }); + }); +} + +function scrollToQuestionColumn(query) { + if (!query) return; + const q = query.toLowerCase(); + const th = [...document.querySelectorAll('#resultsHead th')].find(el => { + const key = (el.dataset.key || '').toLowerCase(); + const text = (el.textContent || '').trim().toLowerCase(); + const title = (el.getAttribute('title') || '').toLowerCase(); + return text === q || title === q || key.endsWith(q) || text.includes(q); + }); + if (!th) { + showToast(`No column matching "${query}"`, 'error'); + return; + } + th.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); + th.style.outline = '2px solid var(--primary)'; + setTimeout(() => { th.style.outline = ''; }, 2000); +} + +function getCoachFilterOptions() { + const map = new Map(); + allClients.forEach(c => { + if (!c.coachID || map.has(c.coachID)) return; + map.set(c.coachID, c.coachUsername || c.coachID); + }); + return [...map.entries()].sort((a, b) => a[1].localeCompare(b[1])); +} + +function getSupervisorFilterOptions() { + return [...new Set(allClients.map(c => c.supervisorUsername).filter(Boolean))].sort((a, b) => + a.localeCompare(b) + ); +} + +function coachDisplayName(c) { + return c.coachUsername || c.coachID || '—'; +} + +function supervisorDisplayName(c) { + return c.supervisorUsername || '—'; +} + +function getFilteredClients() { + let clients = allClients; + if (filterSupervisor) clients = clients.filter(c => c.supervisorUsername === filterSupervisor); + if (filterCoach) clients = clients.filter(c => c.coachID === filterCoach); + if (filterStatus) clients = clients.filter(c => c.status === filterStatus); + if (filterDateFrom || filterDateTo) { + const fromTs = filterDateFrom ? dateInputToUnixStart(filterDateFrom) : null; + const toTs = filterDateTo ? dateInputToUnixEnd(filterDateTo) : null; + clients = clients.filter(c => { + const ts = c.completedAt; + if (!ts) return false; + if (fromTs !== null && ts < fromTs) return false; + if (toTs !== null && ts > toTs) return false; + return true; + }); + } + if (filterSearch) { + const q = filterSearch.toLowerCase(); + clients = clients.filter(c => { + const hay = [ + c.clientCode, + c.coachUsername, + c.supervisorUsername, + c.status, + ].filter(Boolean).join(' ').toLowerCase(); + return hay.includes(q); + }); + } + return clients; +} + +function renderPagination(totalRows) { + const bar = document.getElementById('paginationBar'); + if (!bar) return; + const totalPages = Math.max(1, Math.ceil(totalRows / PAGE_SIZE)); + if (page >= totalPages) page = totalPages - 1; + if (page < 0) page = 0; + + if (totalRows <= PAGE_SIZE) { + bar.innerHTML = totalRows + ? `${totalRows} row${totalRows === 1 ? '' : 's'}` + : ''; + return; + } + + const from = page * PAGE_SIZE + 1; + const to = Math.min((page + 1) * PAGE_SIZE, totalRows); + bar.innerHTML = ` + + Rows ${from}–${to} of ${totalRows} (page ${page + 1}/${totalPages}) + + `; + document.getElementById('pagePrev')?.addEventListener('click', () => { + page--; + renderTableRows(); + }); + document.getElementById('pageNext')?.addEventListener('click', () => { + page++; + renderTableRows(); + }); +} + +function dateInputToUnixStart(yyyyMmDd) { + const [y, m, d] = yyyyMmDd.split('-').map(Number); + return Math.floor(new Date(y, m - 1, d).getTime() / 1000); +} + +function dateInputToUnixEnd(yyyyMmDd) { + const [y, m, d] = yyyyMmDd.split('-').map(Number); + return Math.floor(new Date(y, m - 1, d, 23, 59, 59, 999).getTime() / 1000); +} + +function renderTableRows() { + const body = document.getElementById('resultsBody'); + let clients = getFilteredClients(); + + // Sort + if (sortCol) { + clients = [...clients].sort((a, b) => { + let va = getCellValue(a, sortCol); + let vb = getCellValue(b, sortCol); + if (va == null) va = ''; + if (vb == null) vb = ''; + if (typeof va === 'number' && typeof vb === 'number') { + return sortDir === 'asc' ? va - vb : vb - va; + } + const cmp = String(va).localeCompare(String(vb), undefined, { numeric: true }); + return sortDir === 'asc' ? cmp : -cmp; + }); + } + + document.getElementById('resultCount').textContent = `${clients.length} client${clients.length === 1 ? '' : 's'}`; + + renderPagination(clients.length); + + if (!clients.length) { + body.innerHTML = `No results found`; + return; + } + + const totalPages = Math.max(1, Math.ceil(clients.length / PAGE_SIZE)); + if (page >= totalPages) page = totalPages - 1; + clients = clients.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE); + + // Build option text lookup + const optionMap = {}; + questionsDef.forEach(q => { + (q.answerOptions || []).forEach(o => { + optionMap[o.answerOptionID] = o.defaultText; + }); + }); + + body.innerHTML = clients.map(c => { + const completedDate = c.completedAt ? new Date(c.completedAt * 1000).toLocaleDateString() : '—'; + const statusBadge = c.status ? `${esc(c.status)}` : '—'; + + const questionCells = resultColumns.map(col => { + const ans = c.answers && c.answers[col.questionID]; + const val = resultColumnCellValue(col, ans, optionMap); + if (val == null || val === '') return '—'; + return `${esc(String(val))}`; + }).join(''); + + return ` + ${esc(c.clientCode)} + ${esc(coachDisplayName(c))} + ${esc(supervisorDisplayName(c))} + ${statusBadge} + ${c.sumPoints !== null ? c.sumPoints : '—'} + ${completedDate} + ${questionCells} + `; + }).join(''); +} + +function getCellValue(client, key) { + if (key === 'clientCode') return client.clientCode; + if (key === 'coachUsername') return coachDisplayName(client); + if (key === 'supervisorUsername') return supervisorDisplayName(client); + if (key === 'status') return client.status; + if (key === 'sumPoints') return client.sumPoints; + if (key === 'completedAt') return client.completedAt; + const col = resultColumns.find(c => c.key === key); + if (col) { + const ans = client.answers && client.answers[col.questionID]; + const optionMap = {}; + questionsDef.forEach(q => { + (q.answerOptions || []).forEach(o => { + optionMap[o.answerOptionID] = o.defaultText; + }); + }); + return resultColumnCellValue(col, ans, optionMap); + } + return null; +} + +function exportCSV() { + const clients = getFilteredClients(); + const optionMap = {}; + questionsDef.forEach(q => { + (q.answerOptions || []).forEach(o => { + optionMap[o.answerOptionID] = o.defaultText; + }); + }); + + const headers = ['clientCode', 'Counselor', 'supervisor', 'status', 'sumPoints', 'completedAt', + ...resultColumns.map(col => col.label)]; + + const rows = clients.map(c => { + const base = [ + c.clientCode, coachDisplayName(c), c.supervisorUsername || '', c.status, c.sumPoints ?? '', + c.completedAt ? new Date(c.completedAt * 1000).toISOString() : '' + ]; + const answerCols = resultColumns.map(col => { + const ans = c.answers && c.answers[col.questionID]; + const val = resultColumnCellValue(col, ans, optionMap); + return val != null ? val : ''; + }); + return [...base, ...answerCols]; + }); + + let csv = '\uFEFF'; // BOM + csv += headers.map(csvEsc).join(',') + '\n'; + rows.forEach(r => { csv += r.map(csvEsc).join(',') + '\n'; }); + + const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `${questionnaireMeta.name}_v${questionnaireMeta.version}_results.csv`; + a.click(); + URL.revokeObjectURL(url); + showToast('CSV exported', 'success'); +} + +function csvEsc(val) { + const s = String(val ?? ''); + if (s.includes(',') || s.includes('"') || s.includes('\n')) { + return '"' + s.replace(/"/g, '""') + '"'; + } + return s; +} + +function esc(s) { + const d = document.createElement('div'); + d.textContent = s ?? ''; + return d.innerHTML; +} diff --git a/website/js/pages/translations.js b/website/js/pages/translations.js new file mode 100644 index 0000000..1103a5e --- /dev/null +++ b/website/js/pages/translations.js @@ -0,0 +1,707 @@ +import { apiGet, apiPost, apiPut, apiDelete, apiDownloadFetch, apiUrl } from '../api.js'; +import { canEdit, pageHeaderHTML, showToast } from '../app.js'; +import { confirmAction } from '../confirm-modal.js'; +import { + SOURCE_LANG, + normalizeEntry, + translationFor, + targetLanguages, + sourceLanguageLabel, + translationListHeaderHTML, + buildTranslationGroups, + renderCollapsibleGroupedTranslationsHTML, + flattenAllQuestionnaires, + countMissing, + sectionEntries, + escHtml as esc, +} from '../translations-helpers.js'; + +const STORAGE_LANG = 'qdb_trans_lang'; +const STORAGE_SCOPE = 'qdb_trans_scope'; +const STORAGE_SHOW = 'qdb_trans_show_mode'; + +let transData = null; +let allEntries = []; +let selectedLang = ''; +let selectedScope = ''; +/** @type {'missing' | 'all' | 'complete'} */ +let showMode = 'missing'; +let saveTimers = {}; +let savesInFlight = 0; + +export async function translationsPage() { + const app = document.getElementById('app'); + app.innerHTML = ` + ${pageHeaderHTML('Translations', '')} +
        + `; + + if (canEdit()) { + document.getElementById('transHeaderActions').innerHTML = ` + + + + `; + bindTranslationBundleActions(); + } + + try { + renderShell(); + await loadTranslations(); + } catch (e) { + document.getElementById('transPageRoot').innerHTML = + `

        ${esc(e.message)}

        `; + } +} + +function renderShell() { + document.getElementById('transPageRoot').innerHTML = ` +
        +
        + +
        + +
        + More languages +
        Loading…
        +
        + +
        + + + + +
        + +
        +
        + `; +} + +function pickDefaultTargetLang(languages) { + const targets = targetLanguages(languages); + if (!targets.length) return ''; + const stored = localStorage.getItem(STORAGE_LANG); + if (stored && targets.some(l => l.languageCode === stored)) { + return stored; + } + if (selectedLang && targets.some(l => l.languageCode === selectedLang)) { + return selectedLang; + } + return targets[0].languageCode; +} + +function pickDefaultShowMode() { + const stored = localStorage.getItem(STORAGE_SHOW); + if (stored === 'missing' || stored === 'all' || stored === 'complete') { + return stored; + } + return 'missing'; +} + +function pickDefaultScope(sections, entries, lang) { + const stored = localStorage.getItem(STORAGE_SCOPE); + if (stored === '__all__' || sections.some(s => s.questionnaireID === stored)) { + return stored; + } + for (const sec of sections) { + const slice = sectionEntries(entries, sec); + if (countMissing(slice, lang) > 0) { + return sec.questionnaireID; + } + } + return sections[0]?.questionnaireID || '__app__'; +} + +function scopeLabel(sec, entries, lang) { + const slice = sectionEntries(entries, sec); + const missing = countMissing(slice, lang); + const suffix = missing ? ` (${missing} missing)` : ' (complete)'; + return `${sec.name}${suffix}`; +} + +function scopedSections() { + const sections = transData?.sections || []; + if (!selectedScope || selectedScope === '__all__') { + return sections; + } + return sections.filter(s => s.questionnaireID === selectedScope); +} + +function entriesInScope() { + const sections = scopedSections(); + const out = []; + for (const sec of sections) { + out.push(...sectionEntries(allEntries, sec)); + } + return out; +} + +function setSaveStatus(state) { + const el = document.getElementById('transSaveStatus'); + if (!el) return; + el.dataset.state = state; + if (state === 'saving') { + el.textContent = 'Saving…'; + } else if (state === 'error') { + el.textContent = 'Save failed'; + } else { + el.textContent = 'All changes saved'; + } +} + +async function ensureGermanLanguage() { + try { + await apiPut('translations.php', { type: 'language', languageCode: SOURCE_LANG, name: 'German' }); + } catch (_) { /* already exists */ } +} + +async function loadTranslations() { + const listArea = document.getElementById('transListArea'); + const langSelect = document.getElementById('transLangSelect'); + const filter = document.getElementById('transFilter'); + const typeFilter = document.getElementById('transTypeFilter'); + + if (listArea) listArea.innerHTML = '
        '; + saveTimers = {}; + + try { + await ensureGermanLanguage(); + const data = await apiGet('translations.php?all=1'); + transData = data; + const languages = transData.languages || []; + const questionnaires = transData.questionnaires || []; + + const flat = flattenAllQuestionnaires(questionnaires, data.appStrings || []); + allEntries = flat.allEntries; + transData.sections = flat.sections; + + selectedLang = pickDefaultTargetLang(languages); + showMode = pickDefaultShowMode(); + selectedScope = pickDefaultScope(flat.sections, allEntries, selectedLang); + + if (langSelect) { + const targets = targetLanguages(languages); + langSelect.disabled = !targets.length; + langSelect.innerHTML = targets.length + ? targets.map(l => { + const label = l.name ? `${l.name} (${l.languageCode})` : l.languageCode; + return ``; + }).join('') + : ''; + langSelect.onchange = () => { + selectedLang = langSelect.value; + localStorage.setItem(STORAGE_LANG, selectedLang); + populateScopeSelect(); + renderEntryList(); + }; + } + + const scopeSelect = document.getElementById('transScopeSelect'); + const showSelect = document.getElementById('transShowMode'); + if (scopeSelect) { + scopeSelect.disabled = false; + populateScopeSelect(); + scopeSelect.onchange = () => { + selectedScope = scopeSelect.value; + localStorage.setItem(STORAGE_SCOPE, selectedScope); + renderEntryList(); + }; + } + if (showSelect) { + showSelect.disabled = false; + showSelect.value = showMode; + showSelect.onchange = () => { + showMode = showSelect.value; + localStorage.setItem(STORAGE_SHOW, showMode); + applyFilters(); + }; + } + + if (filter) filter.disabled = false; + if (typeFilter) typeFilter.disabled = false; + + renderLanguagePanel(languages); + renderEntryList(); + bindFilters(); + setSaveStatus('saved'); + } catch (e) { + if (listArea) listArea.innerHTML = `

        ${esc(e.message)}

        `; + showToast(e.message, 'error'); + } +} + +function renderLanguagePanel(languages) { + const panel = document.getElementById('transLangPanel'); + if (!panel) return; + + const others = targetLanguages(languages); + + panel.innerHTML = ` +

        + German source text is edited in the questionnaire editor. Add other languages here. +

        +
        + + DE + German (source) + + ${others.map(l => ` + + ${esc(l.languageCode.toUpperCase())} + ${l.name ? `${esc(l.name)}` : ''} + + + `).join('')} +
        +
        + + + +
        + `; + + document.getElementById('addLangBtn')?.addEventListener('click', async () => { + const code = document.getElementById('newLangCode').value.trim().toLowerCase(); + const name = document.getElementById('newLangName').value.trim(); + if (!code) { showToast('Language code is required', 'error'); return; } + if (code === SOURCE_LANG) { + showToast('German (de) is always available as the source language', 'error'); + return; + } + if (languages.some(l => l.languageCode === code)) { + showToast('Language already exists', 'error'); + return; + } + try { + await apiPut('translations.php', { type: 'language', languageCode: code, name }); + showToast(`Language "${code}" added`, 'success'); + selectedLang = code; + await loadTranslations(); + } catch (e) { + showToast(e.message, 'error'); + } + }); + + panel.querySelectorAll('.lang-chip-remove').forEach(btn => { + btn.addEventListener('click', async () => { + const lc = btn.dataset.lc; + if (!(await confirmAction({ + title: 'Remove language', + message: `Remove "${lc}" and all its translations?`, + confirmLabel: 'Remove', + variant: 'danger', + }))) return; + try { + await apiDelete('translations.php', { type: 'language', languageCode: lc }); + showToast(`Language "${lc}" removed`, 'success'); + if (selectedLang === lc) selectedLang = ''; + await loadTranslations(); + } catch (e) { + showToast(e.message, 'error'); + } + }); + }); +} + +function populateScopeSelect() { + const scopeSelect = document.getElementById('transScopeSelect'); + if (!scopeSelect || !transData) return; + + const sections = transData.sections || []; + const options = sections.map(sec => + `` + ); + options.push(``); + + scopeSelect.innerHTML = options.join(''); + if (!selectedScope || (selectedScope !== '__all__' && !sections.some(s => s.questionnaireID === selectedScope))) { + selectedScope = pickDefaultScope(sections, allEntries, selectedLang); + scopeSelect.value = selectedScope; + localStorage.setItem(STORAGE_SCOPE, selectedScope); + } +} + +function renderEntryList() { + const listArea = document.getElementById('transListArea'); + if (!listArea || !transData) return; + + const languages = transData.languages || []; + const targets = targetLanguages(languages); + const sections = scopedSections(); + + if (!targets.length) { + listArea.innerHTML = ` +

        Add at least one language under More languages to translate into.

        `; + return; + } + + if (!selectedLang) { + listArea.innerHTML = `

        Choose a language above.

        `; + return; + } + + if (!transData.sections?.length) { + listArea.innerHTML = ` +

        No translatable content yet. Add questionnaires and questions in the editor first.

        `; + return; + } + + const sourceLabel = sourceLanguageLabel(languages); + const langLabel = targets.find(l => l.languageCode === selectedLang)?.name || selectedLang.toUpperCase(); + const scopeEntries = entriesInScope(); + const missingCount = countMissing(scopeEntries, selectedLang); + const pct = scopeEntries.length + ? Math.round(((scopeEntries.length - missingCount) / scopeEntries.length) * 100) + : 0; + + const headerOnce = translationListHeaderHTML(langLabel, sourceLabel); + const useQnDetails = selectedScope === '__all__'; + + const sectionsHtml = sections.map(sec => { + const entries = sectionEntries(allEntries, sec); + const groups = buildTranslationGroups(entries, selectedLang, sec.startIdx); + const secMissing = countMissing(entries, selectedLang); + const inner = renderCollapsibleGroupedTranslationsHTML(groups, selectedLang, sourceLabel, true, { + openGroupsWithMissing: selectedScope !== '__all__', + }); + + if (useQnDetails) { + const open = secMissing > 0 && sections.filter(s => + countMissing(sectionEntries(allEntries, s), selectedLang) > 0 + )[0]?.questionnaireID === sec.questionnaireID; + const summary = secMissing + ? `${sec.name} · ${secMissing} missing` + : `${sec.name} · complete`; + return ` +
        + ${esc(summary)} + ${inner} +
        `; + } + + return `
        ${inner}
        `; + }).join(''); + + listArea.innerHTML = ` +
        +
        + ${missingCount ? `${missingCount} missing · ` : ''}${scopeEntries.length} in scope + + + ${pct}% in ${esc(selectedLang.toUpperCase())} + +
        +
        + All changes saved + +
        +
        +
        + ${headerOnce} +
        ${sectionsHtml}
        +
        + `; + + document.getElementById('transJumpMissingBtn')?.addEventListener('click', jumpToNextMissing); + + bindEntryEditing(allEntries); + applyFilters(); +} + +function openAncestors(el) { + let node = el; + while (node) { + if (node.tagName === 'DETAILS') node.open = true; + node = node.parentElement; + } +} + +function jumpToNextMissing() { + const rows = [...document.querySelectorAll('#transGroupedRoot .trans-list-item')] + .filter(r => !r.classList.contains('trans-row-hidden') && r.dataset.missing === '1'); + if (!rows.length) { + showToast(showMode === 'missing' + ? 'All translations in this scope are complete' + : 'No missing translations in view', 'info'); + return; + } + + const activeRow = document.activeElement?.closest?.('.trans-list-item'); + let start = 0; + if (activeRow) { + const idx = rows.indexOf(activeRow); + if (idx >= 0) start = idx + 1; + } + const target = rows[start] || rows[0]; + openAncestors(target); + const inp = target.querySelector('[data-field="translation"]'); + inp?.focus({ preventScroll: false }); + target.scrollIntoView({ behavior: 'smooth', block: 'center' }); +} + +function bindEntryEditing(entries) { + document.querySelectorAll('#transGroupedRoot .trans-cell-input').forEach(inp => { + inp.addEventListener('input', () => { + const idx = parseInt(inp.dataset.idx, 10); + const entry = entries[idx]; + if (!entry) return; + + const isGerman = inp.dataset.field === 'german'; + if (isGerman) { + entry.germanText = inp.value; + if (!entry.translations || typeof entry.translations !== 'object') entry.translations = {}; + entry.translations[SOURCE_LANG] = inp.value; + } else { + if (!entry.translations || typeof entry.translations !== 'object') entry.translations = {}; + entry.translations[selectedLang] = inp.value; + } + + if (!isGerman) { + const missing = !inp.value.trim(); + inp.classList.toggle('trans-input-missing', missing); + const row = inp.closest('.trans-list-item'); + if (row) { + row.classList.toggle('trans-list-item-missing', missing); + row.dataset.missing = missing ? '1' : '0'; + } + } + + const timerKey = isGerman ? `${idx}_de` : `${idx}_${selectedLang}`; + clearTimeout(saveTimers[timerKey]); + saveTimers[timerKey] = setTimeout(() => saveTranslation(entry, inp, isGerman), 500); + setSaveStatus('saving'); + }); + }); +} + +async function saveTranslation(entry, inp, isGerman = false) { + savesInFlight++; + setSaveStatus('saving'); + try { + await apiPut('translations.php', { + type: entry.type, + id: entry.entityId, + languageCode: isGerman ? SOURCE_LANG : selectedLang, + text: inp.value, + }); + inp.classList.add('save-flash'); + setTimeout(() => inp.classList.remove('save-flash'), 400); + updateProgress(); + } catch (e) { + setSaveStatus('error'); + showToast(e.message, 'error'); + } finally { + savesInFlight = Math.max(0, savesInFlight - 1); + if (savesInFlight === 0) { + setSaveStatus('saved'); + populateScopeSelect(); + } + } +} + +function updateProgress() { + const scopeEntries = entriesInScope(); + const missing = countMissing(scopeEntries, selectedLang); + const pct = scopeEntries.length + ? Math.round(((scopeEntries.length - missing) / scopeEntries.length) * 100) + : 0; + const fill = document.querySelector('.trans-progress-fill'); + if (fill) fill.style.width = `${pct}%`; + const label = document.querySelector('.trans-progress-label'); + if (label) label.textContent = `${pct}% in ${selectedLang.toUpperCase()}`; + if (!document.getElementById('transFilter')?.value && !document.getElementById('transTypeFilter')?.value) { + updateVisibleCountSummary(); + } +} + +function updateVisibleCountSummary(visible = null, visibleMissing = null) { + const countEl = document.getElementById('transVisibleCount'); + if (!countEl) return; + const scopeEntries = entriesInScope(); + const text = document.getElementById('transFilter')?.value || ''; + const type = document.getElementById('transTypeFilter')?.value || ''; + if (text || type || showMode !== 'missing') { + if (visible != null) { + countEl.textContent = `Showing ${visible}${visibleMissing ? ` (${visibleMissing} missing)` : ''}`; + } + return; + } + const missing = countMissing(scopeEntries, selectedLang); + countEl.textContent = `${missing ? `${missing} missing · ` : ''}${scopeEntries.length} in scope`; +} + +let filtersBound = false; + +function bindFilters() { + if (filtersBound) { + applyFilters(); + return; + } + filtersBound = true; + + document.getElementById('transFilter')?.addEventListener('input', applyFilters); + document.getElementById('transTypeFilter')?.addEventListener('change', applyFilters); +} + +function applyFilters() { + const text = (document.getElementById('transFilter')?.value || '').toLowerCase(); + const type = document.getElementById('transTypeFilter')?.value || ''; + const items = document.querySelectorAll('#transGroupedRoot .trans-list-item'); + let visible = 0; + let visibleMissing = 0; + + items.forEach(row => { + const key = row.dataset.key?.toLowerCase() || ''; + const label = row.dataset.label?.toLowerCase() + || row.querySelector('.trans-key-text')?.textContent?.toLowerCase() || ''; + const rowType = row.dataset.type || ''; + const german = row.querySelector('.trans-source-text')?.textContent?.toLowerCase() + || row.querySelector('[data-field="german"]')?.value?.toLowerCase() || ''; + const val = row.querySelector('[data-field="translation"]')?.value?.toLowerCase() || ''; + const isMissing = row.dataset.missing === '1'; + let show = (!type || rowType === type) && + (!text || key.includes(text) || label.includes(text) || german.includes(text) || val.includes(text)); + if (showMode === 'missing' && !isMissing) show = false; + if (showMode === 'complete' && isMissing) show = false; + row.classList.toggle('trans-row-hidden', !show); + if (show) { + visible++; + if (row.dataset.missing === '1') visibleMissing++; + } + }); + + document.querySelectorAll('#transGroupedRoot .trans-group-details').forEach(group => { + const rows = group.querySelectorAll('.trans-list-item'); + const anyVisible = [...rows].some(r => !r.classList.contains('trans-row-hidden')); + group.classList.toggle('trans-group-hidden', !anyVisible); + }); + + document.querySelectorAll('#transGroupedRoot .trans-scope-block, #transGroupedRoot .trans-qn-section').forEach(block => { + const anyVisible = [...block.querySelectorAll('.trans-list-item')].some(r => !r.classList.contains('trans-row-hidden')); + block.classList.toggle('trans-qn-hidden', !anyVisible); + }); + + const emptyEl = document.getElementById('transEmptyHint'); + if (emptyEl) emptyEl.remove(); + if (visible === 0 && items.length > 0) { + const root = document.getElementById('transGroupedRoot'); + if (root) { + const hint = document.createElement('p'); + hint.id = 'transEmptyHint'; + hint.className = 'text-muted trans-hint trans-empty-hint'; + hint.textContent = showMode === 'missing' + ? 'All translations in this scope are complete for the selected language.' + : 'No rows match the current filters.'; + root.insertAdjacentElement('afterend', hint); + } + } + + updateVisibleCountSummary(visible, visibleMissing); +} + +function bindTranslationBundleActions() { + const fileInput = document.getElementById('importTransBundleFile'); + const importBtn = document.getElementById('importTransBundleBtn'); + const exportBtn = document.getElementById('exportTransBundleBtn'); + if (!fileInput || !importBtn || !exportBtn) return; + + exportBtn.addEventListener('click', async () => { + exportBtn.disabled = true; + const prev = exportBtn.textContent; + exportBtn.textContent = 'Preparing…'; + try { + const res = await apiDownloadFetch(apiUrl('translations?exportBundle=1')); + if (!res.ok) { + const err = await res.json().catch(() => ({})); + throw new Error(err.error?.message || err.error || 'Export failed'); + } + const blob = await res.blob(); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + const stamp = new Date().toISOString().slice(0, 10); + a.download = `translations_bundle_${stamp}.json`; + a.click(); + URL.revokeObjectURL(url); + showToast('Translations bundle downloaded', 'success'); + } catch (e) { + showToast(e.message, 'error'); + } finally { + exportBtn.disabled = false; + exportBtn.textContent = prev; + } + }); + + importBtn.addEventListener('click', () => fileInput.click()); + + fileInput.addEventListener('change', async () => { + const file = fileInput.files?.[0]; + fileInput.value = ''; + if (!file) return; + + let bundle; + try { + bundle = JSON.parse(await file.text()); + } catch { + showToast('Invalid JSON file', 'error'); + return; + } + + if (!bundle?.entries || !Array.isArray(bundle.entries)) { + showToast('Not a translations bundle (missing entries array)', 'error'); + return; + } + + const count = bundle.entries.length; + if (!(await confirmAction({ + title: 'Import translations', + message: `Import translations for ${count} entries from this file?`, + confirmLabel: 'Import', + }))) return; + + importBtn.disabled = true; + const prev = importBtn.textContent; + importBtn.textContent = 'Importing…'; + try { + const result = await apiPost('translations.php', { + action: 'importBundle', + bundle, + }); + const imported = result.imported ?? 0; + const skipped = result.skipped ?? 0; + let msg = `Imported ${imported} translation(s)`; + if (skipped) msg += ` (${skipped} skipped)`; + showToast(msg, 'success'); + await loadTranslations(); + } catch (e) { + showToast(e.message, 'error'); + } finally { + importBtn.disabled = false; + importBtn.textContent = prev; + } + }); +} diff --git a/website/js/pages/users.js b/website/js/pages/users.js new file mode 100644 index 0000000..9f8a537 --- /dev/null +++ b/website/js/pages/users.js @@ -0,0 +1,562 @@ +import { apiGet, apiPost, apiPut, apiDelete } from '../api.js'; +import { getRole, getUser, pageHeaderHTML, showToast } from '../app.js'; +import { confirmAction } from '../confirm-modal.js'; +import { openAdminResetPasswordModal } from '../password-modal.js'; + +// Roles an admin can create; supervisors can only create coaches +const CREATEABLE_ROLES = { + admin: ['admin', 'supervisor', 'coach'], + supervisor: ['coach'], +}; + +let usersList = []; +let supervisorsList = []; +let callerRole = ''; +/** @type {Record} */ +let filterSearchByRole = { admin: '', supervisor: '', coach: '' }; + +function roleGroupsForCaller() { + return callerRole === 'supervisor' + ? [{ label: 'Counselor', key: 'coach' }] + : [ + { label: 'Admins', key: 'admin' }, + { label: 'Supervisors', key: 'supervisor' }, + { label: 'Counselor', key: 'coach' }, + ]; +} + +function searchPlaceholderForRole(roleKey) { + return roleKey === 'coach' + ? 'Search username or supervisor…' + : 'Search username or location…'; +} + +function usersByRoleMap() { + const byRole = { admin: [], supervisor: [], coach: [] }; + usersList.forEach(u => { + if (byRole[u.role]) byRole[u.role].push(u); + else (byRole.other = byRole.other || []).push(u); + }); + return byRole; +} + +export async function usersPage() { + callerRole = getRole(); + const app = document.getElementById('app'); + + app.innerHTML = ` + ${pageHeaderHTML('Users', ` + + + `)} + +
        + `; + + document.getElementById('showCreateFormBtn').addEventListener('click', () => { + const wrapper = document.getElementById('createUserWrapper'); + if (wrapper.style.display === 'none') { + showCreateForm(); + } else { + hideCreateForm(); + } + }); + document.getElementById('downloadUsersBtn').addEventListener('click', downloadUsersCSV); + + await loadUsers(); +} + +document.addEventListener('qdb:password-reset', (e) => { + const { userID, mustChangePassword } = e.detail || {}; + const u = usersList.find(x => x.userID === userID); + if (u) { + u.mustChangePassword = mustChangePassword; + renderUsers(); + } +}); + +async function loadUsers() { + try { + const data = await apiGet('users.php'); + usersList = data.users || []; + supervisorsList = data.supervisors || []; + callerRole = data.callerRole || callerRole; + renderUsers(); + const dlBtn = document.getElementById('downloadUsersBtn'); + if (dlBtn) dlBtn.disabled = !usersList.length; + } catch (e) { + showToast(e.message, 'error'); + document.getElementById('usersContent').innerHTML = + `

        ${esc(e.message)}

        `; + } +} + +// ── User table ──────────────────────────────────────────────────────────── + +function renderUsers() { + const container = document.getElementById('usersContent'); + const myUsername = getUser(); + + if (!usersList.length) { + container.innerHTML = ` +
        +
        +

        No users found

        +

        ${callerRole === 'supervisor' ? 'You have no counselors yet.' : 'Create the first user above.'}

        +
        +
        `; + return; + } + + const byRole = usersByRoleMap(); + const groups = roleGroupsForCaller(); + + container.innerHTML = groups.map(group => { + const allInRole = byRole[group.key] || []; + if (!allInRole.length) return ''; + return ` +
        +

        + ${group.label} (0) +

        +
        + +
        +
        + + + + + ${group.key !== 'coach' ? '' : ''} + + + + + + +
        UsernameLocationSupervisorMust change passwordCreatedActions
        +
        +
        `; + }).join(''); + + container.addEventListener('input', (e) => { + const input = e.target.closest('.user-role-search'); + if (!input) return; + const roleKey = input.dataset.role; + if (!roleKey) return; + filterSearchByRole[roleKey] = input.value; + updateRoleGroupTable(roleKey, myUsername); + }); + + groups.forEach(group => { + if ((byRole[group.key] || []).length) { + updateRoleGroupTable(group.key, myUsername); + } + }); +} + +function filterUsers(users, roleKey) { + let list = users; + const q = (filterSearchByRole[roleKey] || '').trim().toLowerCase(); + if (q) { + list = list.filter(u => { + const hay = [u.username, u.location, u.supervisorUsername].filter(Boolean).join(' ').toLowerCase(); + return hay.includes(q); + }); + } + return list; +} + +function updateRoleGroupTable(roleKey, myUsername) { + const tbody = document.getElementById(`usersBody-${roleKey}`); + const countEl = document.querySelector(`[data-role-count="${roleKey}"]`); + if (!tbody) return; + + const byRole = usersByRoleMap(); + const allInRole = byRole[roleKey] || []; + const users = filterUsers(allInRole, roleKey); + const colSpan = 5; + + if (countEl) { + const q = (filterSearchByRole[roleKey] || '').trim(); + countEl.textContent = q && users.length !== allInRole.length + ? `${users.length} of ${allInRole.length}` + : String(users.length); + } + + if (!users.length) { + tbody.innerHTML = ` + + + ${allInRole.length ? 'No users match' : 'No users'} + + `; + return; + } + + tbody.innerHTML = users.map(u => userRowHTML(u, myUsername)).join(''); + tbody.querySelectorAll('.delete-user-btn').forEach(btn => { + btn.addEventListener('click', () => deleteUser(btn.dataset.id, btn.dataset.name)); + }); + tbody.querySelectorAll('.reset-password-btn').forEach(btn => { + btn.addEventListener('click', () => openAdminResetPasswordModal({ + userID: btn.dataset.id, + username: btn.dataset.name, + role: btn.dataset.role || '', + })); + }); + tbody.querySelectorAll('.revoke-sessions-btn').forEach(btn => { + btn.addEventListener('click', () => revokeUserSessions(btn.dataset.id, btn.dataset.name)); + }); + tbody.querySelectorAll('.coach-supervisor-select').forEach(sel => { + sel.addEventListener('change', () => reassignCoachSupervisor(sel)); + }); +} + +function supervisorSelectHTML(u) { + if (!supervisorsList.length) { + return 'No supervisors'; + } + const cur = u.supervisorID || ''; + const opts = supervisorsList.map(s => { + const label = s.location ? `${s.username} (${s.location})` : s.username; + const selected = s.supervisorID === cur ? ' selected' : ''; + return ``; + }).join(''); + return ``; +} + +function userRowHTML(u, myUsername) { + const isSelf = u.username === myUsername; + let detail; + if (u.role === 'coach') { + detail = callerRole === 'admin' + ? supervisorSelectHTML(u) + : esc(u.supervisorUsername || '—'); + } else { + detail = esc(u.location || '—'); + } + const created = u.createdAt + ? new Date(parseInt(u.createdAt, 10) * 1000).toLocaleDateString() + : '—'; + const pwBadge = u.mustChangePassword == 1 + ? 'Temporary' + : ''; + + const canDelete = !isSelf && ( + callerRole === 'admin' || + (callerRole === 'supervisor' && u.role === 'coach') + ); + const canResetPassword = !isSelf && ( + (callerRole === 'admin' && (u.role === 'supervisor' || u.role === 'coach')) || + (callerRole === 'supervisor' && u.role === 'coach') + ); + const canRevokeSessions = !isSelf && ( + callerRole === 'admin' || + (callerRole === 'supervisor' && u.role === 'coach') + ); + + const actions = []; + if (canResetPassword) { + actions.push( + `` + ); + } + if (canRevokeSessions) { + actions.push( + `` + ); + } + if (canDelete) { + actions.push( + `` + ); + } + + return ` + + + ${esc(u.username)} + ${isSelf ? 'You' : ''} + + ${detail} + ${pwBadge} + ${created} + ${actions.join(' ')} + `; +} + +async function reassignCoachSupervisor(selectEl) { + const userID = selectEl.dataset.userId; + const username = selectEl.dataset.username || ''; + const prev = selectEl.dataset.prev || ''; + const supervisorID = selectEl.value; + + if (supervisorID === prev) { + return; + } + + selectEl.disabled = true; + try { + const data = await apiPut('users.php', { userID, supervisorID }); + const u = usersList.find(x => x.userID === userID); + if (u) { + u.supervisorID = data.supervisorID ?? supervisorID; + u.supervisorUsername = data.supervisorUsername ?? ''; + } + selectEl.dataset.prev = supervisorID; + showToast(`Counselor "${username}" assigned to ${data.supervisorUsername || 'supervisor'}`, 'success'); + } catch (e) { + selectEl.value = prev; + showToast(e.message, 'error'); + } finally { + selectEl.disabled = false; + } +} + +async function revokeUserSessions(userID, username) { + if (!(await confirmAction({ + title: 'Sign out everywhere', + message: `Sign out "${username}" on all devices?\n\nThey must log in again on the website and mobile app.`, + confirmLabel: 'Sign out', + variant: 'danger', + }))) { + return; + } + try { + const data = await apiPut('users.php', { action: 'revokeSessions', userID }); + if (!data.success) throw new Error(data.error || 'Failed'); + const n = data.revokedSessions ?? 0; + showToast(`Signed out "${username}" (${n} session${n === 1 ? '' : 's'} revoked)`, 'success'); + } catch (e) { + showToast(e.message, 'error'); + } +} + +async function deleteUser(userID, username) { + if (!(await confirmAction({ + title: 'Delete user', + message: `Delete user "${username}"? This cannot be undone.`, + confirmLabel: 'Delete', + variant: 'danger', + }))) return; + try { + const data = await apiDelete('users.php', { userID }); + if (data.success) { + usersList = usersList.filter(u => u.userID !== userID); + showToast(`User "${username}" deleted`, 'success'); + renderUsers(); + } + } catch (e) { + showToast(e.message, 'error'); + } +} + +// ── Create user form ────────────────────────────────────────────────────── + +function showCreateForm() { + const wrapper = document.getElementById('createUserWrapper'); + const allowedRoles = CREATEABLE_ROLES[callerRole] || ['coach']; + const isSupervisor = callerRole === 'supervisor'; + + wrapper.style.display = ''; + wrapper.innerHTML = ` +
        +

        Create New User

        +
        +
        + + +
        +
        + + +
        +
        + + ${isSupervisor + ? `` + : `` + } +
        +
        + +
        + + +
        + +
        + + ${isSupervisor + ? `` + : `` + } +
        + + + +
        + + +
        + +
        + `; + + document.getElementById('cu_username').focus(); + + // Show/hide location vs supervisor field based on role selection (admin only) + if (!isSupervisor) { + document.getElementById('cu_role').addEventListener('change', (e) => { + const r = e.target.value; + document.getElementById('cu_location_row').style.display = r !== 'coach' ? '' : 'none'; + document.getElementById('cu_supervisor_row').style.display = r === 'coach' ? '' : 'none'; + }); + // Set initial state based on default selection + const initRole = document.getElementById('cu_role').value; + document.getElementById('cu_location_row').style.display = initRole !== 'coach' ? '' : 'none'; + document.getElementById('cu_supervisor_row').style.display = initRole === 'coach' ? '' : 'none'; + } + + document.getElementById('cu_submit').addEventListener('click', submitCreateUser); + document.getElementById('cu_cancel').addEventListener('click', hideCreateForm); + + // Enter key on last visible field submits + wrapper.querySelectorAll('input').forEach(inp => { + inp.addEventListener('keydown', (e) => { + if (e.key === 'Enter') submitCreateUser(); + if (e.key === 'Escape') hideCreateForm(); + }); + }); +} + +function hideCreateForm() { + const wrapper = document.getElementById('createUserWrapper'); + if (wrapper) { wrapper.style.display = 'none'; wrapper.innerHTML = ''; } +} + +async function submitCreateUser() { + const errEl = document.getElementById('cu_error'); + errEl.style.display = 'none'; + + const isSupervisor = callerRole === 'supervisor'; + const username = document.getElementById('cu_username').value.trim(); + const password = document.getElementById('cu_password').value; + const role = isSupervisor ? 'coach' : (document.getElementById('cu_role').value || 'coach'); + const mustChange = 1; + + let location = ''; + let supervisorID = ''; + if (!isSupervisor) { + if (role !== 'coach') { + location = document.getElementById('cu_location').value.trim(); + } else { + supervisorID = document.getElementById('cu_supervisor_select').value; + } + } + + if (!username) { showError(errEl, 'Username is required'); return; } + if (password.length < 6) { showError(errEl, 'Password must be at least 6 characters'); return; } + if (role === 'coach' && !isSupervisor && !supervisorID) { + showError(errEl, 'Please select a supervisor for this counselor'); + return; + } + + const btn = document.getElementById('cu_submit'); + btn.disabled = true; + btn.textContent = 'Creating...'; + + try { + const data = await apiPost('users.php', { + username, password, role, location, supervisorID, mustChangePassword: mustChange, + }); + if (!data.success) throw new Error(data.error || 'Failed to create user'); + + // Add to local list and re-render + usersList.push({ + userID: data.userID, + username: data.username, + role: data.role, + entityID: data.entityID, + location: data.location || '', + supervisorID: data.supervisorID || null, + supervisorUsername: data.supervisorUsername || null, + mustChangePassword: data.mustChangePassword, + createdAt: data.createdAt, + }); + showToast(`User "${data.username}" created`, 'success'); + hideCreateForm(); + renderUsers(); + } catch (e) { + showError(errEl, e.message); + btn.disabled = false; + btn.textContent = 'Create User'; + } +} + +function showError(el, msg) { + el.textContent = msg; + el.style.display = ''; +} + +function downloadUsersCSV() { + if (!usersList.length) { + showToast('No users to export', 'error'); + return; + } + + const headers = ['username', 'role', 'location', 'supervisor', 'mustChangePassword', 'createdAt', 'userID']; + const rows = usersList.map(u => [ + u.username, + u.role === 'coach' ? 'Counselor' : u.role, + u.role === 'coach' ? '' : (u.location || ''), + u.role === 'coach' ? (u.supervisorUsername || u.supervisorID || '') : '', + u.mustChangePassword == 1 ? 'yes' : 'no', + u.createdAt ? new Date(parseInt(u.createdAt, 10) * 1000).toISOString() : '', + u.userID, + ]); + + let csv = '\uFEFF'; + csv += headers.map(csvEsc).join(',') + '\n'; + rows.forEach(r => { csv += r.map(csvEsc).join(',') + '\n'; }); + + const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `users_${new Date().toISOString().slice(0, 10)}.csv`; + a.click(); + URL.revokeObjectURL(url); + showToast('Users exported', 'success'); +} + +function csvEsc(val) { + const s = String(val ?? ''); + if (s.includes(',') || s.includes('"') || s.includes('\n')) { + return '"' + s.replace(/"/g, '""') + '"'; + } + return s; +} + +function esc(s) { + const d = document.createElement('div'); + d.textContent = s ?? ''; + return d.innerHTML; +} diff --git a/website/js/password-modal.js b/website/js/password-modal.js new file mode 100644 index 0000000..8fb35a3 --- /dev/null +++ b/website/js/password-modal.js @@ -0,0 +1,208 @@ +import { apiPatch, apiPost, invalidateSessionCheck } from './api.js'; +import { getRole, getUser, showToast, formatRoleLabel } from './app.js'; + +/** @typedef {'admin_reset' | 'self'} PasswordModalMode */ + +/** @type {{ mode: PasswordModalMode, userID?: string, username?: string, role?: string } | null} */ +let modalContext = null; + +function esc(s) { + const d = document.createElement('div'); + d.textContent = s ?? ''; + return d.innerHTML; +} + +function roleLabel(role) { + return formatRoleLabel(role); +} + +function signInHintForRole(role) { + if (role === 'coach') { + return 'Counselors sign in through the mobile app and will be prompted to choose a new password there.'; + } + return 'Supervisors sign in through this website and will be prompted to choose a new password at login.'; +} + +function ensurePasswordModal() { + let modal = document.getElementById('passwordModal'); + if (modal) return modal; + + modal = document.createElement('div'); + modal.id = 'passwordModal'; + modal.className = 'modal-overlay'; + modal.hidden = true; + modal.setAttribute('role', 'dialog'); + modal.setAttribute('aria-modal', 'true'); + modal.setAttribute('aria-labelledby', 'passwordModalTitle'); + modal.innerHTML = ` + + `; + document.body.appendChild(modal); + + modal.addEventListener('click', (e) => { + if (e.target === modal) closePasswordModal(); + }); + modal.querySelector('[data-password-cancel]').addEventListener('click', closePasswordModal); + modal.querySelector('[data-password-submit]').addEventListener('click', submitPasswordModal); + modal.querySelector('#pw_new_password_confirm').addEventListener('keydown', (e) => { + if (e.key === 'Enter') submitPasswordModal(); + }); + + document.addEventListener('keydown', (e) => { + const el = document.getElementById('passwordModal'); + if (!el || el.hidden) return; + if (e.key === 'Escape') closePasswordModal(); + }); + + return modal; +} + +/** + * @param {{ mode: PasswordModalMode, userID?: string, username?: string, role?: string }} options + */ +function openPasswordModal(options) { + modalContext = options; + const modal = ensurePasswordModal(); + const isSelf = options.mode === 'self'; + const username = options.username || getUser(); + const role = options.role || getRole(); + + modal.querySelector('#passwordModalTitle').textContent = isSelf ? 'Change your password' : 'Reset password'; + modal.querySelector('#passwordModalSubtitle').innerHTML = isSelf + ? `Choose a new password for ${esc(username)} (${esc(roleLabel(role))}).` + : `Set a new password for ${esc(username)} (${esc(roleLabel(role))}).`; + modal.querySelector('#passwordModalHint').textContent = isSelf + ? 'You will stay signed in after saving. Use this password on your next login.' + : signInHintForRole(role); + + modal.querySelector('#passwordModalTempHint').style.display = isSelf ? 'none' : ''; + modal.querySelector('#passwordModalOldGroup').style.display = isSelf ? '' : 'none'; + + modal.querySelector('#pw_old_password').value = ''; + modal.querySelector('#pw_new_password').value = ''; + modal.querySelector('#pw_new_password_confirm').value = ''; + const errEl = modal.querySelector('#passwordModalError'); + errEl.style.display = 'none'; + errEl.textContent = ''; + + modal.querySelector('[data-password-submit]').textContent = isSelf ? 'Change password' : 'Set password'; + + modal.hidden = false; + document.body.classList.add('modal-open'); + (isSelf ? modal.querySelector('#pw_old_password') : modal.querySelector('#pw_new_password')).focus(); +} + +export function closePasswordModal() { + const modal = document.getElementById('passwordModal'); + if (modal) modal.hidden = true; + document.body.classList.remove('modal-open'); + modalContext = null; +} + +export function openAdminResetPasswordModal({ userID, username, role }) { + openPasswordModal({ mode: 'admin_reset', userID, username, role }); +} + +export function openChangeOwnPasswordModal() { + openPasswordModal({ mode: 'self' }); +} + +function showModalError(el, msg) { + el.textContent = msg; + el.style.display = ''; +} + +async function submitPasswordModal() { + if (!modalContext) return; + const modal = document.getElementById('passwordModal'); + const errEl = modal.querySelector('#passwordModalError'); + errEl.style.display = 'none'; + + const newPassword = modal.querySelector('#pw_new_password').value; + const confirm = modal.querySelector('#pw_new_password_confirm').value; + + if (newPassword.length < 6) { + showModalError(errEl, 'Password must be at least 6 characters'); + return; + } + if (newPassword !== confirm) { + showModalError(errEl, 'Passwords do not match'); + return; + } + + const submitBtn = modal.querySelector('[data-password-submit]'); + submitBtn.disabled = true; + const prevLabel = submitBtn.textContent; + submitBtn.textContent = 'Saving…'; + + try { + if (modalContext.mode === 'self') { + const oldPassword = modal.querySelector('#pw_old_password').value; + if (!oldPassword) { + showModalError(errEl, 'Current password is required'); + return; + } + const data = await apiPost('auth/change-password', { + username: getUser(), + old_password: oldPassword, + new_password: newPassword, + }); + if (!data.success) throw new Error(data.error || 'Failed to change password'); + if (data.token) localStorage.setItem('qdb_token', data.token); + if (data.user) localStorage.setItem('qdb_user', data.user); + if (data.role) localStorage.setItem('qdb_role', data.role); + invalidateSessionCheck(); + closePasswordModal(); + showToast('Your password was changed', 'success'); + return; + } + + const { userID, username } = modalContext; + + const data = await apiPatch('users.php', { + userID, + password: newPassword, + mustChangePassword: 1, + }); + if (!data.success) throw new Error(data.error || 'Failed to reset password'); + + closePasswordModal(); + showToast( + `Temporary password set for "${username}" — they must change it on next sign-in`, + 'success' + ); + + document.dispatchEvent(new CustomEvent('qdb:password-reset', { + detail: { userID, mustChangePassword: data.mustChangePassword ?? 1 }, + })); + } catch (e) { + showModalError(errEl, e.message); + } finally { + submitBtn.disabled = false; + submitBtn.textContent = prevLabel; + } +} diff --git a/website/js/questionnaire-preview.js b/website/js/questionnaire-preview.js new file mode 100644 index 0000000..5ea2e06 --- /dev/null +++ b/website/js/questionnaire-preview.js @@ -0,0 +1,555 @@ +/** + * All-in-one interactive preview for questionnaire editing. + * All visible questions on one scrollable page; branching updates visibility live. + * Answers stay in memory only — nothing is saved to the server. + */ + +const GLASS_LABELS = [ + { key: 'never_glass', label: 'Never' }, + { key: 'little_glass', label: 'A little' }, + { key: 'moderate_glass', label: 'Moderate' }, + { key: 'much_glass', label: 'Much' }, + { key: 'extreme_glass', label: 'Extreme' }, +]; + +/** Sample codes shown in preview (read-only), mimicking the app after load/login. */ +const PREVIEW_CLIENT_CODE = 'CLIENT-042'; +const PREVIEW_COACH_CODE = 'coach.demo'; + +const MONTHS = [ + 'January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December', +]; + +function esc(s) { + const d = document.createElement('div'); + d.textContent = s ?? ''; + return d.innerHTML; +} + +function parseConfig(q) { + try { return JSON.parse(q.configJson || '{}'); } catch { return {}; } +} + +function localId(q) { + return q.localId || (q.questionID || '').split('__').pop() || ''; +} + +function questionKey(q) { + const cfg = parseConfig(q); + return (q.questionKey || cfg.questionKey || '').trim(); +} + +function optionKeyOf(o) { + if (o.optionKey) return o.optionKey; + const dt = (o.defaultText || '').trim(); + return /^[a-zA-Z][a-zA-Z0-9_]*$/.test(dt) ? dt : dt; +} + +function optionLabel(o) { + return (o.labelGerman || '').trim() || o.defaultText || o.optionKey || '?'; +} + +function glassScaleOptions(q) { + const opts = q.answerOptions || []; + if (opts.length) { + return opts.map(o => ({ key: optionKeyOf(o), label: optionLabel(o) })); + } + return GLASS_LABELS; +} + +function valueSpinnerValues(config) { + const range = config.range || { min: 0, max: 10 }; + const min = Number(range.min ?? 0); + const max = Number(range.max ?? 10); + const lo = Math.min(min, max); + const hi = Math.max(min, max); + const values = []; + for (let v = lo; v <= hi; v++) values.push(v); + return values; +} + +function hasBranchingOptions(q) { + const layout = q.type || ''; + const config = parseConfig(q); + if (layout === 'radio_question') { + return (q.answerOptions || []).some(o => o.nextQuestionId?.trim()); + } + if (layout === 'value_spinner') { + const opts = config.valueOptions || config.options || []; + return opts.some(o => o.nextQuestionId?.trim()) || Boolean(config.nextQuestionId?.trim()); + } + if (layout === 'string_spinner') { + return Boolean(config.nextQuestionId?.trim() || config.otherNextQuestionId?.trim()); + } + if (layout === 'multi_check_box_question') { + return Boolean(config.nextQuestionId?.trim() || config.otherNextQuestionId?.trim()); + } + const withQuestionNext = new Set([ + 'free_text', 'date_spinner', 'slider_question', 'glass_scale_question', + 'client_coach_code_question', + ]); + return withQuestionNext.has(layout) && Boolean(config.nextQuestionId?.trim()); +} + +function resolveNextLocalId(q, answer) { + const layout = q.type || ''; + const config = parseConfig(q); + + if (layout === 'radio_question') { + const key = typeof answer === 'string' ? answer : ''; + const opt = (q.answerOptions || []).find(o => optionKeyOf(o) === key); + return opt?.nextQuestionId?.trim() || null; + } + if (layout === 'value_spinner') { + const num = parseInt(String(answer), 10); + const branch = (config.valueOptions || config.options || []) + .find(o => Number(o.value) === num); + if (branch?.nextQuestionId?.trim()) return branch.nextQuestionId.trim(); + return config.nextQuestionId?.trim() || null; + } + if (layout === 'string_spinner') { + const val = String(answer ?? ''); + const otherKey = config.otherOptionKey?.trim(); + const otherNext = config.otherNextQuestionId?.trim(); + if (otherKey && otherNext && val === otherKey) return otherNext; + return config.nextQuestionId?.trim() || null; + } + if (layout === 'multi_check_box_question') { + const keys = Array.isArray(answer) ? answer : []; + const otherKey = config.otherOptionKey?.trim(); + const otherNext = config.otherNextQuestionId?.trim(); + if (otherKey && otherNext && keys.includes(otherKey)) return otherNext; + return config.nextQuestionId?.trim() || null; + } + return config.nextQuestionId?.trim() || null; +} + +function readAnswerFromBody(body, q) { + if (!body) return null; + const layout = q.type || ''; + const config = parseConfig(q); + + switch (layout) { + case 'radio_question': { + const checked = body.querySelector('input[type=radio]:checked'); + return checked ? checked.value : null; + } + case 'multi_check_box_question': { + const boxes = body.querySelectorAll('input[type=checkbox]:checked'); + return [...boxes].map(el => el.value); + } + case 'value_spinner': + case 'string_spinner': { + const sel = body.querySelector('select.qp-select'); + const val = sel?.value ?? ''; + return val === '' ? null : val; + } + case 'slider_question': { + const range = body.querySelector('input[type=range]'); + return range ? range.value : null; + } + case 'date_spinner': { + const precision = config.precision || 'full'; + const year = body.querySelector('[data-part=year]')?.value; + if (!year) return null; + if (precision === 'year') return year; + const month = body.querySelector('[data-part=month]')?.value || '01'; + if (precision === 'year_month') return `${year}-${month}`; + const day = body.querySelector('[data-part=day]')?.value || '01'; + return `${year}-${month}-${day}`; + } + case 'free_text': { + const text = body.querySelector('input[type=text], textarea')?.value?.trim(); + return text || null; + } + case 'glass_scale_question': { + const symptoms = config.symptoms || []; + if (!symptoms.length) return {}; + const result = {}; + for (const sym of symptoms) { + const picked = body.querySelector(`input[data-symptom="${CSS.escape(sym)}"]:checked`); + if (!picked) return null; + result[sym] = picked.value; + } + return result; + } + case 'client_coach_code_question': { + const client = body.querySelector('[data-field=client]')?.value?.trim(); + const coach = body.querySelector('[data-field=coach]')?.value?.trim(); + if (!client && !coach) return null; + return { client_code: client || '', coach_code: coach || '' }; + } + case 'last_page': + return true; + default: + return null; + } +} + +function hasAnswerInUi(q, sectionEl) { + const body = sectionEl?.querySelector('.qp-question-body'); + const layout = q.type || ''; + const config = parseConfig(q); + const answer = readAnswerFromBody(body, q); + + switch (layout) { + case 'radio_question': + return answer !== null; + case 'multi_check_box_question': { + const min = config.minSelection || 0; + const count = Array.isArray(answer) ? answer.length : 0; + return count >= Math.max(min, 0) && (count > 0 || !q.isRequired); + } + case 'value_spinner': + case 'string_spinner': + return answer !== null && answer !== ''; + case 'slider_question': + return answer !== null; + case 'date_spinner': { + const year = body?.querySelector('[data-part=year]')?.value; + if (!year) return false; + if (config.precision === 'year') return true; + const month = body?.querySelector('[data-part=month]')?.value; + if (!month) return false; + if (config.precision === 'year_month') return true; + return Boolean(body?.querySelector('[data-part=day]')?.value); + } + case 'free_text': + return Boolean(answer); + case 'glass_scale_question': { + const symptoms = config.symptoms || []; + if (!symptoms.length) return true; + return answer !== null && typeof answer === 'object' + && symptoms.every(s => answer[s]); + } + case 'client_coach_code_question': + return Boolean(answer?.client_code && answer?.coach_code); + case 'last_page': + return true; + default: + return true; + } +} + +function branchAnswerFromSection(q, sectionEl) { + if (hasBranchingOptions(q) && !hasAnswerInUi(q, sectionEl)) return null; + const body = sectionEl?.querySelector('.qp-question-body'); + return readAnswerFromBody(body, q); +} + +function visibleQuestionIndices(questions, sectionByIdx) { + const visible = new Set(); + let i = 0; + while (i < questions.length) { + const q = questions[i]; + if (q.type === 'last_page') { + i++; + continue; + } + visible.add(i); + const sectionEl = sectionByIdx.get(i); + const answer = sectionEl ? branchAnswerFromSection(q, sectionEl) : null; + const nextId = answer !== null ? resolveNextLocalId(q, answer) : null; + + if (nextId) { + const isLastPageTarget = questions.some( + qq => qq.type === 'last_page' && localId(qq) === nextId, + ); + if (isLastPageTarget) break; + const target = questions.findIndex(qq => localId(qq) === nextId); + i = target >= 0 ? target : i + 1; + } else if (hasBranchingOptions(q) && answer === null) { + break; + } else { + i++; + } + } + return visible; +} + +function symptomRowLabel(q, symptomKey) { + const rows = q.glassSymptoms || []; + const row = rows.find(r => r.key === symptomKey); + return (row?.labelDe || '').trim() || symptomKey; +} + +function renderQuestionBody(q, sectionIdx) { + const layout = q.type || ''; + const config = parseConfig(q); + const qText = q.defaultText || questionKey(q) || 'Question'; + const prefix = `qp_${sectionIdx}`; + + let inner = ''; + if (config.noteBefore) inner += `

        ${esc(config.noteBefore)}

        `; + inner += `

        ${esc(qText)}

        `; + + switch (layout) { + case 'radio_question': + inner += `
        `; + for (const o of (q.answerOptions || [])) { + const key = optionKeyOf(o); + inner += ``; + } + inner += `
        `; + break; + + case 'multi_check_box_question': + inner += `
        `; + for (const o of (q.answerOptions || [])) { + const key = optionKeyOf(o); + inner += ``; + } + if (config.otherOptionKey && config.otherNextQuestionId) { + const ok = config.otherOptionKey; + inner += ``; + } + inner += `
        `; + if (config.minSelection) { + inner += `

        Select at least ${config.minSelection} option(s).

        `; + } + break; + + case 'value_spinner': { + const values = valueSpinnerValues(config); + inner += ``; + break; + } + + case 'slider_question': { + const range = config.range || { min: 0, max: 100 }; + const min = Number(range.min ?? 0); + const max = Number(range.max ?? 100); + const step = Number(config.step ?? 1) || 1; + inner += ` +
        + + ${min}${config.unitLabel ? ' ' + esc(config.unitLabel) : ''} +
        `; + break; + } + + case 'date_spinner': { + const precision = config.precision || 'full'; + const years = []; + const now = new Date().getFullYear(); + for (let yr = now + 1; yr >= 1900; yr--) years.push(yr); + inner += `
        `; + if (precision === 'full') { + inner += ``; + } + if (precision !== 'year') { + inner += ``; + } + inner += `
        `; + break; + } + + case 'string_spinner': { + inner += ``; + break; + } + + case 'free_text': + inner += ``; + break; + + case 'glass_scale_question': { + const symptoms = config.symptoms || []; + const scaleOpts = glassScaleOptions(q); + if (!symptoms.length) { + inner += `

        Informational screen (no symptom rows).

        `; + } else { + inner += `
        `; + for (const so of scaleOpts) inner += ``; + inner += ``; + symptoms.forEach((sym, si) => { + inner += ``; + scaleOpts.forEach(so => { + inner += ``; + }); + inner += ``; + }); + inner += `
        Symptom${esc(so.label)}
        ${esc(symptomRowLabel(q, sym))}
        `; + } + break; + } + + case 'client_coach_code_question': + inner += ` +

        In the app, client and Counselor codes are loaded automatically — Counselor review them here, not type them in.

        +
        + + +
        `; + break; + + case 'last_page': + inner += `

        ${esc(config.textKey || 'End of questionnaire.')}

        `; + break; + + default: + inner += `

        Preview not available for layout ${esc(layout)}.

        `; + } + + if (config.noteAfter) inner += `

        ${esc(config.noteAfter)}

        `; + return inner; +} + +function layoutLabel(type) { + const labels = { + radio_question: 'Radio', + multi_check_box_question: 'Checkbox', + glass_scale_question: 'Glass scale', + value_spinner: 'Value spinner', + slider_question: 'Slider', + date_spinner: 'Date', + string_spinner: 'String spinner', + free_text: 'Free text', + client_coach_code_question: 'Client / Counselor code', + last_page: 'Last page', + }; + return labels[type] || type || 'Unknown'; +} + +/** + * @param {HTMLElement} container + * @param {{ questionnaireName?: string, questions: object[] }} opts + */ +export function mountQuestionnairePreview(container, opts) { + const questions = opts.questions || []; + + function render() { + if (!questions.length) { + container.innerHTML = ` +
        +

        No questions to preview. Add questions on the Questions tab first.

        +
        `; + return; + } + + const sections = questions.map((q, idx) => { + const lid = localId(q); + const num = idx + 1; + return ` + `; + }).join(''); + + container.innerHTML = ` +
        +
        Preview mode — all questions on one page. Answers are not saved.
        +
        + + +
        +
        +
        + ${sections} +
        +
        +
        `; + + wireEvents(); + updateVisibility(); + } + + function sectionElements() { + return [...container.querySelectorAll('.qp-section')]; + } + + function sectionByIdxMap() { + const map = new Map(); + for (const el of sectionElements()) { + map.set(parseInt(el.dataset.idx, 10), el); + } + return map; + } + + function updateVisibility() { + const map = sectionByIdxMap(); + const visible = visibleQuestionIndices(questions, map); + let shown = 0; + for (const el of sectionElements()) { + const idx = parseInt(el.dataset.idx, 10); + const isVisible = visible.has(idx); + el.hidden = !isVisible; + el.classList.toggle('qp-section-hidden', !isVisible); + if (isVisible) shown++; + } + const counter = container.querySelector('#qpVisibleCount'); + if (counter) { + counter.textContent = `Showing ${shown} of ${questions.length} questions`; + } + } + + function wireEvents() { + const form = container.querySelector('#qpForm'); + if (!form) return; + + form.addEventListener('input', () => updateVisibility()); + form.addEventListener('change', () => updateVisibility()); + + form.querySelectorAll('input[type=range]').forEach(slider => { + const output = slider.closest('.qp-slider')?.querySelector('.qp-slider-value'); + if (!output) return; + slider.addEventListener('input', () => { + output.textContent = slider.value; + }); + }); + + container.querySelector('[data-action=restart]')?.addEventListener('click', () => { + form.reset(); + form.querySelectorAll('input[type=radio], input[type=checkbox]').forEach(el => { + el.checked = false; + }); + form.querySelectorAll('.qp-slider-value').forEach((output, i) => { + const slider = form.querySelectorAll('input[type=range]')[i]; + if (slider) output.textContent = slider.value; + }); + updateVisibility(); + }); + } + + render(); + return { restart: () => container.querySelector('[data-action=restart]')?.click() }; +} diff --git a/website/js/router.js b/website/js/router.js new file mode 100644 index 0000000..3abd697 --- /dev/null +++ b/website/js/router.js @@ -0,0 +1,56 @@ +const routes = []; +let currentCleanup = null; + +export function addRoute(pattern, handler) { + let regex; + const paramNames = []; + if (pattern instanceof RegExp) { + regex = pattern; + } else { + const parts = pattern.replace(/\/:([^/]+)/g, (_, name) => { + paramNames.push(name); + return '/([^/]+)'; + }); + regex = new RegExp('^' + parts + '$'); + } + routes.push({ regex, paramNames, handler }); +} + +export function navigate(hash) { + window.location.hash = hash; +} + +export function currentHash() { + return window.location.hash.slice(1) || '/'; +} + +async function resolve() { + if (typeof currentCleanup === 'function') { + currentCleanup(); + currentCleanup = null; + } + + const path = currentHash(); + for (const route of routes) { + const match = path.match(route.regex); + if (match) { + const params = {}; + route.paramNames.forEach((name, i) => { + params[name] = decodeURIComponent(match[i + 1]); + }); + try { + currentCleanup = await route.handler(params) || null; + } catch (e) { + console.error('Route handler error:', e); + } + return; + } + } + // fallback: home dashboard + navigate('#/'); +} + +export function startRouter() { + window.addEventListener('hashchange', resolve); + resolve(); +} diff --git a/website/js/table-filter.js b/website/js/table-filter.js new file mode 100644 index 0000000..8536871 --- /dev/null +++ b/website/js/table-filter.js @@ -0,0 +1,52 @@ +/** + * Client-side search for .data-table bodies. + * @param {HTMLTableSectionElement} tbody + * @param {string} query + * @param {(row: HTMLTableRowElement) => string} getRowText + * @returns {number} visible row count (excludes placeholder rows) + */ +export function filterTableBodyRows(tbody, query, getRowText) { + if (!tbody) return 0; + const q = query.trim().toLowerCase(); + let visible = 0; + tbody.querySelectorAll('tr').forEach(row => { + if (row.dataset.filterPlaceholder === '1') { + row.style.display = q ? 'none' : ''; + return; + } + const text = getRowText(row).toLowerCase(); + const show = !q || text.includes(q); + row.style.display = show ? '' : 'none'; + if (show) visible++; + }); + return visible; +} + +/** + * @param {HTMLInputElement} input + * @param {HTMLTableSectionElement} tbody + * @param {(row: HTMLTableRowElement) => string} getRowText + * @param {HTMLElement|null} countEl + * @param {{ total: number, noneLabel?: string }} [countOpts] + */ +export function wireTableSearch(input, tbody, getRowText, countEl, countOpts = {}) { + if (!input || !tbody) return; + const update = () => { + const visible = filterTableBodyRows(tbody, input.value, getRowText); + const total = countOpts.total ?? tbody.querySelectorAll('tr:not([data-filter-placeholder])').length; + if (!countEl) return; + const q = input.value.trim(); + if (!q) { + countEl.textContent = countOpts.noneLabel + ? `${total} ${countOpts.noneLabel}` + : ''; + return; + } + const noun = countOpts.noneLabel || 'rows'; + countEl.textContent = visible + ? `${visible} of ${total} ${noun}` + : `No ${noun} match`; + }; + input.addEventListener('input', update); + update(); +} diff --git a/website/js/test-data.js b/website/js/test-data.js new file mode 100644 index 0000000..d7a7111 --- /dev/null +++ b/website/js/test-data.js @@ -0,0 +1,125 @@ +const STABLE_KEY_RE = /^[a-zA-Z][a-zA-Z0-9_]*$/; + +/** Local question id from full questionID (hash__q5 → q5). */ +export function questionLocalKey(questionID, fallback = '') { + const id = String(questionID ?? ''); + const sep = id.lastIndexOf('__'); + if (sep >= 0) return id.slice(sep + 2); + return fallback || id; +} + +function parseQuestionConfig(q) { + if (!q || typeof q !== 'object') return {}; + let cfg = q.configJson; + if (typeof cfg === 'string') { + try { + cfg = JSON.parse(cfg); + } catch { + cfg = {}; + } + } + return cfg && typeof cfg === 'object' ? cfg : {}; +} + +/** Stable question key for table headers / CSV (matches server qdb_question_column_label). */ +export function questionDisplayKey(q) { + if (!q) return ''; + if (q.questionKey) return String(q.questionKey); + const cfg = parseQuestionConfig(q); + const fromCfg = String(cfg.questionKey || '').trim(); + if (fromCfg && STABLE_KEY_RE.test(fromCfg)) return fromCfg; + const dt = String(q.defaultText || '').trim(); + if (dt && STABLE_KEY_RE.test(dt)) return dt; + return questionLocalKey(q.questionID, dt); +} + +/** German question text for header tooltips. */ +export function questionGermanLabel(q) { + const dt = String(q?.defaultText || '').trim(); + const key = questionDisplayKey(q); + if (dt && dt !== key) return dt; + return ''; +} + +/** One symptom value from parent glass-scale JSON answer. */ +export function glassSymptomAnswerValue(raw, symptomKey) { + if (!raw || !symptomKey) return ''; + try { + const o = JSON.parse(raw); + if (o && typeof o === 'object' && !Array.isArray(o)) { + const v = o[symptomKey]; + return v != null ? String(v).trim() : ''; + } + } catch { + /* ignore */ + } + return ''; +} + +/** + * Flat result/export columns: glass_scale questions → one column per symptom. + */ +export function buildResultColumns(questions) { + const cols = []; + for (const q of questions || []) { + const cfg = parseQuestionConfig(q); + const symptoms = cfg.symptoms; + if (q.type === 'glass_scale_question' && Array.isArray(symptoms) && symptoms.length > 0) { + const parentKey = questionDisplayKey(q); + for (const sk of symptoms) { + const symptomKey = String(sk).trim(); + if (!symptomKey) continue; + cols.push({ + key: `glass_${q.questionID}_${symptomKey}`, + questionID: q.questionID, + symptomKey, + label: symptomKey, + title: parentKey && parentKey !== symptomKey + ? `${parentKey} · ${symptomKey}` + : symptomKey, + isGlassSymptom: true, + question: q, + }); + } + continue; + } + const label = questionDisplayKey(q); + cols.push({ + key: `q_${q.questionID}`, + questionID: q.questionID, + symptomKey: null, + label, + title: questionGermanLabel(q) || label, + isGlassSymptom: false, + question: q, + }); + } + return cols; +} + +/** Display text for one results table cell. */ +export function resultColumnCellValue(col, answer, optionMap = {}) { + if (!answer) return null; + if (col.isGlassSymptom) { + const v = glassSymptomAnswerValue(answer.freeTextValue, col.symptomKey); + return v || null; + } + const q = col.question; + if (answer.answerOptionID && optionMap[answer.answerOptionID]) { + return optionMap[answer.answerOptionID]; + } + if (answer.freeTextValue) return answer.freeTextValue; + if (answer.numericValue !== null && answer.numericValue !== undefined) { + return answer.numericValue; + } + return null; +} + +/** header_order.json column id → question key (questionnaire_x-y → y). */ +export function headerColumnQuestionKey(headerId) { + const id = String(headerId ?? ''); + if (id === 'client_code') return 'client_code'; + const dash = id.indexOf('-'); + if (dash < 0) return id; + return id.slice(dash + 1); +} diff --git a/website/js/translations-helpers.js b/website/js/translations-helpers.js new file mode 100644 index 0000000..f97c860 --- /dev/null +++ b/website/js/translations-helpers.js @@ -0,0 +1,285 @@ +/** German (de) is the canonical source language for questionnaire content. */ +export const SOURCE_LANG = 'de'; + +export function normalizeTranslations(raw) { + if (!raw) return {}; + if (Array.isArray(raw)) { + const map = {}; + raw.forEach(row => { + if (row && row.languageCode) map[row.languageCode] = row.text ?? ''; + }); + return map; + } + return typeof raw === 'object' ? { ...raw } : {}; +} + +export function normalizeEntry(entry) { + const translations = normalizeTranslations(entry.translations); + const germanText = (entry.germanText || translations[SOURCE_LANG] || entry.key || '').trim() + || entry.key + || ''; + return { ...entry, translations, germanText }; +} + +export function germanFor(entry) { + return entry.germanText || normalizeTranslations(entry.translations)[SOURCE_LANG] || entry.key || ''; +} + +const HEX_ID_RE = /^[a-f0-9]{32}$/i; + +/** Key column label (short id or readable preview — not the app lookup key). */ +export function entryDisplayKey(entry) { + const dk = entry.displayKey || ''; + if (dk && !HEX_ID_RE.test(dk)) return dk; + if (entry.type === 'question' || entry.type === 'answer_option') { + const key = (entry.key || '').trim(); + if (key) { + return key.length > 56 ? `${key.slice(0, 56)}…` : key; + } + } + if (entry.type === 'question' || entry.type === 'answer_option') { + const id = entry.entityId || ''; + const sep = id.lastIndexOf('__'); + if (sep >= 0) return id.slice(sep + 2); + } + return dk || entry.key || ''; +} + +export function translationFor(entry, lang) { + return normalizeTranslations(entry.translations)[lang] || ''; +} + +export function isEntryMissing(entry, lang) { + return !translationFor(entry, lang).trim(); +} + +export function countMissing(entries, lang) { + return entries.filter(e => isEntryMissing(e, lang)).length; +} + +export function sectionEntries(allEntries, section) { + let entries = allEntries.slice(section.startIdx, section.startIdx + section.entryCount); + if (section.isApp) { + entries = entries.filter(e => e.type === 'app_string'); + } + return entries; +} + +export function targetLanguages(languages) { + return (languages || []).filter(l => l.languageCode !== SOURCE_LANG); +} + +/** Sort rows within a group: missing translations first, then questionnaire order */ +export function sortGroupItems(items, targetLang) { + return [...items].sort((a, b) => { + const aMissing = !translationFor(a.entry, targetLang).trim(); + const bMissing = !translationFor(b.entry, targetLang).trim(); + if (aMissing !== bMissing) return aMissing ? -1 : 1; + + const orderA = a.entry.sortOrder ?? 0; + const orderB = b.entry.sortOrder ?? 0; + if (orderA !== orderB) return orderA - orderB; + + const typeCmp = (a.entry.type === 'question' ? 0 : 1) - (b.entry.type === 'question' ? 0 : 1); + if (typeCmp !== 0) return typeCmp; + + return germanFor(a.entry).localeCompare(germanFor(b.entry), 'de'); + }); +} + +/** + * Groups for one questionnaire: UI strings, then questions + options (interleaved). + * @param {number} indexOffset - global index for save handlers + */ +export function buildTranslationGroups(entries, targetLang, indexOffset = 0) { + const items = entries.map((entry, i) => ({ entry, idx: indexOffset + i })); + const appStrings = items.filter(({ entry }) => entry.type === 'app_string'); + const qStrings = items.filter(({ entry }) => entry.type === 'string'); + const content = items.filter(({ entry }) => + entry.type !== 'string' && entry.type !== 'app_string' + ); + const groups = []; + + if (appStrings.length) { + groups.push({ + id: 'app_strings', + title: 'App strings', + items: sortGroupItems(appStrings, targetLang), + }); + } + if (qStrings.length) { + groups.push({ + id: 'strings', + title: 'UI strings', + items: sortGroupItems(qStrings, targetLang), + }); + } + if (content.length) { + groups.push({ + id: 'content', + title: 'Questions', + items: sortGroupItems(content, targetLang), + }); + } + return groups; +} + +/** Render grouped sections (optional questionnaire wrapper title). */ +export function renderGroupedTranslationsHTML(groups, targetLang, sourceLabel, editable, options = {}) { + const { showColumnHeader = true, questionnaireTitle = null } = options; + if (!groups.length) return ''; + + const header = showColumnHeader ? translationListHeaderHTML( + targetLang, + sourceLabel + ) : ''; + + const qnHeader = questionnaireTitle + ? `

        ${escHtml(questionnaireTitle)}

        ` + : ''; + + const body = groups.map(g => ` +
        +

        ${escHtml(g.title)}

        +
        + ${g.items.map(({ entry, idx }) => + translationRowHTML(entry, idx, { targetLang, editable }) + ).join('')} +
        +
        + `).join(''); + + const wrapClass = questionnaireTitle ? 'trans-qn-block' : 'trans-groups-wrap'; + return `
        ${qnHeader}${header}${body}
        `; +} + +/** Only global app UI rows (never questionnaire questions/options). */ +export function filterGlobalAppEntries(entries) { + return (entries || []).filter(e => e && e.type === 'app_string'); +} + +/** Flatten app strings + questionnaires from ?all=1 */ +export function flattenAllQuestionnaires(questionnaires, appStrings = []) { + const allEntries = []; + const sections = []; + + const appNorm = filterGlobalAppEntries((appStrings || []).map(normalizeEntry)); + if (appNorm.length) { + const startIdx = 0; + appNorm.forEach(e => allEntries.push(e)); + sections.push({ + questionnaireID: '__app__', + name: 'App UI', + startIdx, + entryCount: appNorm.length, + isApp: true, + }); + } + + for (const qn of questionnaires) { + const normalized = (qn.entries || []).map(normalizeEntry); + if (!normalized.length) continue; + const startIdx = allEntries.length; + normalized.forEach(e => allEntries.push(e)); + sections.push({ + questionnaireID: qn.questionnaireID, + name: qn.name || 'Questionnaire', + startIdx, + entryCount: normalized.length, + isApp: false, + }); + } + + return { allEntries, sections }; +} + +export function typeBadge(type) { + const map = { question: 'Q', answer_option: 'Opt', string: 'Str', app_string: 'App' }; + const cls = { question: 'badge-q', answer_option: 'badge-opt', string: 'badge-str', app_string: 'badge-app' }; + return `${map[type] || type}`; +} + +export function escHtml(s) { + if (s == null) return ''; + return String(s) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +} + +export function sourceLanguageLabel(languages) { + const row = (languages || []).find(l => l.languageCode === SOURCE_LANG); + return row?.name ? `${row.name} (${SOURCE_LANG})` : 'German'; +} + +/** Column headers: label | main language (German) | target language */ +export function translationListHeaderHTML(targetLangLabel, sourceLabel = 'German') { + return ` +
        + Label + ${escHtml(sourceLabel)} + ${escHtml(targetLangLabel)} +
        `; +} + +export function translationRowHTML(entry, idx, { targetLang, editable = true }) { + const lookupKey = entry.key || ''; + const label = entryDisplayKey(entry); + const german = germanFor(entry); + const val = translationFor(entry, targetLang); + const missing = !val.trim(); + const disabled = editable ? '' : 'disabled'; + + const sourceCell = editable + ? `` + : `${escHtml(german)}`; + + const germanPreview = german.length > 72 ? `${german.slice(0, 72)}…` : german; + + return ` +
        +
        + ${typeBadge(entry.type)} +
        + ${escHtml(germanPreview || label)} + ${escHtml(lookupKey)} +
        +
        +
        ${sourceCell}
        + +
        `; +} + +/** + * Grouped rows with collapsible sections and missing counts in summaries. + */ +export function renderCollapsibleGroupedTranslationsHTML(groups, targetLang, sourceLabel, editable, options = {}) { + const { openGroupsWithMissing = false } = options; + if (!groups.length) return ''; + + const body = groups.map(g => { + const missing = g.items.filter(({ entry }) => isEntryMissing(entry, targetLang)).length; + const total = g.items.length; + const open = openGroupsWithMissing && missing > 0; + const summary = missing + ? `${g.title} · ${missing} missing` + : `${g.title} · complete (${total})`; + + return ` +
        + ${escHtml(summary)} +
        + ${g.items.map(({ entry, idx }) => + translationRowHTML(entry, idx, { targetLang, editable }) + ).join('')} +
        +
        `; + }).join(''); + + return `
        ${body}
        `; +} diff --git a/website/media/UniKonstanz_Logo_Optimum_RGB.eps b/website/media/UniKonstanz_Logo_Optimum_RGB.eps new file mode 100644 index 0000000000000000000000000000000000000000..d0860c01d2c852defc093db76053c609bb0c3b83 GIT binary patch literal 1607062 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�M=d$ubL>ZsYAPV0VmEJ-6lfZSA0CoiYOff&% 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 z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!) z36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@ zkN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg011!)36KB@kN^pg z013>VfScf-`%m=GNfZ6^x{3bz_(cC)JMy1f-x@dR*0;XPU10BWm;LvL-GBchkAyw) z$Wu>+J@wQVz7Y0>FTCIa*b6SW{BqdKFaPqFVZZ$4AN&AT{)>{zLK*+0w=umHDb5s0 zz5kdPh!sRE|7WO{MUFFcPPq5-FIE8l#R|Z`SONGKD**pu1>j$-0Q`#;fPb+9@GtJY z+<)Z+d;j~Nbkgfy_wkQo1xN|^qPwgfeSELaK0`Uhez^Ox3*5#3gGXe4{}1eu{vX&= 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 literal 0 HcmV?d00001 diff --git a/website/media/uni-logo.png b/website/media/uni-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4bc20981d1069475f124604d92cec6b7afed3172 GIT binary patch literal 43991 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 BGs*w} literal 0 HcmV?d00001 diff --git a/website/temp-assets/questionnaire_0_readme.json b/website/temp-assets/questionnaire_0_readme.json new file mode 100644 index 0000000..228c980 --- /dev/null +++ b/website/temp-assets/questionnaire_0_readme.json @@ -0,0 +1,158 @@ +{ + "meta": { + "_comment": "Diese Zeiel erklärt im folgenden wie die einzelnen Fragetypen definiert werden können und was für features diese mit sich bringen.", + "id": "questionnaire_0_readme" + }, + "questions": [ + + + { + "id": "q1", + "_comment": "Frage mit Radio Buttons. In diesem Fragetypen kann man maximal eine Antwort wählen. Über nextQuestionId kann man die nächste Frage definieren., Über pointsMap kann man die Punkte für die Antwort der Frage festlegen.", + "layout": "radio_question", + "question": "consent_instruction", + "options": [ + { "key": "consent_signed"}, + { "key": "consent_not_signed", + "nextQuestionId": "last_page"} + ], + "pointsMap": { + "consent_signed": 0, + "consent_not_signed": 0 + } + }, + + + { + "id": "q2", + "_comment": "Client- und Coach-Code (z. B. wenn Einwilligung nicht unterschrieben).", + "layout": "client_coach_code_question", + "question": "no_consent_entered", + "hint1": "client_code", + "hint2": "coach_code" + }, + + + { + "id": "q3", + "_comment": "Frage mit Spinner, dessen Antwortmöglichkeiten über options selbst definiert werden können.", + "layout": "string_spinner", + "question": "other_accommodation", + "options": ["Unterkunft 1", "Unterkunft 2", "Unterkunft 3"] + }, + + + { + "id": "q3b", + "_comment": "Freitext-Eingabe (einzeilig oder mehrzeilig). maxLength und hint sind optional. Eingaben werden gegen SQL-Injection-Muster gefiltert.", + "layout": "free_text", + "question": "other_accommodation", + "hint": "enter_text_to_continue", + "maxLength": 500, + "multiline": true + }, + + + { + "id": "q4", + "_comment": "Datumsfrage. precision: full (YYYY-MM-DD), year_month (YYYY-MM), year (YYYY).", + "layout": "date_spinner", + "question": "departure_country", + "precision": "year_month" + }, + + + { + "id": "q5", + "_comment": "Datumsfrage mit constraints. precision steuert Tag/Monat/Jahr-Spinner.", + "layout": "date_spinner", + "question": "since_in_germany", + "precision": "year_month", + "constraints": { + "notBefore": "departure_country", + "notAfter": "departure_country" + } + }, + + + { + "id": "q5b", + "_comment": "Glas-Skala (Symptome als Zeilen, 5 Stufen). scaleType: glass (Standard) oder thermometer.", + "layout": "glass_scale_question", + "question": "example_symptom_scale", + "scaleType": "thermometer", + "symptoms": ["symptom_a", "symptom_b"] + }, + + + { + "id": "q6", + "_comment": "Frage mit Spinner, dessen Antwortmöglichkeiten über range selbst definiert werden können.", + "layout": "value_spinner", + "question": "number_family_members", + "range": { + "min": 1, + "max": 15 + } + }, + + + { + "id": "q7", + "_comment": "Frage nach dem Code des Klienten und den Code des Coaches.", + "layout": "client_coach_code_question", + "question": "client_code_entry_question", + "hint1": "client_code", + "hint2": "coach_code" + }, + + + { + "id": "q8", + "_comment": "Frage mit Checkboxen, dessen Antwortmöglichkeiten über options selbst definiert werden können. Über pointsMap können die Punkte für die jeweiligen Antworten festgelegt werden. Über minSelection kann die minimal Anzahl an auszufüllenden Antworten festgelegt werden.", + "layout": "multi_check_box_question", + "question": "languages_spoken", + "options": [ + { "key": "language_albanian" }, + { "key": "language_pashto" }, + { "key": "language_russian" }, + { "key": "language_other"} + ], + "pointsMap": { + "language_albanian": 1, + "language_pashto": 1, + "language_russian": 1, + "language_other": 1 + }, + "minSelection": 1 + }, + + + { + "id": "q9", + "_comment":"Glass scale frage, bei der es für jede Frage, welche über symptoms festgelegt werden kann eine Antwort festgelegt werden muss. Die Antwort ganz links gibt immer 0 Punkte und die Antwort ganz rechts gibt immer 4 Punkte.", + "layout": "glass_scale_question", + "question": "glass_explanation", + "symptoms": [ + "q1_symptom", + "q2_symptom", + "q3_symptom", + "q4_symptom", + "q5_symptom", + "q6_symptom", + "q7_symptom", + "q8_symptom", + "q9_symptom" + ] + }, + + + { + "id": "last_page", + "_comment":"Letzte Seite, welche den Abschluss des Fragebogens markiert.", + "layout": "last_page", + "textKey": "finish_data_entry", + "question": "data_final_warning" + } + ] +} diff --git a/website/temp-assets/questionnaire_1_demographic_information.json b/website/temp-assets/questionnaire_1_demographic_information.json new file mode 100644 index 0000000..4eaf298 --- /dev/null +++ b/website/temp-assets/questionnaire_1_demographic_information.json @@ -0,0 +1,306 @@ +{ + "meta": { + "id": "questionnaire_1_demographic_information" + }, + "questions": [ + { + "id": "q1", + "layout": "radio_question", + "question": "consent_instruction", + "options": [ + { "key": "consent_signed", + "nextQuestionId": "q6"}, + { "key": "consent_not_signed", + "nextQuestionId": "q2"} + ], + "pointsMap": { + "consent_signed": 0, + "consent_not_signed": 0 + } + }, + { + "id": "q2", + "layout": "client_coach_code_question", + "question": "no_consent_entered", + "hint1": "client_code", + "hint2": "coach_code" + }, + { + "id": "last_page", + "layout": "last_page", + "textKey": "finish_data_entry", + "question": "data_final_warning" + }, + { + "id": "q28", + "layout": "radio_question", + "question": "accommodation", + "options": [ + { "key": "steinstrasse", + "nextQuestionId": "last_page"}, + { "key": "doerfle", + "nextQuestionId": "last_page"}, + { "key": "zoll_emmishofer", + "nextQuestionId": "last_page"}, + { "key": "other", + "nextQuestionId": "q4"} + ] + }, + { + "id": "q4", + "layout": "string_spinner", + "question": "other_accommodation", + "options": ["Unterkunft 1", "Unterkunft 2", "Unterkunft 3"] + }, + { + "id": "last_page", + "layout": "last_page", + "textKey": "finish_data_entry", + "question": "data_final_warning" + }, + { + "id": "q6", + "layout": "client_coach_code_question", + "question": "client_code_entry_question", + "hint1": "client_code", + "hint2": "coach_code" + }, + { + "id": "q29", + "layout": "radio_question", + "question": "accommodation", + "options": [ + { "key": "steinstrasse", + "nextQuestionId": "q9"}, + { "key": "doerfle", + "nextQuestionId": "q9"}, + { "key": "zoll_emmishofer", + "nextQuestionId": "q9"}, + { "key": "other", + "nextQuestionId": "q8"} + ], + "pointsMap": { + "consent_signed": 0, + "consent_not_signed": 0 + } + }, + { + "id": "q8", + "layout": "string_spinner", + "question": "other_accommodation", + "options": ["Unterkunft 1", "Unterkunft 2", "Unterkunft 3"] + }, + { + "id": "q9", + "layout": "value_spinner", + "question": "age", + "range": { + "min": 18, + "max": 122 + } + }, + { + "id": "q10", + "layout": "radio_question", + "question": "gender", + "options": [ + { "key": "gender_male"}, + { "key": "gender_female"}, + { "key": "gender_diverse"} + ], + "pointsMap": { + "consent_signed": 0, + "consent_not_signed": 0 + } + }, + { + "id": "q11", + "layout": "string_spinner", + "question": "country_of_origin" + }, + { + "id": "q13", + "layout": "date_spinner", + "question": "departure_country", + "precision": "year_month" + }, + { + "id": "q14", + "layout": "date_spinner", + "question": "since_in_germany", + "precision": "year_month", + "constraints": { + "notBefore": "departure_country" + } + }, + { + "id": "q16", + "layout": "radio_question", + "question": "living_situation", + "options": [ + { "key": "alone", + "nextQuestionId": "q18"}, + { "key": "with_family", + "nextQuestionId": "q17"} + ], + "pointsMap": { + "consent_signed": 0, + "consent_not_signed": 0 + } + }, + { + "id": "q17", + "layout": "value_spinner", + "question": "number_family_members", + "range": { + "min": 1, + "max": 15 + } + }, + { + "id": "q18", + "layout": "multi_check_box_question", + "question": "languages_spoken", + "options": [ + { "key": "language_albanian" }, + { "key": "language_pashto" }, + { "key": "language_russian" }, + { "key": "language_serbo" }, + { "key": "language_somali" }, + { "key": "language_tamil" }, + { "key": "language_tigrinya" }, + { "key": "language_turkish" }, + { "key": "language_ukrainian" }, + { "key": "language_urdu" }, + { "key": "language_arabic" }, + { "key": "language_dari_farsi" }, + { "key": "language_chinese" }, + { "key": "language_english" }, + { "key": "language_macedonian" }, + { "key": "language_kurmanji" }, + { "key": "language_hindi" }, + { "key": "language_french"}, + { "key": "language_other"} + ], + "pointsMap": { + "language_albanian": 0, + "language_pashto": 0, + "language_russian": 0, + "language_serbo": 0, + "language_somali": 0, + "language_tamil": 0, + "language_tigrinya": 0, + "language_turkish": 0, + "language_ukrainian": 0, + "language_urdu": 0, + "language_arabic": 0, + "language_dari_farsi": 0, + "language_chinese": 0, + "language_english": 0, + "language_macedonian": 0, + "language_kurmanji": 0, + "language_hindi": 0, + "language_french": 0, + "language_other": 0 + }, + "minSelection": 1 + }, + { + "id": "q19", + "layout": "radio_question", + "question": "german_skills", + "options": [ + { "key": "skill_none"}, + { "key": "skill_basic"}, + { "key": "skill_a1"}, + { "key": "skill_a2"}, + { "key": "skill_b1"}, + { "key": "skill_b2"}, + { "key": "skill_c1"}, + { "key": "skill_c2"} + ], + "pointsMap": { + "consent_signed": 0, + "consent_not_signed": 0 + } + }, + { + "id": "q30", + "layout": "value_spinner", + "question": "school_years_total", + "range": { + "min": 0, + "max": 15 + }, + "options": [ + { "value": 0, "nextQuestionId": "q23" } + ] + }, + + { + "id": "q20", + "layout": "value_spinner", + "question": "school_years_origin", + "range": { + "min": 0, + "max": 15 + } + }, + { + "id": "q21", + "layout": "value_spinner", + "question": "school_years_transit", + "range": { + "min": 0, + "max": 15 + } + }, + { + "id": "q22", + "layout": "value_spinner", + "question": "school_years_germany", + "range": { + "min": 0, + "max": 15 + } + }, + { + "id": "q23", + "layout": "radio_question", + "question": "vocational_training", + "options": [ + { "key": "answer_no"}, + { "key": "answer_started"}, + { "key": "answer_completed"} + ], + "pointsMap": { + "consent_signed": 0, + "consent_not_signed": 0 + } + }, + { + "id": "q24", + "layout": "date_spinner", + "question": "provisional_accommodation_since", + "precision": "year_month", + "constraints": { + "notBefore": "since_in_germany" + } + }, + { + "id": "q25", + "layout": "date_spinner", + "question": "asylum_procedure_since", + "precision": "year_month", + "constraints": { + "notBefore": "since_in_germany" + } + }, + { + "id": "last_page", + "layout": "last_page", + "textKey": "finish_data_entry", + "question": "data_final_warning" + } + ] +} diff --git a/website/temp-assets/questionnaire_2_rhs.json b/website/temp-assets/questionnaire_2_rhs.json new file mode 100644 index 0000000..4699964 --- /dev/null +++ b/website/temp-assets/questionnaire_2_rhs.json @@ -0,0 +1,137 @@ +{ + "meta": { + "id": "questionnaire_2_rhs" + }, + "questions": [ + { + "id": "q0", + "layout": "client_coach_code_question", + "question": "client_code_entry_question", + "hint1": "client_code", + "hint2": "coach_code" + }, + { + "id": "q3", + "layout": "glass_scale_question", + "question": "glass_explanation", + "symptoms": [ + "q1_symptom", + "q2_symptom", + "q3_symptom", + "q4_symptom", + "q5_symptom", + "q6_symptom", + "q7_symptom", + "q8_symptom", + "q9_symptom" + ] + }, + { + "id": "q4", + "layout": "glass_scale_question", + "question": "how_strong_past_month", + "symptoms": [ + "q10_reexperience_trauma", + "q11_physical_reaction", + "q12_emotional_numbness", + "q13_easily_startled" + ] + }, + { + "id": "q5", + "layout": "radio_question", + "textKey": "resilience_reflection_prompt", + "question": "q14_intro", + "options": [ + { "key": "resilience_fully_capable" }, + { "key": "resilience_mostly_capable" }, + { "key": "resilience_some_capable_some_not" }, + { "key": "resilience_mostly_not_capable" }, + { "key": "resilience_not_capable" } + ], + "pointsMap": { + "resilience_fully_capable": 0, + "resilience_mostly_capable": 1, + "resilience_some_capable_some_not": 2, + "resilience_mostly_not_capable": 3, + "resilience_not_capable": 4 + } + }, + { + "id": "q6", + "layout": "value_spinner", + "question": "pain_rating_instruction", + "range": { + "min": 0, + "max": 10 + } + }, + { + "id": "q7", + "layout": "radio_question", + "question": "violence_question_1", + "options": [ + { "key": "no", + "nextQuestionId": "q10"}, + { "key": "yes" } + ] + }, + { + "id": "q8", + "layout": "radio_question", + "question": "times_happend", + "options": [ + { "key": "once" }, + { "key": "multiple_times" } + ] + }, + { + "id": "q9", + "layout": "value_spinner", + "question": "age_at_incident", + "range": { + "min": 1, + "max": 122 + } + }, + { + "id": "q10", + "layout": "radio_question", + "question": "conflict_since_arrival", + "options": [ + { "key": "no", + "nextQuestionId": "last_page"}, + { "key": "yes" } + ] + }, + { + "id": "q11", + "layout": "radio_question", + "question": "times_happend2", + "options": [ + { "key": "once" }, + { "key": "multiple_times" } + ] + }, + { + "id": "q12", + "layout": "value_spinner", + "question": "age_at_incident2", + "range": { + "min": 1, + "max": 122 + } + }, + { + "id": "q25", + "layout": "date_spinner", + "question": "asylum_procedure_since" + }, + { + "id": "last_page", + "layout": "last_page", + "textKey": "finish_data_entry", + "question": "data_final_warning" + } + ] +} diff --git a/website/temp-assets/questionnaire_3_integration_index.json b/website/temp-assets/questionnaire_3_integration_index.json new file mode 100644 index 0000000..a0498ab --- /dev/null +++ b/website/temp-assets/questionnaire_3_integration_index.json @@ -0,0 +1,255 @@ +{ + "meta": { + "id": "questionnaire_3_integration_index" + }, + "questions": [ + { + "id": "q0", + "layout": "client_coach_code_question", + "question": "client_code_entry_question", + "hint1": "client_code", + "hint2": "coach_code" + }, + { + "id": "q1", + "layout": "radio_question", + "textKey": "intro_life_in_germany", + "question": "feeling_connected_to_germany_question", + "options": [ + { "key": "very_connected" }, + { "key": "quite_connected" }, + { "key": "moderately_connected" }, + { "key": "somewhat_loose" }, + { "key": "not_connected_at_all" } + ], + "pointsMap": { + "very_connected": 3, + "quite_connected": 4, + "moderately_connected": 5, + "somewhat_loose": 1, + "not_connected_at_all": 2 + } + }, + { + "id": "q2", + "layout": "radio_question", + "question": "understanding_political_issues", + "options": [ + { "key": "very_good" }, + { "key": "good" }, + { "key": "fairly_good" }, + { "key": "somewhat" }, + { "key": "not_at_all" } + ], + "pointsMap": { + "very_good": 1, + "good": 2, + "fairly_good": 3, + "somewhat": 4, + "not_at_all": 5 + } + }, + { + "id": "q3", + "layout": "radio_question", + "question": "unexpected_expense_question", + "options": [ + { "key": "expense_50" }, + { "key": "expense_100" }, + { "key": "expense_300" }, + { "key": "expense_500" }, + { "key": "expense_800" } + ], + "pointsMap": { + "expense_50": 1, + "expense_100": 2, + "expense_300": 3, + "expense_500": 4, + "expense_800": 5 + } + }, + { + "id": "q4", + "layout": "radio_question", + "question": "dining_with_germans_question", + "options": [ + { "key": "never" }, + { "key": "once_a_year" }, + { "key": "once_a_month" }, + { "key": "once_a_week" }, + { "key": "almost_every_day" } + ], + "pointsMap": { + "never": 1, + "once_a_year": 2, + "once_a_month": 3, + "once_a_week": 4, + "almost_every_day": 5 + } + }, + { + "id": "q5", + "layout": "radio_question", + "question": "reading_german_articles_question", + "options": [ + { "key": "very_good" }, + { "key": "good" }, + { "key": "moderately_good" }, + { "key": "not_good" }, + { "key": "not_at_all" } + ], + "pointsMap": { + "very_good": 1, + "good": 2, + "moderately_good": 3, + "not_good": 4, + "not_at_all": 5 + } + }, + { + "id": "q6", + "layout": "radio_question", + "question": "visiting_doctor_question", + "options": [ + { "key": "very_difficult" }, + { "key": "rather_difficult" }, + { "key": "neither_nor" }, + { "key": "rather_easy" }, + { "key": "very_easy" } + ], + "pointsMap": { + "very_difficult": 1, + "rather_difficult": 2, + "neither_nor": 3, + "rather_easy": 4, + "very_easy": 5 + } + }, + { + "id": "q7", + "layout": "radio_question", + "question": "feeling_as_outsider_question", + "options": [ + { "key": "never" }, + { "key": "rarely" }, + { "key": "sometimes" }, + { "key": "often" }, + { "key": "always" } + ], + "pointsMap": { + "never": 1, + "rarely": 2, + "sometimes": 3, + "often": 4, + "always": 5 + } + }, + { + "id": "q8", + "layout": "radio_question", + "question": "recent_occupation_question", + "options": [ + { "key": "employed" }, + { "key": "education" }, + { "key": "internship" }, + { "key": "unemployed_searching" }, + { "key": "unemployed_not_searching" }, + { "key": "sick_or_disabled" }, + { "key": "unpaid_housework" }, + { "key": "other_activity" } + ], + "pointsMap": { + "employed": 1, + "education": 2, + "internship": 3, + "unemployed_searching": 4, + "unemployed_not_searching": 8, + "sick_or_disabled": 5, + "unpaid_housework": 6, + "other_activity": 7 + } + }, + { + "id": "q9", + "layout": "radio_question", + "question": "discussing_politics_question", + "options": [ + { "key": "never" }, + { "key": "once_a_year" }, + { "key": "once_a_month" }, + { "key": "once_a_week" }, + { "key": "almost_every_day" } + ], + "pointsMap": { + "never": 1, + "once_a_year": 2, + "once_a_month": 3, + "once_a_week": 4, + "almost_every_day": 5 + } + }, + { + "id": "q10", + "layout": "radio_question", + "question": "contact_with_germans_question", + "options": [ + { "key": "zero" }, + { "key": "one_to_three" }, + { "key": "three_to_six" }, + { "key": "seven_to_fourteen" }, + { "key": "fifteen_or_more" } + ], + "pointsMap": { + "zero": 1, + "one_to_three": 2, + "three_to_six": 3, + "seven_to_fourteen": 4, + "fifteen_or_more": 5 + } + }, + { + "id": "q11", + "layout": "radio_question", + "question": "speaking_german_opinion_question", + "options": [ + { "key": "very_good" }, + { "key": "good" }, + { "key": "moderately_good" }, + { "key": "not_good" }, + { "key": "not_at_all" } + ], + "pointsMap": { + "very_good": 1, + "good": 2, + "moderately_good": 3, + "not_good": 4, + "not_at_all": 5 + } + }, + { + "id": "q12", + "layout": "radio_question", + "question": "job_search_question", + "options": [ + { "key": "very_difficult" }, + { "key": "rather_difficult" }, + { "key": "neither_nor" }, + { "key": "rather_easy" }, + { "key": "very_easy" } + ], + "pointsMap": { + "very_difficult": 1, + "rather_difficult": 2, + "neither_nor": 3, + "rather_easy": 4, + "very_easy": 5 + } + }, + { + "id": "last_page", + "layout": "last_page", + "textKey": "finish_data_entry", + "question": "data_final_warning" + } + ] +} diff --git a/website/temp-assets/questionnaire_4_consultation_results.json b/website/temp-assets/questionnaire_4_consultation_results.json new file mode 100644 index 0000000..b8a32aa --- /dev/null +++ b/website/temp-assets/questionnaire_4_consultation_results.json @@ -0,0 +1,182 @@ +{ + "meta": { + "id": "questionnaire_4_consultation_results" + }, + "questions": [ + { + "id": "q0", + "layout": "client_coach_code_question", + "question": "client_code_entry_question", + "hint1": "client_code", + "hint2": "coach_code" + }, + { + "id": "q1", + "layout": "date_spinner", + "question": "date_consultation_health_interview_result" + }, + { + "id": "q2", + "layout": "radio_question", + "question": "consultation_decision", + "options": [ + { + "key": "green", + "nextQuestionId": "q3"}, + {"key": "yellow", + "nextQuestionId": "q4"}, + { "key": "red", + "nextQuestionId": "q9"} + ], + "pointsMap": { + "green": 0, + "yellow": 0, + "red": 0 + } + }, + { + "id": "q3", + "layout": "radio_question", + "question": "consent_conversation_in_6_months", + "options": [ + { "key": "yes", + "nextQuestionId": "last_page"}, + { "key": "no", + "nextQuestionId": "last_page"} + ], + "pointsMap": { + "yes": 0, + "no": 0 + } + }, + { + "id": "q4", + "layout": "radio_question", + "question": "participation_in_coaching", + "options": [ + {"key": "yes", + "nextQuestionId": "q5"}, + {"key": "no", + "nextQuestionId": "q6"}, + {"key": "time_to_think_about_it", + "nextQuestionId": "q7"} + ], + "pointsMap": { + "yes": 0, + "no": 0, + "time_to_think_about_it": 0 + } + }, + { + "id": "q5", + "layout": "radio_question", + "question": "consent_coaching_given", + "options": [ + {"key": "consent_yes", + "nextQuestionId": "q3"}, + {"key": "consent_no", + "nextQuestionId": "last_page"} + ], + "pointsMap": { + "consent_yes": 0, + "consent_no": 0 + } + }, + { + "id": "q6", + "layout": "radio_question", + "question": "consent_conversation_in_6_months", + "options": [ + { "key": "yes", + "nextQuestionId": "last_page"}, + { "key": "no", + "nextQuestionId": "last_page"} + ], + "pointsMap": { + "yes": 0, + "no": 0 + } + }, + { + "id": "q7", + "layout": "date_spinner", + "question": "decision_after_reflection_period" + }, + { + "id": "q8", + "layout": "radio_question", + "question": "consent_conversation_in_6_months", + "options": [ + { "key": "yes", + "nextQuestionId": "last_page"}, + { "key": "no", + "nextQuestionId": "last_page"} + ], + "pointsMap": { + "yes": 0, + "no": 0 + } + }, + { + "id": "q9", + "layout": "radio_question", + "question": "professional_referral", + "options": [ + { "key": "yes"}, + { "key": "no"} + ], + "pointsMap": { + "yes": 0, + "no": 0 + } + }, + { + "id": "q10", + "layout": "radio_question", + "question": "confidentiality_agreement" , + "options": [ + { "key": "available_yes"}, + { "key": "available_no", + "nextQuestionId": "last_page"} + ], + "pointsMap": { + "yes": 0, + "no": 0 + } + }, + { + "id": "q11", + "layout": "radio_question", + "question": "health_insurance_card", + "options": [ + { "key": "yes"}, + { "key": "no"} + ], + "pointsMap": { + "yes": 0, + "no": 0 + } + }, + { + "id": "q12", + "layout": "radio_question", + "question": "consent_conversation_in_6_months", + "options": [ + { "key": "yes", + "nextQuestionId": "last_page"}, + { "key": "no", + "nextQuestionId": "last_page"} + ], + "pointsMap": { + "yes": 0, + "no": 0 + } + }, + { + "id": "last_page", + "layout": "last_page", + "textKey": "finish_data_entry", + "question": "data_final_warning" + } + ] +} \ No newline at end of file diff --git a/website/temp-assets/questionnaire_5_final_interview.json b/website/temp-assets/questionnaire_5_final_interview.json new file mode 100644 index 0000000..e017b65 --- /dev/null +++ b/website/temp-assets/questionnaire_5_final_interview.json @@ -0,0 +1,84 @@ +{ + "meta": { + "id": "questionnaire_5_final_interview" + }, + "questions": [ + { + "id": "q0", + "layout": "client_coach_code_question", + "question": "client_code_entry_question", + "hint1": "client_code", + "hint2": "coach_code" + }, + { + "id": "q1", + "layout": "radio_question", + "question": "consent_followup_6_months", + "options": [ + { "key": "yes" }, + { "key": "no" } + ] + }, + { + "id": "q2", + "layout": "date_spinner", + "question": "date_final_interview" + }, + { + "id": "q3", + "layout": "value_spinner", + "question": "amount_nat_appointments", + "range": { + "min": 0, + "max": 20 + } + }, + { + "id": "q4", + "layout": "value_spinner", + "question": "amount_session_flowers", + "range": { + "min": 0, + "max": 20 + } + }, + { + "id": "q5", + "layout": "value_spinner", + "question": "amount_session_stones", + "range": { + "min": 0, + "max": 20 + } + }, + { + "id": "q6", + "layout": "radio_question", + "question": "termination_nat_coaching", + "options": [ + {"key": "termination_nat_regular", + "nextQuestionId": "last_page"}, + {"key": "termination_nat_referral", + "nextQuestionId": "last_page"}, + {"key": "termination_nat_dropout", + "nextQuestionId": "q7"} + ] + }, + { + "id": "q7", + "layout": "radio_question", + "question": "client_canceled_NAT", + "options": [ + {"key": "after_meeting"}, + {"key": "without_giving_reasons"}, + {"key": "with_reasons_given"} + ] + }, + { + "id": "last_page", + "layout": "last_page", + "textKey": "finish_data_entry", + "question": "data_final_warning" + } + ] +} diff --git a/website/temp-assets/questionnaire_6_follow_up_survey.json b/website/temp-assets/questionnaire_6_follow_up_survey.json new file mode 100644 index 0000000..8680a7e --- /dev/null +++ b/website/temp-assets/questionnaire_6_follow_up_survey.json @@ -0,0 +1,349 @@ +{ + "meta": { + "id": "questionnaire_6_follow_up_survey" + }, + "questions": [ + { + "id": "q0", + "layout": "client_coach_code_question", + "question": "client_code_entry_question", + "hint1": "client_code", + "hint2": "coach_code" + }, + { + "id": "q1", + "layout": "radio_question", + "question": "follow_up", + "options": [ + { + "key": "follow_up_completed", + "nextQuestionId": "q2" + }, + { + "key": "follow_up_failed_contact", + "nextQuestionId": "last_page" + }, + { + "key": "follow_up_consent_withdrawn", + "nextQuestionId": "last_page" + } + ], + "pointsMap": { + "follow_up_completed": 0, + "follow_up_failed_contact": 0, + "follow_up_consent_withdrawn": 0 + } + }, + { + "id": "q2", + "layout": "radio_question", + "question": "special_burden_question", + "options": [ + { "key": "yes" }, + { "key": "no" } + ], + "pointsMap": { + "yes": 0, + "no": 0 + } + }, + + { + "id": "q3", + "layout": "glass_scale_question", + "question": "glass_explanation", + "symptoms": [ + "q1_symptom", + "q2_symptom", + "q3_symptom", + "q4_symptom", + "q5_symptom", + "q6_symptom", + "q7_symptom", + "q8_symptom", + "q9_symptom" + ] + }, + { + "id": "q4", + "layout": "glass_scale_question", + "question": "how_strong_past_month", + "symptoms": [ + "q10_reexperience_trauma", + "q11_physical_reaction", + "q12_emotional_numbness", + "q13_easily_startled" + ] + }, + { + "id": "q5", + "layout": "radio_question", + "textKey": "resilience_reflection_prompt", + "question": "q14_intro", + "options": [ + { "key": "resilience_fully_capable" }, + { "key": "resilience_mostly_capable" }, + { "key": "resilience_some_capable_some_not" }, + { "key": "resilience_mostly_not_capable" }, + { "key": "resilience_not_capable" } + ], + "pointsMap": { + "resilience_fully_capable": 0, + "resilience_mostly_capable": 0, + "resilience_some_capable_some_not": 0, + "resilience_mostly_not_capable": 0, + "resilience_not_capable": 0 + } + }, + { + "id": "q6", + "layout": "value_spinner", + "question": "pain_rating_instruction", + "range": { + "min": 0, + "max": 10 + } + }, + { + "id": "q7", + "layout": "radio_question", + "textKey": "intro_life_in_germany", + "question": "feeling_connected_to_germany_question", + "options": [ + { "key": "very_connected" }, + { "key": "quite_connected" }, + { "key": "moderately_connected" }, + { "key": "somewhat_loose" }, + { "key": "not_connected_at_all" } + ], + "pointsMap": { + "very_connected": 0, + "quite_connected": 0, + "moderately_connected": 0, + "somewhat_loose": 0, + "not_connected_at_all": 0 + } + }, + { + "id": "q8", + "layout": "radio_question", + "question": "understanding_political_issues", + "options": [ + { "key": "very_good" }, + { "key": "good" }, + { "key": "fairly_good" }, + { "key": "somewhat" }, + { "key": "not_at_all" } + ], + "pointsMap": { + "very_good": 0, + "good": 0, + "fairly_good": 0, + "somewhat": 0, + "not_at_all": 0 + } + }, + { + "id": "q9", + "layout": "radio_question", + "question": "unexpected_expense_question", + "options": [ + { "key": "expense_50" }, + { "key": "expense_100" }, + { "key": "expense_300" }, + { "key": "expense_500" }, + { "key": "expense_800" } + ], + "pointsMap": { + "expense_50": 0, + "expense_100": 0, + "expense_300": 0, + "expense_500": 0, + "expense_800": 0 + } + }, + { + "id": "q10", + "layout": "radio_question", + "question": "dining_with_germans_question", + "options": [ + { "key": "never" }, + { "key": "once_a_year" }, + { "key": "once_a_month" }, + { "key": "once_a_week" }, + { "key": "almost_every_day" } + ], + "pointsMap": { + "never": 0, + "once_a_year": 0, + "once_a_month": 0, + "once_a_week": 0, + "almost_every_day": 0 + } + }, + { + "id": "q11", + "layout": "radio_question", + "question": "reading_german_articles_question", + "options": [ + { "key": "very_good" }, + { "key": "good" }, + { "key": "moderately_good" }, + { "key": "not_good" }, + { "key": "not_at_all" } + ], + "pointsMap": { + "very_good": 0, + "good": 0, + "moderately_good": 0, + "not_good": 0, + "not_at_all": 0 + } + }, + { + "id": "q12", + "layout": "radio_question", + "question": "visiting_doctor_question", + "options": [ + { "key": "very_difficult" }, + { "key": "rather_difficult" }, + { "key": "neither_nor" }, + { "key": "rather_easy" }, + { "key": "very_easy" } + ], + "pointsMap": { + "very_difficult": 0, + "rather_difficult": 0, + "neither_nor": 0, + "rather_easy": 0, + "very_easy": 0 + } + }, + { + "id": "q13", + "layout": "radio_question", + "question": "feeling_as_outsider_question", + "options": [ + { "key": "never" }, + { "key": "rarely" }, + { "key": "sometimes" }, + { "key": "often" }, + { "key": "always" } + ], + "pointsMap": { + "never": 0, + "rarely": 0, + "sometimes": 0, + "often": 0, + "always": 0 + } + }, + { + "id": "q14", + "layout": "radio_question", + "question": "recent_occupation_question", + "options": [ + { "key": "employed" }, + { "key": "education" }, + { "key": "internship" }, + { "key": "unemployed_searching" }, + { "key": "unemployed_not_searching" }, + { "key": "sick_or_disabled" }, + { "key": "unpaid_housework" }, + { "key": "other_activity" } + ], + "pointsMap": { + "employed": 0, + "education": 0, + "internship": 0, + "unemployed_searching": 0, + "unemployed_not_searching": 0, + "sick_or_disabled": 0, + "unpaid_housework": 0, + "other_activity": 0 + } + }, + { + "id": "q15", + "layout": "radio_question", + "question": "discussing_politics_question", + "options": [ + { "key": "never" }, + { "key": "once_a_year" }, + { "key": "once_a_month" }, + { "key": "once_a_week" }, + { "key": "almost_every_day" } + ], + "pointsMap": { + "never": 0, + "once_a_year": 0, + "once_a_month": 0, + "once_a_week": 0, + "almost_every_day": 0 + } + }, + { + "id": "q16", + "layout": "radio_question", + "question": "contact_with_germans_question", + "options": [ + { "key": "zero" }, + { "key": "one_to_three" }, + { "key": "three_to_six" }, + { "key": "seven_to_fourteen" }, + { "key": "fifteen_or_more" } + ], + "pointsMap": { + "zero": 0, + "one_to_three": 0, + "three_to_six": 0, + "seven_to_fourteen": 0, + "fifteen_or_more": 0 + } + }, + { + "id": "q17", + "layout": "radio_question", + "question": "speaking_german_opinion_question", + "options": [ + { "key": "very_good" }, + { "key": "good" }, + { "key": "fairly_good" }, + { "key": "not_good" }, + { "key": "not_at_all" } + ], + "pointsMap": { + "very_good": 0, + "good": 0, + "fairly_good": 0, + "not_good": 0, + "not_at_all": 0 + } + }, + { + "id": "q18", + "layout": "radio_question", + "question": "job_search_question", + "options": [ + { "key": "very_difficult" }, + { "key": "rather_difficult" }, + { "key": "neither_nor" }, + { "key": "rather_easy" }, + { "key": "very_easy" } + ], + "pointsMap": { + "very_difficult": 0, + "rather_difficult": 0, + "neither_nor": 0, + "rather_easy": 0, + "very_easy": 0 + } + }, + { + "id": "last_page", + "layout": "last_page", + "textKey": "finish_data_entry", + "question": "data_final_warning" + } + ] +} diff --git a/website/temp-assets/questionnaire_order.json b/website/temp-assets/questionnaire_order.json new file mode 100644 index 0000000..5f971e3 --- /dev/null +++ b/website/temp-assets/questionnaire_order.json @@ -0,0 +1,67 @@ +[ + { + "file": "questionnaire_1_demographic_information.json", + "showPoints": false, + "condition": { + "alwaysAvailable": true + } + }, + { + "file": "questionnaire_2_rhs.json", + "showPoints": true, + "condition": { + "alwaysAvailable": true + } + }, + { + "file": "questionnaire_3_integration_index.json", + "showPoints": true, + "condition": { + "alwaysAvailable": true + } + }, + { + "file": "questionnaire_4_consultation_results.json", + "showPoints": false, + "condition": { + "requiresCompleted": [ + "questionnaire_1_demographic_information", + "questionnaire_2_rhs", + "questionnaire_3_integration_index" + ], + "questionnaire": "questionnaire_1_demographic_information", + "questionId": "consent_instruction", + "operator": "==", + "value": "consent_signed" + } + }, + { + "file": "questionnaire_5_final_interview.json", + "showPoints": false, + "condition": { + "requiresCompleted": ["questionnaire_4_consultation_results"], + "questionnaire": "questionnaire_4_consultation_results", + "questionId": "consultation_decision", + "operator": "==", + "value": "yellow" + } + }, + { + "file": "questionnaire_6_follow_up_survey.json", + "showPoints": true, + "condition": { + "anyOf": [ + { + "requiresCompleted": ["questionnaire_5_final_interview"] + }, + { + "requiresCompleted": ["questionnaire_4_consultation_results"], + "questionnaire": "questionnaire_4_consultation_results", + "questionId": "consultation_decision", + "operator": "!=", + "value": "yellow" + } + ] + } + } +]