import type { Questionnaire, QuestionnaireItem, QuestionnaireResponse } from 'fhir/r4'; import { answeredQuestions } from './questionnaireAnswerText'; import { changedAnswers } from './changedAnswers'; import { QUESTIONNAIRE_HIDDEN_URL } from './loincUncomputedTotals'; /** * The most questions a form may have and still be answered inside the task dialog. A longer one * opens on its own page, where the patient can come back to it. */ export const LONGEST_FORM_ANSWERED_IN_PLACE = 12; const isHidden = (item: QuestionnaireItem): boolean => (item.extension ?? []).some((extension) => extension.url === QUESTIONNAIRE_HIDDEN_URL && extension.valueBoolean === true); /** The questions a patient can answer: every item they can see but headings and the groups that hold others. */ const visibleQuestionIds = (questionnaire: Pick | undefined): Set => { const ids = new Set(); const walk = (items: QuestionnaireItem[] | undefined) => { for (const item of items ?? []) { if (isHidden(item)) continue; if (item.type !== 'display' && item.type !== 'group') ids.add(item.linkId); walk(item.item); } }; walk(questionnaire?.item); return ids; }; export const countQuestions = (questionnaire: Pick | undefined): number => visibleQuestionIds(questionnaire).size; export const isLongForm = (questionnaire: Pick | undefined): boolean => countQuestions(questionnaire) > LONGEST_FORM_ANSWERED_IN_PLACE; /** How many of the questions the patient can see the response answers; a stored hidden answer never counts. */ export const countAnswered = ( response: QuestionnaireResponse | undefined, questionnaire: Pick | undefined, ): number => { const visible = visibleQuestionIds(questionnaire); return new Set(answeredQuestions(response).map((entry) => entry.linkId).filter((id) => visible.has(id))).size; }; export const describeProgress = (answered: number, total: number): string => `${answered} of ${total} answered`; const joinNames = (names: string[]): string => names.length < 2 ? names.join('') : `${names.slice(0, -1).join(', ')} and ${names[names.length - 1]}`; /** The changed questions by name, so the patient can see which answers moved. */ export const describeChangedQuestions = ( current: QuestionnaireResponse | undefined, previous: QuestionnaireResponse | undefined, ): string | undefined => { const names = changedAnswers(current, previous) .filter((entry) => entry.changed) .map((entry) => `“${entry.question.trim()}”`); if (!names.length) { return undefined; } return `You changed ${names.length} ${names.length === 1 ? 'answer' : 'answers'}: ${joinNames(names)}.`; };