Add guided reflection flow with mood-adaptive CBT/ACT questions

Walks users through 3-4 guided questions based on mood category:
positive (great/good) gets gratitude-oriented questions, neutral
(average) gets exploratory questions, and negative (bad/horrible)
gets empathetic questions. Stored as JSON in MoodEntryModel,
integrated into PDF reports, AI summaries, and CSV export.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-03-11 10:51:36 -05:00
parent 19b4c8b05b
commit 5bd8f8076a
13 changed files with 15340 additions and 13617 deletions

View File

@@ -213,6 +213,18 @@ final class ReportPDFGenerator {
<td>\(escapeHTML(weatherStr))</td>
</tr>
"""
if let reflection = entry.reflection, reflection.answeredCount > 0 {
rows += """
<tr class="reflection-row">
<td colspan="4">
<div class="reflection-block">
\(formatReflectionHTML(reflection))
</div>
</td>
</tr>
"""
}
}
return """
@@ -261,6 +273,13 @@ final class ReportPDFGenerator {
}
}
private func formatReflectionHTML(_ reflection: GuidedReflection) -> String {
reflection.responses
.filter { !$0.answer.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }
.map { "<p class=\"reflection-q\">\(escapeHTML($0.question))</p><p class=\"reflection-a\">\(escapeHTML($0.answer))</p>" }
.joined()
}
private func escapeHTML(_ string: String) -> String {
string
.replacingOccurrences(of: "&", with: "&amp;")
@@ -396,6 +415,10 @@ final class ReportPDFGenerator {
page-break-inside: avoid;
}
.stats { font-size: 10pt; color: #666; margin-bottom: 6px; }
.reflection-row td { border-bottom: none; padding-top: 0; }
.reflection-block { background: #f9f7ff; padding: 8px 12px; margin: 4px 0 8px 0; border-radius: 4px; }
.reflection-q { font-style: italic; font-size: 9pt; color: #666; margin-bottom: 2px; }
.reflection-a { font-size: 10pt; color: #333; margin-bottom: 6px; }
.page-break { page-break-before: always; }
.no-page-break { page-break-inside: avoid; }
.footer {