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); CREATE TABLE IF NOT EXISTS program_session ( sessionID TEXT NOT NULL PRIMARY KEY, name TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', sessionNumber INTEGER NOT NULL DEFAULT 1, orderIndex INTEGER NOT NULL DEFAULT 0, isActive INTEGER NOT NULL DEFAULT 1, createdAt INTEGER NOT NULL DEFAULT 0, updatedAt INTEGER NOT NULL DEFAULT 0 ); CREATE TABLE IF NOT EXISTS program_session_questionnaire ( sessionID TEXT NOT NULL, questionnaireID TEXT NOT NULL, orderIndex INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (sessionID, questionnaireID), FOREIGN KEY(sessionID) REFERENCES program_session(sessionID) ON DELETE CASCADE, FOREIGN KEY(questionnaireID) REFERENCES questionnaire(questionnaireID) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_program_session_order ON program_session(orderIndex, sessionNumber); CREATE TABLE IF NOT EXISTS client_program_cycle ( clientCode TEXT NOT NULL PRIMARY KEY, cycleNumber INTEGER NOT NULL DEFAULT 1, cycleStartedAt INTEGER NOT NULL DEFAULT 0, updatedAt INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(clientCode) REFERENCES client(clientCode) ON DELETE CASCADE );