Files
NanoJason/cbt-therapy-app-mockup.html
2025-12-06 16:02:48 +04:00

2651 lines
93 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MindShift - CBT Therapy App</title>
<!-- PWA Meta Tags -->
<meta name="theme-color" content="#FF6B6B">
<meta name="description" content="Your personal CBT therapy companion for mood management and mental wellness">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="MindShift">
<meta name="application-name" content="MindShift">
<meta name="msapplication-TileColor" content="#FF6B6B">
<meta name="msapplication-config" content="browserconfig.xml">
<!-- PWA Manifest -->
<link rel="manifest" href="manifest.json">
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--primary: #FF6B6B;
--primary-light: #FF8E8E;
--primary-dark: #E55555;
--primary-container: #FFE5E5;
--on-primary: #FFFFFF;
--on-primary-container: #410002;
--secondary: #FFB74D;
--secondary-container: #FFF3E0;
--on-secondary: #FFFFFF;
--on-secondary-container: #4E2B00;
--tertiary: #4FC3F7;
--tertiary-container: #E1F5FE;
--surface: #FFFFFF;
--surface-variant: #F5F5F5;
--on-surface: #212121;
--on-surface-variant: #757575;
--outline: #BDBDBD;
--shadow: rgba(0,0,0,0.1);
--error: #FF5252;
--success: #66BB6A;
--warning: #FFA726;
--joy: #AB47BC;
--peace: #26A69A;
--energy: #FFEE58;
}
body {
font-family: 'Roboto', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: var(--on-surface);
line-height: 1.5;
min-height: 100vh;
position: relative;
overflow-x: hidden;
}
/* Animated background particles */
.particles {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 0;
}
.particle {
position: absolute;
width: 10px;
height: 10px;
background: rgba(255, 255, 255, 0.5);
border-radius: 50%;
animation: float 6s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0) rotate(0deg); opacity: 0; }
10% { opacity: 1; }
90% { opacity: 1; }
100% { transform: translateY(-100vh) rotate(720deg); opacity: 0; }
}
/* Welcome overlay */
.welcome-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #FF6B6B 0%, #4ECDC4 100%);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 1000;
color: white;
animation: fadeOut 0.5s ease-out 2s forwards;
}
.welcome-title {
font-size: 48px;
font-weight: 700;
margin-bottom: 16px;
animation: slideInUp 0.8s ease-out;
}
.welcome-subtitle {
font-size: 20px;
opacity: 0.9;
animation: slideInUp 0.8s ease-out 0.2s both;
}
.welcome-emoji {
font-size: 80px;
margin-bottom: 24px;
animation: bounce 1s ease-in-out;
}
@keyframes fadeOut {
to { opacity: 0; visibility: hidden; }
}
@keyframes slideInUp {
from { transform: translateY(30px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
@keyframes bounce {
0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
40% { transform: translateY(-30px); }
60% { transform: translateY(-15px); }
}
/* Header */
.app-header {
background-color: var(--primary);
color: var(--on-primary);
padding: 16px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
position: sticky;
top: 0;
z-index: 100;
}
.header-content {
max-width: 1200px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
}
.header-actions {
display: flex;
gap: 8px;
position: relative;
}
.notification-badge {
position: absolute;
top: 8px;
right: 8px;
background: var(--secondary);
color: white;
font-size: 10px;
font-weight: bold;
padding: 2px 5px;
border-radius: 10px;
min-width: 16px;
text-align: center;
}
.notification-dropdown {
position: absolute;
top: 100%;
right: 0;
background: white;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
padding: 16px;
min-width: 300px;
max-height: 400px;
overflow-y: auto;
z-index: 1000;
display: none;
}
.notification-item {
padding: 12px;
border-bottom: 1px solid var(--outline);
cursor: pointer;
transition: background-color 0.2s;
}
.notification-item:hover {
background-color: var(--surface-variant);
}
.notification-item:last-child {
border-bottom: none;
}
.notification-time {
font-size: 12px;
color: var(--on-surface-variant);
margin-top: 4px;
}
.app-title {
font-size: 24px;
font-weight: 500;
display: flex;
align-items: center;
gap: 8px;
}
/* Navigation */
.bottom-nav {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: var(--surface);
box-shadow: 0 -2px 8px rgba(0,0,0,0.1);
display: flex;
justify-content: space-around;
padding: 8px 0;
z-index: 100;
}
.nav-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 8px 16px;
cursor: pointer;
transition: all 0.3s ease;
border: none;
background: none;
color: var(--on-surface-variant);
}
.nav-item.active {
color: var(--primary);
}
.nav-item:hover {
background-color: var(--surface-variant);
border-radius: 12px;
}
.nav-item .material-icons {
font-size: 24px;
}
.nav-label {
font-size: 12px;
margin-top: 4px;
}
/* Main Content */
.main-content {
max-width: 1200px;
margin: 0 auto;
padding: 16px;
padding-bottom: 80px;
min-height: calc(100vh - 120px);
}
/* Cards */
.card {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 8px 32px rgba(0,0,0,0.1);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, var(--primary), var(--secondary), var(--tertiary));
transform: scaleX(0);
transition: transform 0.3s ease;
}
.card:hover {
transform: translateY(-5px) scale(1.02);
box-shadow: 0 12px 40px rgba(0,0,0,0.15);
}
.card:hover::before {
transform: scaleX(1);
}
.card-title {
font-size: 20px;
font-weight: 500;
margin-bottom: 12px;
color: var(--on-surface);
}
/* Mood Selector */
.mood-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 16px;
margin: 20px 0;
}
.mood-card {
background: linear-gradient(135deg, rgba(255,255,255,0.9), rgba(255,255,255,0.7));
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 20px;
text-align: center;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
border: 3px solid transparent;
position: relative;
overflow: hidden;
}
.mood-card::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 107, 107, 0.1);
transform: translate(-50%, -50%);
transition: width 0.6s, height 0.6s;
}
.mood-card:hover {
transform: translateY(-8px) scale(1.1);
box-shadow: 0 15px 35px rgba(0,0,0,0.2);
background: linear-gradient(135deg, var(--primary-container), rgba(255,255,255,0.9));
}
.mood-card:hover::after {
width: 200px;
height: 200px;
}
.mood-card.selected {
border-color: var(--primary);
background: linear-gradient(135deg, var(--primary), var(--secondary));
color: white;
transform: scale(1.05);
animation: pulse 1s ease-in-out;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(255, 107, 107, 0.7); }
70% { box-shadow: 0 0 0 20px rgba(255, 107, 107, 0); }
100% { box-shadow: 0 0 0 0 rgba(255, 107, 107, 0); }
}
.mood-emoji {
font-size: 48px;
margin-bottom: 12px;
display: block;
animation: wiggle 2s ease-in-out infinite;
}
@keyframes wiggle {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(-10deg); }
75% { transform: rotate(10deg); }
}
.mood-card:hover .mood-emoji {
animation: bounce 0.5s ease-in-out;
}
.mood-label {
font-size: 16px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* Intensity Slider */
.intensity-container {
margin: 20px 0;
}
.intensity-label {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
}
.slider {
width: 100%;
height: 6px;
border-radius: 3px;
background: var(--surface-variant);
outline: none;
-webkit-appearance: none;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--primary);
cursor: pointer;
}
.slider::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--primary);
cursor: pointer;
border: none;
}
/* Buttons */
.btn {
padding: 16px 32px;
border: none;
border-radius: 30px;
font-size: 18px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
display: inline-flex;
align-items: center;
gap: 12px;
position: relative;
overflow: hidden;
text-transform: uppercase;
letter-spacing: 1px;
}
.btn::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.3);
transform: translate(-50%, -50%);
transition: width 0.6s, height 0.6s;
}
.btn:hover::before {
width: 300px;
height: 300px;
}
.btn-primary {
background: linear-gradient(135deg, var(--primary), var(--secondary));
color: var(--on-primary);
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3);
}
.btn-primary:hover {
transform: translateY(-3px);
box-shadow: 0 8px 25px rgba(255, 107, 107, 0.4);
}
.btn-secondary {
background: linear-gradient(135deg, var(--tertiary), var(--peace));
color: white;
box-shadow: 0 4px 15px rgba(79, 195, 247, 0.3);
}
.btn-secondary:hover {
transform: translateY(-3px);
box-shadow: 0 8px 25px rgba(79, 195, 247, 0.4);
}
.btn-block {
width: 100%;
justify-content: center;
}
.btn:active {
transform: translateY(-1px);
}
/* Exercise Cards */
.exercise-card {
background: linear-gradient(135deg, rgba(255,255,255,0.95), rgba(255,255,255,0.85));
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 24px;
margin-bottom: 20px;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
border: 2px solid transparent;
position: relative;
overflow: hidden;
}
.exercise-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, var(--primary), var(--secondary), var(--tertiary));
transform: scaleX(0);
transition: transform 0.3s ease;
}
.exercise-card:hover {
transform: translateY(-8px) scale(1.02);
box-shadow: 0 15px 40px rgba(0,0,0,0.15);
background: linear-gradient(135deg, var(--primary-container), rgba(255,255,255,0.95));
}
.exercise-card:hover::before {
transform: scaleX(1);
}
.exercise-title {
display: flex;
align-items: center;
gap: 12px;
font-weight: 600;
font-size: 18px;
margin-bottom: 12px;
color: var(--on-surface);
}
.exercise-title .material-icons {
font-size: 28px;
color: var(--primary);
}
.exercise-duration {
font-size: 14px;
color: var(--primary);
font-weight: 500;
margin-bottom: 12px;
display: inline-block;
padding: 4px 12px;
background: var(--primary-container);
border-radius: 20px;
}
.exercise-description {
font-size: 15px;
color: var(--on-surface-variant);
line-height: 1.5;
}
/* Exercise Modals */
.exercise-modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 90%;
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
z-index: 1000;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
.exercise-modal h3 {
color: var(--primary);
margin-bottom: 24px;
font-size: 24px;
}
.exercise-actions {
display: flex;
gap: 12px;
margin-top: 24px;
justify-content: flex-end;
}
/* Emotion Inputs */
.emotion-inputs {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
}
.emotion-name {
flex: 1;
}
.emotion-slider {
width: 150px;
}
.emotion-value {
min-width: 40px;
text-align: center;
font-weight: 600;
color: var(--primary);
}
/* Mindfulness Timer */
.timer-display {
text-align: center;
margin: 40px 0;
}
#mindfulness-timer {
font-size: 72px;
font-weight: 300;
color: var(--primary);
font-family: 'Courier New', monospace;
}
.mindfulness-instructions {
text-align: center;
margin: 20px 0;
line-height: 1.8;
}
/* Breathing Exercise */
.breathing-content {
text-align: center;
}
.breathing-circle {
width: 200px;
height: 200px;
border-radius: 50%;
background: linear-gradient(135deg, var(--primary), var(--secondary));
margin: 40px auto;
display: flex;
align-items: center;
justify-content: center;
transition: all 4s ease-in-out;
}
.breathing-circle.inhale {
transform: scale(1.3);
}
.breathing-circle.exhale {
transform: scale(0.7);
}
.breathing-text {
color: white;
font-size: 24px;
font-weight: 600;
}
.breathing-instructions {
margin: 20px 0;
line-height: 1.6;
}
/* Gratitude Exercise */
.gratitude-content {
max-width: 500px;
margin: 0 auto;
}
.exercise-intro {
font-size: 18px;
color: var(--on-surface);
margin-bottom: 24px;
text-align: center;
}
.gratitude-list {
margin-bottom: 24px;
}
/* Progress Section */
.progress-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 16px;
margin: 16px 0;
}
.progress-card {
background-color: var(--surface-variant);
border-radius: 12px;
padding: 16px;
text-align: center;
}
.progress-value {
font-size: 24px;
font-weight: 500;
color: var(--primary);
margin-bottom: 4px;
}
.progress-label {
font-size: 14px;
color: var(--on-surface-variant);
}
.chart-container {
position: relative;
height: 200px;
background: rgba(255, 255, 255, 0.5);
border-radius: 12px;
padding: 16px;
}
.analytics-card {
background: rgba(255, 255, 255, 0.7);
border-radius: 12px;
padding: 16px;
}
.analytics-card h4 {
margin: 0 0 8px 0;
color: var(--primary);
font-size: 16px;
}
.emotion-bar {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.emotion-label {
flex: 1;
font-size: 14px;
}
.emotion-value {
width: 40px;
text-align: right;
font-size: 14px;
font-weight: 500;
}
.emotion-progress {
height: 6px;
background: var(--surface-variant);
border-radius: 3px;
margin: 0 8px;
overflow: hidden;
}
.emotion-progress-fill {
height: 100%;
background: var(--primary);
border-radius: 3px;
transition: width 0.3s ease;
}
/* Floating Action Button */
.fab {
position: fixed;
bottom: 90px;
right: 20px;
width: 56px;
height: 56px;
border-radius: 50%;
background-color: var(--primary);
color: var(--on-primary);
border: none;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
z-index: 99;
}
.fab:hover {
transform: scale(1.1);
box-shadow: 0 6px 12px rgba(0,0,0,0.3);
}
/* Responsive Design - Mobile First */
/* Small phones (320px and up) */
@media (max-width: 374px) {
.mood-grid {
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
.mood-emoji {
font-size: 36px;
}
.welcome-title {
font-size: 32px;
}
.card {
padding: 16px;
}
.btn {
padding: 14px 24px;
font-size: 16px;
}
}
/* Large phones (375px to 767px) */
@media (min-width: 375px) and (max-width: 767px) {
.mood-grid {
grid-template-columns: repeat(3, 1fr);
}
.progress-container {
grid-template-columns: repeat(2, 1fr);
}
}
/* Tablets (768px to 1023px) */
@media (min-width: 768px) and (max-width: 1023px) {
.main-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
padding: 24px;
}
.full-width {
grid-column: 1 / -1;
}
.bottom-nav {
display: none;
}
.app-header {
padding: 20px 24px;
}
.mood-grid {
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.card {
padding: 24px;
}
.welcome-title {
font-size: 56px;
}
.sidebar-nav {
display: flex;
position: fixed;
left: 0;
top: 80px;
bottom: 0;
width: 80px;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
flex-direction: column;
align-items: center;
padding: 20px 0;
box-shadow: 2px 0 10px rgba(0,0,0,0.1);
}
.nav-item {
flex-direction: column;
padding: 16px;
margin-bottom: 8px;
border-radius: 16px;
min-width: 60px;
}
.nav-label {
font-size: 10px;
margin-top: 4px;
}
.content-area {
margin-left: 80px;
}
}
/* Small Desktops (1024px to 1439px) */
@media (min-width: 1024px) and (max-width: 1439px) {
.main-content {
display: grid;
grid-template-columns: 280px 1fr;
gap: 32px;
padding: 32px;
max-width: 1400px;
margin: 0 auto;
}
.full-width {
grid-column: 1 / -1;
}
.bottom-nav {
display: none;
}
.sidebar {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 24px;
padding: 24px;
height: fit-content;
position: sticky;
top: 100px;
}
.sidebar-nav {
display: flex;
flex-direction: column;
gap: 8px;
}
.nav-item {
flex-direction: row;
justify-content: flex-start;
padding: 16px 20px;
border-radius: 16px;
transition: all 0.3s ease;
}
.nav-item:hover {
background: var(--primary-container);
transform: translateX(8px);
}
.nav-item.active {
background: linear-gradient(135deg, var(--primary), var(--secondary));
color: white;
}
.content-area {
min-height: calc(100vh - 140px);
}
.mood-grid {
grid-template-columns: repeat(3, 1fr);
gap: 24px;
}
.progress-container {
grid-template-columns: repeat(4, 1fr);
}
}
/* Large Desktops (1440px and up) */
@media (min-width: 1440px) {
.main-content {
grid-template-columns: 320px 1fr 280px;
gap: 40px;
padding: 40px;
max-width: 1800px;
}
.sidebar {
grid-row: 1 / -1;
}
.right-panel {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 24px;
padding: 24px;
height: fit-content;
position: sticky;
top: 100px;
}
.mood-grid {
grid-template-columns: repeat(3, 1fr);
gap: 30px;
}
.card {
padding: 32px;
}
.welcome-title {
font-size: 64px;
}
.exercise-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24px;
}
}
/* Landscape orientations */
@media (max-height: 600px) and (orientation: landscape) {
.welcome-overlay {
padding: 20px;
}
.welcome-title {
font-size: 36px;
margin-bottom: 8px;
}
.welcome-emoji {
font-size: 60px;
margin-bottom: 16px;
}
.app-header {
padding: 12px 16px;
}
.main-content {
padding: 16px;
}
}
/* Animations */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.fade-in {
animation: fadeIn 0.5s ease-out;
}
/* Hidden sections */
.section {
display: none;
}
.section.active {
display: block;
}
/* Thought Record */
.thought-record {
background-color: var(--surface-variant);
border-radius: 12px;
padding: 16px;
margin: 16px 0;
}
.form-group {
margin-bottom: 16px;
}
.form-label {
font-size: 14px;
font-weight: 500;
margin-bottom: 8px;
display: block;
color: var(--on-surface);
}
.form-input {
width: 100%;
padding: 12px;
border: 1px solid var(--outline);
border-radius: 8px;
font-size: 16px;
background-color: var(--surface);
color: var(--on-surface);
}
.form-input:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 2px var(--primary-container);
}
textarea.form-input {
resize: vertical;
min-height: 100px;
}
</style>
</head>
<body>
<!-- Welcome Overlay -->
<div class="welcome-overlay">
<div class="welcome-emoji">🌟</div>
<h1 class="welcome-title">Welcome to MindShift!</h1>
<p class="welcome-subtitle">Your journey to a brighter mind starts now</p>
</div>
<!-- Animated Particles -->
<div class="particles" id="particles"></div>
<!-- Header -->
<header class="app-header">
<div class="header-content">
<div class="app-title">
<span class="material-icons">psychology</span>
MindShift
</div>
<div class="header-actions">
<button class="btn btn-secondary" onclick="toggleNotifications()" id="notification-btn">
<span class="material-icons">notifications</span>
<span class="notification-badge" id="notification-badge" style="display: none;">0</span>
</button>
<div class="notification-dropdown" id="notification-dropdown">
<h3 style="margin: 0 0 12px 0;">Notifications</h3>
<div id="notification-list">
<!-- Notifications will be added here -->
</div>
</div>
<button class="btn btn-secondary">
<span class="material-icons">account_circle</span>
</button>
</div>
</div>
</header>
<!-- Main Content -->
<main class="main-content">
<!-- Home Section -->
<section id="home" class="section active fade-in">
<div class="card">
<h2 class="card-title">How are you feeling today?</h2>
<p style="margin-bottom: 20px; color: var(--on-surface-variant);">
Select your current mood to get started with personalized CBT exercises
</p>
<div class="mood-grid">
<div class="mood-card" onclick="selectMood(this, 'anxious')">
<div class="mood-emoji">😰</div>
<div class="mood-label">Anxious</div>
</div>
<div class="mood-card" onclick="selectMood(this, 'sad')">
<div class="mood-emoji">😢</div>
<div class="mood-label">Sad</div>
</div>
<div class="mood-card" onclick="selectMood(this, 'angry')">
<div class="mood-emoji">😠</div>
<div class="mood-label">Angry</div>
</div>
<div class="mood-card" onclick="selectMood(this, 'stressed')">
<div class="mood-emoji">😫</div>
<div class="mood-label">Stressed</div>
</div>
<div class="mood-card" onclick="selectMood(this, 'happy')">
<div class="mood-emoji">😊</div>
<div class="mood-label">Happy</div>
</div>
<div class="mood-card" onclick="selectMood(this, 'confused')">
<div class="mood-emoji">😕</div>
<div class="mood-label">Confused</div>
</div>
</div>
<div class="intensity-container">
<div class="intensity-label">
<span>Intensity</span>
<span id="intensityValue">5</span>
</div>
<input type="range" class="slider" id="intensitySlider" min="1" max="10" value="5" oninput="updateIntensity(this.value)">
</div>
<button class="btn btn-primary btn-block" onclick="showSituationBuilder()">
<span class="material-icons">arrow_forward</span>
Build Your Situation
</button>
</div>
<div class="card">
<h3 class="card-title">Quick Actions</h3>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
<button class="btn btn-secondary" onclick="quickRelax()">
<span class="material-icons">self_improvement</span>
Quick Relax
</button>
<button class="btn btn-secondary" onclick="breathingExercise()">
<span class="material-icons">air</span>
Breathe
</button>
</div>
</div>
</section>
<!-- Situation Builder Section -->
<section id="situation-builder" class="section">
<div class="card">
<h2 class="card-title">Build Your Situation</h2>
<div class="thought-record">
<div class="form-group">
<label class="form-label">What's happening right now?</label>
<textarea class="form-input" placeholder="Describe the situation or trigger..."></textarea>
</div>
<div class="form-group">
<label class="form-label">What thoughts are going through your mind?</label>
<textarea class="form-input" placeholder="I'm thinking that..."></textarea>
</div>
<div class="form-group">
<label class="form-label">Physical sensations</label>
<textarea class="form-input" placeholder="I feel in my body..."></textarea>
</div>
<div class="form-group">
<label class="form-label">What's the worst that could happen?</label>
<textarea class="form-input" placeholder="The worst case scenario is..."></textarea>
</div>
</div>
<button class="btn btn-primary btn-block" onclick="generateExercises()">
<span class="material-icons">auto_awesome</span>
Generate My CBT Exercises
</button>
</div>
</section>
<!-- Exercises Section -->
<section id="exercises" class="section">
<div class="card">
<h2 class="card-title">Your Personalized CBT Exercises</h2>
<p style="margin-bottom: 20px; color: var(--on-surface-variant);">
Based on your current state, these exercises can help you feel better
</p>
<div class="exercise-card" onclick="startThoughtRecord()">
<div class="exercise-title">
<span class="material-icons">psychology</span>
Thought Record
</div>
<div class="exercise-duration">Duration: 10 minutes</div>
<div class="exercise-description">
Identify and reframe negative thought patterns using cognitive restructuring.
</div>
</div>
<div class="exercise-card" onclick="startMindfulness()">
<div class="exercise-title">
<span class="material-icons">self_improvement</span>
Mindfulness
</div>
<div class="exercise-duration">Duration: 5 minutes</div>
<div class="exercise-description">
Practice present moment awareness and reduce stress.
</div>
</div>
<div class="exercise-card" onclick="startGratitude()">
<div class="exercise-title">
<span class="material-icons">favorite</span>
Gratitude
</div>
<div class="exercise-duration">Duration: 5 minutes</div>
<div class="exercise-description">
Cultivate positive thinking by focusing on what you're thankful for.
</div>
</div>
<div class="exercise-card" onclick="startBreathing()">
<div class="exercise-title">
<span class="material-icons">spa</span>
Breathing
</div>
<div class="exercise-duration">Duration: 3 minutes</div>
<div class="exercise-description">
Regulate your nervous system with this simple breathing technique.
</div>
</div>
</div>
<!-- Thought Record Exercise -->
<div id="thought-record-exercise" class="card exercise-modal" style="display: none;">
<h3>Thought Record Exercise</h3>
<div class="thought-record">
<div class="form-group">
<label class="form-label">1. What was the situation?</label>
<textarea class="form-input" id="situation" placeholder="Describe what happened..."></textarea>
</div>
<div class="form-group">
<label class="form-label">2. What emotions did you feel? (Rate 0-100)</label>
<div class="emotion-inputs">
<input type="text" class="form-input emotion-name" placeholder="Emotion (e.g., Sad)">
<input type="range" class="emotion-slider" min="0" max="100" value="50">
<span class="emotion-value">50</span>
</div>
<button class="btn btn-secondary" onclick="addEmotionInput()">Add Another Emotion</button>
</div>
<div class="form-group">
<label class="form-label">3. What automatic thoughts came to mind?</label>
<textarea class="form-input" id="auto-thoughts" placeholder="Write down your thoughts..."></textarea>
</div>
<div class="form-group">
<label class="form-label">4. Evidence that supports the thought</label>
<textarea class="form-input" id="support-evidence" placeholder="What evidence supports this thought?"></textarea>
</div>
<div class="form-group">
<label class="form-label">5. Evidence that doesn't support the thought</label>
<textarea class="form-input" id="against-evidence" placeholder="What evidence contradicts this thought?"></textarea>
</div>
<div class="form-group">
<label class="form-label">6. Alternative balanced thought</label>
<textarea class="form-input" id="balanced-thought" placeholder="Write a more balanced perspective..."></textarea>
</div>
<div class="form-group">
<label class="form-label">7. Rate your belief in the original thought (0-100)</label>
<input type="range" class="form-input" id="belief-rating" min="0" max="100" value="50">
<span id="belief-value">50</span>%
</div>
<div class="form-group">
<label class="form-label">8. What emotions do you feel now? (Rate 0-100)</label>
<div class="emotion-inputs">
<input type="text" class="form-input emotion-name" placeholder="Emotion (e.g., Calm)">
<input type="range" class="emotion-slider" min="0" max="100" value="30">
<span class="emotion-value">30</span>
</div>
</div>
<div class="exercise-actions">
<button class="btn btn-primary" onclick="saveThoughtRecord()">Save Exercise</button>
<button class="btn btn-secondary" onclick="closeExercise('thought-record-exercise')">Cancel</button>
</div>
</div>
</div>
<!-- Mindfulness Exercise -->
<div id="mindfulness-exercise" class="card exercise-modal" style="display: none;">
<h3>Mindfulness Exercise</h3>
<div class="mindfulness-content">
<div class="timer-display">
<span id="mindfulness-timer">5:00</span>
</div>
<div class="mindfulness-instructions">
<p>Find a comfortable position and focus on your breath.</p>
<p>Notice the sensation of breathing in and out.</p>
<p>When your mind wanders, gently bring it back to your breath.</p>
</div>
<div class="exercise-actions">
<button class="btn btn-primary" onclick="startMindfulnessTimer()">Start</button>
<button class="btn btn-secondary" onclick="pauseMindfulnessTimer()">Pause</button>
<button class="btn btn-secondary" onclick="closeExercise('mindfulness-exercise')">Close</button>
</div>
</div>
</div>
<!-- Gratitude Exercise -->
<div id="gratitude-exercise" class="card exercise-modal" style="display: none;">
<h3>Gratitude Exercise</h3>
<div class="gratitude-content">
<p class="exercise-intro">List three things you're grateful for today:</p>
<div class="gratitude-list">
<div class="form-group">
<input type="text" class="form-input" placeholder="1. I'm grateful for..." id="gratitude-1">
</div>
<div class="form-group">
<input type="text" class="form-input" placeholder="2. I'm grateful for..." id="gratitude-2">
</div>
<div class="form-group">
<input type="text" class="form-input" placeholder="3. I'm grateful for..." id="gratitude-3">
</div>
</div>
<div class="form-group">
<label class="form-label">Why are these things meaningful to you?</label>
<textarea class="form-input" id="gratitude-reflection" placeholder="Reflect on why these things matter..."></textarea>
</div>
<div class="exercise-actions">
<button class="btn btn-primary" onclick="saveGratitude()">Save Exercise</button>
<button class="btn btn-secondary" onclick="closeExercise('gratitude-exercise')">Cancel</button>
</div>
</div>
</div>
<!-- Breathing Exercise -->
<div id="breathing-exercise" class="card exercise-modal" style="display: none;">
<h3>Breathing Exercise</h3>
<div class="breathing-content">
<div class="breathing-circle" id="breathing-circle">
<div class="breathing-text" id="breathing-text">Ready</div>
</div>
<div class="breathing-instructions">
<p>Follow the circle's rhythm: breathe in as it expands, breathe out as it contracts.</p>
</div>
<div class="exercise-actions">
<button class="btn btn-primary" onclick="startBreathingExercise()">Start</button>
<button class="btn btn-secondary" onclick="stopBreathingExercise()">Stop</button>
<button class="btn btn-secondary" onclick="closeExercise('breathing-exercise')">Close</button>
</div>
</div>
</div>
</section>
<!-- Progress Section -->
<section id="progress" class="section">
<div class="card">
<h2 class="card-title">Your Progress</h2>
<div class="progress-container" id="progress-stats">
<!-- Progress stats will be dynamically inserted here -->
</div>
</div>
<div class="card">
<h3 class="card-title">Weekly Activity</h3>
<div class="chart-container" style="margin-top: 16px;">
<canvas id="weeklyChart" width="400" height="200"></canvas>
</div>
</div>
<div class="card">
<h3 class="card-title">Recent Entries</h3>
<div style="margin-top: 16px;">
<div style="padding: 12px 0; border-bottom: 1px solid var(--outline);">
<div style="display: flex; justify-content: space-between; align-items: center;">
<span>Today - Feeling Anxious</span>
<span style="color: var(--primary);">Completed</span>
</div>
</div>
<div style="padding: 12px 0; border-bottom: 1px solid var(--outline);">
<div style="display: flex; justify-content: space-between; align-items: center;">
<span>Yesterday - Feeling Stressed</span>
<span style="color: var(--primary);">Completed</span>
</div>
</div>
<div style="padding: 12px 0;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<span>2 days ago - Feeling Happy</span>
<span style="color: var(--success);">Great!</span>
</div>
</div>
</div>
</div>
</section>
<!-- Analytics Section -->
<section id="analytics" class="section">
<div class="card">
<h2 class="card-title">Mood Analytics</h2>
<div class="analytics-filters" style="margin-bottom: 24px;">
<button class="btn btn-secondary" onclick="updateAnalyticsView('week')">Week</button>
<button class="btn btn-secondary" onclick="updateAnalyticsView('month')">Month</button>
<button class="btn btn-secondary" onclick="updateAnalyticsView('year')">Year</button>
</div>
<div class="chart-container" style="margin-bottom: 24px;">
<h3 style="margin-bottom: 16px;">Mood Trends</h3>
<canvas id="moodTrendChart" width="400" height="250"></canvas>
</div>
<div class="analytics-grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px;">
<div class="analytics-card">
<h4>Most Common Emotions</h4>
<div id="commonEmotions" style="margin-top: 12px;">
<!-- Will be populated dynamically -->
</div>
</div>
<div class="analytics-card">
<h4>Exercise Preferences</h4>
<div id="exercisePrefs" style="margin-top: 12px;">
<!-- Will be populated dynamically -->
</div>
</div>
<div class="analytics-card">
<h4>Best Days</h4>
<div id="bestDays" style="margin-top: 12px;">
<!-- Will be populated dynamically -->
</div>
</div>
<div class="analytics-card">
<h4>Insights</h4>
<div id="insights" style="margin-top: 12px;">
<!-- Will be populated dynamically -->
</div>
</div>
</div>
</div>
</section>
</main>
<!-- Floating Action Button -->
<button class="fab" onclick="emergencyHelp()">
<span class="material-icons">help</span>
</button>
<!-- Bottom Navigation -->
<nav class="bottom-nav">
<button class="nav-item active" onclick="showSection('home')">
<span class="material-icons">home</span>
<span class="nav-label">Home</span>
</button>
<button class="nav-item" onclick="showSection('exercises')">
<span class="material-icons">fitness_center</span>
<span class="nav-label">Exercises</span>
</button>
<button class="nav-item" onclick="showSection('progress')">
<span class="material-icons">trending_up</span>
<span class="nav-label">Progress</span>
</button>
<button class="nav-item" onclick="showSection('analytics')">
<span class="material-icons">insights</span>
<span class="nav-label">Analytics</span>
</button>
</nav>
<script>
// Navigation
function showSection(sectionId) {
// Hide all sections
document.querySelectorAll('.section').forEach(section => {
section.classList.remove('active');
});
// Show selected section
document.getElementById(sectionId).classList.add('active');
// Update nav items
document.querySelectorAll('.nav-item').forEach(item => {
item.classList.remove('active');
});
event.target.closest('.nav-item').classList.add('active');
// Load analytics data if analytics section is shown
if (sectionId === 'analytics') {
loadAnalyticsData();
}
}
// Mood selection
function selectMood(element, mood) {
// Remove previous selection
document.querySelectorAll('.mood-card').forEach(card => {
card.classList.remove('selected');
});
// Add selection to clicked mood
element.classList.add('selected');
// Store selected mood
localStorage.setItem('selectedMood', mood);
}
// Intensity slider
function updateIntensity(value) {
document.getElementById('intensityValue').textContent = value;
localStorage.setItem('moodIntensity', value);
}
// Show situation builder
function showSituationBuilder() {
showSection('situation-builder');
document.querySelectorAll('.nav-item').forEach(item => {
item.classList.remove('active');
});
}
// Generate exercises
function generateExercises() {
showSection('exercises');
document.querySelectorAll('.nav-item').forEach(item => {
item.classList.remove('active');
});
document.querySelectorAll('.nav-item')[1].classList.add('active');
}
// Start exercise
function startExercise(type) {
alert(`Starting ${type} exercise... This would open the exercise interface.`);
}
// Quick actions
function quickRelax() {
alert('Starting quick relaxation exercise...');
}
function breathingExercise() {
alert('Starting breathing exercise...');
}
// Emergency help
function emergencyHelp() {
alert('If you\'re in crisis, please call emergency services or a crisis hotline.');
}
// Initialize
document.addEventListener('DOMContentLoaded', function() {
// Create floating particles
createParticles();
// Add fade-in animation to cards
document.querySelectorAll('.card').forEach((card, index) => {
card.style.animationDelay = `${index * 0.1}s`;
card.classList.add('fade-in');
});
// Add motivational quotes rotation
startQuoteRotation();
// Initialize emotion sliders
initializeEmotionSliders();
// Initialize belief rating slider
initializeBeliefSlider();
// Load saved data
loadSavedData();
});
// Create floating particles
function createParticles() {
const particlesContainer = document.getElementById('particles');
const particleCount = 20;
for (let i = 0; i < particleCount; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.left = Math.random() * 100 + '%';
particle.style.animationDelay = Math.random() * 6 + 's';
particle.style.animationDuration = (Math.random() * 3 + 3) + 's';
particlesContainer.appendChild(particle);
}
}
// Motivational quotes
const quotes = [
"You are stronger than you think! 💪",
"Every moment is a fresh beginning! 🌅",
"Your potential is limitless! ✨",
"You've got this! Keep going! 🚀",
"Believe in yourself! 🌟"
];
function startQuoteRotation() {
let quoteIndex = 0;
setInterval(() => {
quoteIndex = (quoteIndex + 1) % quotes.length;
// You can display these quotes in a dedicated element
}, 5000);
}
// Exercise Functions
function startThoughtRecord() {
document.getElementById('thought-record-exercise').style.display = 'block';
document.getElementById('exercises').classList.add('blur-background');
}
function startMindfulness() {
document.getElementById('mindfulness-exercise').style.display = 'block';
document.getElementById('exercises').classList.add('blur-background');
}
function startGratitude() {
document.getElementById('gratitude-exercise').style.display = 'block';
document.getElementById('exercises').classList.add('blur-background');
}
function startBreathing() {
document.getElementById('breathing-exercise').style.display = 'block';
document.getElementById('exercises').classList.add('blur-background');
}
function closeExercise(exerciseId) {
document.getElementById(exerciseId).style.display = 'none';
document.getElementById('exercises').classList.remove('blur-background');
}
// Thought Record Functions
function addEmotionInput() {
const emotionContainer = document.querySelector('.emotion-inputs').parentElement;
const newEmotionInput = document.createElement('div');
newEmotionInput.className = 'emotion-inputs';
newEmotionInput.innerHTML = `
<input type="text" class="form-input emotion-name" placeholder="Emotion">
<input type="range" class="emotion-slider" min="0" max="100" value="50">
<span class="emotion-value">50</span>
`;
emotionContainer.insertBefore(newEmotionInput, emotionContainer.lastElementChild);
initializeEmotionSliders();
}
function initializeEmotionSliders() {
document.querySelectorAll('.emotion-slider').forEach(slider => {
const valueDisplay = slider.nextElementSibling;
slider.addEventListener('input', function() {
valueDisplay.textContent = this.value;
});
});
}
function initializeBeliefSlider() {
const beliefSlider = document.getElementById('belief-rating');
const beliefValue = document.getElementById('belief-value');
if (beliefSlider && beliefValue) {
beliefSlider.addEventListener('input', function() {
beliefValue.textContent = this.value;
});
}
}
function saveThoughtRecord() {
const thoughtRecord = {
date: new Date().toISOString(),
situation: document.getElementById('situation').value,
emotions: getEmotions(),
autoThoughts: document.getElementById('auto-thoughts').value,
supportEvidence: document.getElementById('support-evidence').value,
againstEvidence: document.getElementById('against-evidence').value,
balancedThought: document.getElementById('balanced-thought').value,
beliefRating: document.getElementById('belief-rating').value,
newEmotions: getNewEmotions()
};
saveToLocalStorage('thoughtRecords', thoughtRecord);
showSuccessMessage('Thought record saved successfully!');
closeExercise('thought-record-exercise');
updateProgress();
}
function getEmotions() {
const emotions = [];
const emotionInputs = document.querySelectorAll('.emotion-inputs');
emotionInputs.forEach(input => {
const name = input.querySelector('.emotion-name').value;
const value = input.querySelector('.emotion-slider').value;
if (name) emotions.push({ name, value });
});
return emotions;
}
function getNewEmotions() {
// Similar to getEmotions but for the new emotions section
return [];
}
// Mindfulness Timer Functions
let mindfulnessTimer;
let mindfulnessSeconds = 300; // 5 minutes
function startMindfulnessTimer() {
mindfulnessTimer = setInterval(() => {
mindfulnessSeconds--;
updateMindfulnessDisplay();
if (mindfulnessSeconds <= 0) {
pauseMindfulnessTimer();
showSuccessMessage('Mindfulness exercise completed!');
}
}, 1000);
}
function pauseMindfulnessTimer() {
clearInterval(mindfulnessTimer);
}
function updateMindfulnessDisplay() {
const minutes = Math.floor(mindfulnessSeconds / 60);
const seconds = mindfulnessSeconds % 60;
document.getElementById('mindfulness-timer').textContent =
`${minutes}:${seconds.toString().padStart(2, '0')}`;
}
// Gratitude Functions
function saveGratitude() {
const gratitude = {
date: new Date().toISOString(),
items: [
document.getElementById('gratitude-1').value,
document.getElementById('gratitude-2').value,
document.getElementById('gratitude-3').value
],
reflection: document.getElementById('gratitude-reflection').value
};
saveToLocalStorage('gratitudeEntries', gratitude);
showSuccessMessage('Gratitude entry saved!');
closeExercise('gratitude-exercise');
updateProgress();
}
// Breathing Exercise Functions
let breathingInterval;
let breathingPhase = 'inhale';
function startBreathingExercise() {
breathingInterval = setInterval(() => {
const circle = document.getElementById('breathing-circle');
const text = document.getElementById('breathing-text');
if (breathingPhase === 'inhale') {
circle.classList.remove('exhale');
circle.classList.add('inhale');
text.textContent = 'Breathe In';
breathingPhase = 'hold';
} else if (breathingPhase === 'hold') {
text.textContent = 'Hold';
breathingPhase = 'exhale';
} else {
circle.classList.remove('inhale');
circle.classList.add('exhale');
text.textContent = 'Breathe Out';
breathingPhase = 'inhale';
}
}, 4000);
}
function stopBreathingExercise() {
clearInterval(breathingInterval);
const circle = document.getElementById('breathing-circle');
const text = document.getElementById('breathing-text');
circle.classList.remove('inhale', 'exhale');
text.textContent = 'Ready';
breathingPhase = 'inhale';
}
// Storage Functions
function saveToLocalStorage(key, data) {
const existingData = JSON.parse(localStorage.getItem(key) || '[]');
existingData.push(data);
localStorage.setItem(key, JSON.stringify(existingData));
}
function loadSavedData() {
// Load any saved data and update UI accordingly
const thoughtRecords = JSON.parse(localStorage.getItem('thoughtRecords') || '[]');
const gratitudeEntries = JSON.parse(localStorage.getItem('gratitudeEntries') || '[]');
// Initialize notification system
initializeNotifications();
// Update progress based on saved data
updateProgress();
}
// Progress Functions
function updateProgress() {
const thoughtRecords = JSON.parse(localStorage.getItem('thoughtRecords') || '[]');
const gratitudeEntries = JSON.parse(localStorage.getItem('gratitudeEntries') || '[]');
// Calculate stats
const totalSessions = thoughtRecords.length + gratitudeEntries.length;
const dayStreak = calculateDayStreak(thoughtRecords, gratitudeEntries);
const avgMood = calculateAverageMood(thoughtRecords);
const improvement = calculateImprovement(thoughtRecords);
// Update progress stats
const progressContainer = document.getElementById('progress-stats');
progressContainer.innerHTML = `
<div class="progress-card">
<div class="progress-value">${dayStreak}</div>
<div class="progress-label">Day Streak</div>
</div>
<div class="progress-card">
<div class="progress-value">${totalSessions}</div>
<div class="progress-label">Sessions</div>
</div>
<div class="progress-card">
<div class="progress-value">${improvement}%</div>
<div class="progress-label">Improvement</div>
</div>
<div class="progress-card">
<div class="progress-value">${avgMood}</div>
<div class="progress-label">Avg Mood</div>
</div>
`;
// Draw weekly chart
drawWeeklyChart(thoughtRecords, gratitudeEntries);
}
function calculateDayStreak(thoughtRecords, gratitudeEntries) {
const allDates = [...thoughtRecords, ...gratitudeEntries]
.map(entry => new Date(entry.date).toDateString())
.filter((date, index, arr) => arr.indexOf(date) === index);
if (allDates.length === 0) return 0;
allDates.sort((a, b) => new Date(b) - new Date(a));
let streak = 0;
const today = new Date().toDateString();
let checkDate = new Date();
for (let i = 0; i < allDates.length; i++) {
if (allDates[i] === checkDate.toDateString()) {
streak++;
checkDate.setDate(checkDate.getDate() - 1);
} else if (i > 0) {
break;
}
}
return streak;
}
function calculateAverageMood(thoughtRecords) {
if (thoughtRecords.length === 0) return '0.0';
const totalMood = thoughtRecords.reduce((sum, record) => {
const newEmotions = record.newEmotions || {};
const moodValue = Object.values(newEmotions).reduce((s, v) => s + parseInt(v), 0) /
Object.keys(newEmotions).length || 0;
return sum + moodValue;
}, 0);
return (totalMood / thoughtRecords.length).toFixed(1);
}
function calculateImprovement(thoughtRecords) {
if (thoughtRecords.length < 2) return 0;
const recent = thoughtRecords.slice(-7);
const earlier = thoughtRecords.slice(0, Math.min(7, thoughtRecords.length - recent.length));
const avgRecent = calculateAverageMood(recent);
const avgEarlier = calculateAverageMood(earlier);
return Math.max(0, Math.round(((avgRecent - avgEarlier) / 10) * 100));
}
function drawWeeklyChart(thoughtRecords, gratitudeEntries) {
const canvas = document.getElementById('weeklyChart');
if (!canvas) return;
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
// Clear canvas
ctx.clearRect(0, 0, width, height);
// Get last 7 days
const days = [];
const counts = [];
for (let i = 6; i >= 0; i--) {
const date = new Date();
date.setDate(date.getDate() - i);
const dateStr = date.toDateString();
const dayCount = thoughtRecords.filter(r =>
new Date(r.date).toDateString() === dateStr
).length + gratitudeEntries.filter(e =>
new Date(e.date).toDateString() === dateStr
).length;
days.push(date.toLocaleDateString('en', { weekday: 'short' }));
counts.push(dayCount);
}
// Draw chart
const maxValue = Math.max(...counts, 5);
const barWidth = width / 7 * 0.6;
const spacing = width / 7;
ctx.fillStyle = '#FF6B6B';
ctx.font = '12px sans-serif';
ctx.textAlign = 'center';
counts.forEach((count, index) => {
const barHeight = (count / maxValue) * (height - 40);
const x = index * spacing + (spacing - barWidth) / 2;
const y = height - barHeight - 20;
// Draw bar
ctx.fillRect(x, y, barWidth, barHeight);
// Draw day label
ctx.fillStyle = '#666';
ctx.fillText(days[index], x + barWidth / 2, height - 5);
// Draw count
if (count > 0) {
ctx.fillStyle = '#FF6B6B';
ctx.fillText(count, x + barWidth / 2, y - 5);
}
});
}
// Analytics Functions
let currentAnalyticsView = 'week';
function updateAnalyticsView(view) {
currentAnalyticsView = view;
// Update button states
document.querySelectorAll('.analytics-filters .btn').forEach(btn => {
btn.classList.remove('btn-primary');
btn.classList.add('btn-secondary');
});
event.target.classList.remove('btn-secondary');
event.target.classList.add('btn-primary');
// Update analytics data
loadAnalyticsData();
}
function loadAnalyticsData() {
const thoughtRecords = JSON.parse(localStorage.getItem('thoughtRecords') || '[]');
const gratitudeEntries = JSON.parse(localStorage.getItem('gratitudeEntries') || '[]');
// Update mood trend chart
drawMoodTrendChart(thoughtRecords);
// Update common emotions
updateCommonEmotions(thoughtRecords);
// Update exercise preferences
updateExercisePreferences(thoughtRecords, gratitudeEntries);
// Update best days
updateBestDays(thoughtRecords, gratitudeEntries);
// Update insights
updateInsights(thoughtRecords, gratitudeEntries);
}
function drawMoodTrendChart(thoughtRecords) {
const canvas = document.getElementById('moodTrendChart');
if (!canvas) return;
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
// Clear canvas
ctx.clearRect(0, 0, width, height);
// Filter records based on current view
const daysToShow = currentAnalyticsView === 'week' ? 7 :
currentAnalyticsView === 'month' ? 30 : 365;
const dates = [];
const moods = [];
for (let i = daysToShow - 1; i >= 0; i--) {
const date = new Date();
date.setDate(date.getDate() - i);
const dateStr = date.toDateString();
const dayRecords = thoughtRecords.filter(r =>
new Date(r.date).toDateString() === dateStr
);
const avgMood = dayRecords.length > 0 ?
calculateAverageMood(dayRecords) : 0;
dates.push(date.toLocaleDateString('en', {
month: 'short',
day: 'numeric',
...(currentAnalyticsView === 'year' && { year: 'numeric' })
}));
moods.push(parseFloat(avgMood));
}
// Draw line chart
const padding = 40;
const graphWidth = width - padding * 2;
const graphHeight = height - padding * 2;
const maxMood = Math.max(...moods, 10);
// Draw axes
ctx.strokeStyle = '#ddd';
ctx.beginPath();
ctx.moveTo(padding, padding);
ctx.lineTo(padding, height - padding);
ctx.lineTo(width - padding, height - padding);
ctx.stroke();
// Draw mood line
ctx.strokeStyle = '#FF6B6B';
ctx.lineWidth = 2;
ctx.beginPath();
moods.forEach((mood, index) => {
const x = padding + (index / (moods.length - 1)) * graphWidth;
const y = height - padding - (mood / maxMood) * graphHeight;
if (index === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
// Draw points
ctx.fillStyle = '#FF6B6B';
moods.forEach((mood, index) => {
const x = padding + (index / (moods.length - 1)) * graphWidth;
const y = height - padding - (mood / maxMood) * graphHeight;
ctx.beginPath();
ctx.arc(x, y, 4, 0, Math.PI * 2);
ctx.fill();
});
// Draw labels
ctx.fillStyle = '#666';
ctx.font = '10px sans-serif';
ctx.textAlign = 'center';
const labelInterval = Math.ceil(dates.length / 7);
dates.forEach((date, index) => {
if (index % labelInterval === 0 || index === dates.length - 1) {
const x = padding + (index / (dates.length - 1)) * graphWidth;
ctx.fillText(date, x, height - padding + 20);
}
});
}
function updateCommonEmotions(thoughtRecords) {
const emotionCounts = {};
thoughtRecords.forEach(record => {
const emotions = record.emotions || {};
Object.keys(emotions).forEach(emotion => {
emotionCounts[emotion] = (emotionCounts[emotion] || 0) + 1;
});
});
const sortedEmotions = Object.entries(emotionCounts)
.sort((a, b) => b[1] - a[1])
.slice(0, 5);
const container = document.getElementById('commonEmotions');
const maxCount = sortedEmotions[0]?.[1] || 1;
container.innerHTML = sortedEmotions.map(([emotion, count]) => `
<div class="emotion-bar">
<span class="emotion-label">${emotion}</span>
<div class="emotion-progress">
<div class="emotion-progress-fill" style="width: ${(count / maxCount) * 100}%"></div>
</div>
<span class="emotion-value">${count}</span>
</div>
`).join('');
}
function updateExercisePreferences(thoughtRecords, gratitudeEntries) {
const thoughtCount = thoughtRecords.length;
const gratitudeCount = gratitudeEntries.length;
const total = thoughtCount + gratitudeCount || 1;
const container = document.getElementById('exercisePrefs');
container.innerHTML = `
<div class="emotion-bar">
<span class="emotion-label">Thought Records</span>
<div class="emotion-progress">
<div class="emotion-progress-fill" style="width: ${(thoughtCount / total) * 100}%"></div>
</div>
<span class="emotion-value">${Math.round((thoughtCount / total) * 100)}%</span>
</div>
<div class="emotion-bar">
<span class="emotion-label">Gratitude</span>
<div class="emotion-progress">
<div class="emotion-progress-fill" style="width: ${(gratitudeCount / total) * 100}%"></div>
</div>
<span class="emotion-value">${Math.round((gratitudeCount / total) * 100)}%</span>
</div>
`;
}
function updateBestDays(thoughtRecords, gratitudeEntries) {
const dayMoods = {};
// Calculate average mood for each day of week
for (let i = 0; i < 7; i++) {
const dayName = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][i];
const dayRecords = thoughtRecords.filter(r =>
new Date(r.date).getDay() === i
);
if (dayRecords.length > 0) {
dayMoods[dayName] = calculateAverageMood(dayRecords);
}
}
const sortedDays = Object.entries(dayMoods)
.sort((a, b) => b[1] - a[1])
.slice(0, 3);
const container = document.getElementById('bestDays');
container.innerHTML = sortedDays.map(([day, mood]) => `
<div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
<span>${day}</span>
<span style="font-weight: 500;">${mood}</span>
</div>
`).join('') || '<p style="color: #666; font-size: 14px;">Not enough data</p>';
}
function updateInsights(thoughtRecords, gratitudeEntries) {
const insights = [];
// Generate insights based on data
if (thoughtRecords.length > 5) {
const recentMoods = thoughtRecords.slice(-5).map(r => calculateAverageMood([r]));
const avgRecent = recentMoods.reduce((a, b) => a + parseFloat(b), 0) / recentMoods.length;
if (avgRecent > 7) {
insights.push("Your mood has been improving! Keep it up!");
} else if (avgRecent < 4) {
insights.push("Consider trying more exercises to boost your mood");
}
}
if (gratitudeEntries.length > thoughtRecords.length) {
insights.push("You're doing great with gratitude practice!");
}
const container = document.getElementById('insights');
container.innerHTML = insights.length > 0 ?
insights.map(insight => `<p style="margin-bottom: 8px; font-size: 14px;">💡 ${insight}</p>`).join('') :
'<p style="color: #666; font-size: 14px;">Continue using the app to see insights</p>';
}
// Notification System
let notifications = [];
let notificationPermission = 'default';
function initializeNotifications() {
// Request notification permission
if ('Notification' in window) {
Notification.requestPermission().then(permission => {
notificationPermission = permission;
});
}
// Load saved notifications
const savedNotifications = JSON.parse(localStorage.getItem('notifications') || '[]');
notifications = savedNotifications;
updateNotificationBadge();
// Schedule daily reminders
scheduleDailyReminders();
}
function toggleNotifications() {
const dropdown = document.getElementById('notification-dropdown');
const isVisible = dropdown.style.display === 'block';
if (isVisible) {
dropdown.style.display = 'none';
} else {
dropdown.style.display = 'block';
renderNotifications();
}
}
function addNotification(title, message, type = 'info') {
const notification = {
id: Date.now(),
title,
message,
type,
timestamp: new Date().toISOString(),
read: false
};
notifications.unshift(notification);
// Keep only last 20 notifications
if (notifications.length > 20) {
notifications = notifications.slice(0, 20);
}
// Save to localStorage
localStorage.setItem('notifications', JSON.stringify(notifications));
// Update badge
updateNotificationBadge();
// Show browser notification if permission granted
if (notificationPermission === 'granted') {
new Notification(title, {
body: message,
icon: '/favicon.ico'
});
}
}
function updateNotificationBadge() {
const badge = document.getElementById('notification-badge');
const unreadCount = notifications.filter(n => !n.read).length;
if (unreadCount > 0) {
badge.textContent = unreadCount;
badge.style.display = 'block';
} else {
badge.style.display = 'none';
}
}
function renderNotifications() {
const list = document.getElementById('notification-list');
if (notifications.length === 0) {
list.innerHTML = '<p style="color: #666; text-align: center;">No notifications yet</p>';
return;
}
list.innerHTML = notifications.map(notification => {
const timeAgo = getTimeAgo(new Date(notification.timestamp));
const icon = notification.type === 'success' ? 'check_circle' :
notification.type === 'reminder' ? 'notifications' : 'info';
return `
<div class="notification-item ${notification.read ? 'read' : ''}"
onclick="markNotificationRead(${notification.id})">
<div style="display: flex; align-items: flex-start; gap: 12px;">
<span class="material-icons" style="color: var(--primary); font-size: 20px;">
${icon}
</span>
<div style="flex: 1;">
<div style="font-weight: 500; margin-bottom: 4px;">${notification.title}</div>
<div style="font-size: 14px; color: var(--on-surface);">${notification.message}</div>
<div class="notification-time">${timeAgo}</div>
</div>
</div>
</div>
`;
}).join('');
}
function markNotificationRead(id) {
const notification = notifications.find(n => n.id === id);
if (notification) {
notification.read = true;
localStorage.setItem('notifications', JSON.stringify(notifications));
updateNotificationBadge();
renderNotifications();
}
}
function getTimeAgo(date) {
const seconds = Math.floor((new Date() - date) / 1000);
if (seconds < 60) return 'Just now';
if (seconds < 3600) return `${Math.floor(seconds / 60)} minutes ago`;
if (seconds < 86400) return `${Math.floor(seconds / 3600)} hours ago`;
if (seconds < 604800) return `${Math.floor(seconds / 86400)} days ago`;
return date.toLocaleDateString();
}
function scheduleDailyReminders() {
// Check if we should show a reminder
const lastReminder = localStorage.getItem('lastReminder');
const today = new Date().toDateString();
if (lastReminder !== today) {
// Schedule reminder for 9 AM
const now = new Date();
const reminderTime = new Date();
reminderTime.setHours(9, 0, 0, 0);
if (reminderTime <= now) {
// If it's already past 9 AM, show reminder now
showDailyReminder();
} else {
// Schedule for 9 AM
const msUntilReminder = reminderTime - now;
setTimeout(showDailyReminder, msUntilReminder);
}
}
}
function showDailyReminder() {
const today = new Date().toDateString();
localStorage.setItem('lastReminder', today);
addNotification(
'Daily Check-in',
"How are you feeling today? Take a moment to check in with your emotions.",
'reminder'
);
}
// Close notification dropdown when clicking outside
document.addEventListener('click', function(event) {
const notificationBtn = document.getElementById('notification-btn');
const dropdown = document.getElementById('notification-dropdown');
if (!notificationBtn.contains(event.target) && !dropdown.contains(event.target)) {
dropdown.style.display = 'none';
}
});
// UI Helper Functions
function showSuccessMessage(message) {
// Create and show a success message
const successDiv = document.createElement('div');
successDiv.className = 'success-message';
successDiv.textContent = message;
successDiv.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: var(--success);
color: white;
padding: 16px 24px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 2000;
animation: slideIn 0.3s ease;
`;
document.body.appendChild(successDiv);
setTimeout(() => {
successDiv.remove();
}, 3000);
}
// PWA Install Prompt
let deferredPrompt;
let installButtonShown = false;
window.addEventListener('beforeinstallprompt', (e) => {
// Prevent the mini-infobar from appearing on mobile
e.preventDefault();
// Stash the event so it can be triggered later
deferredPrompt = e;
// Show install button after user interacts with app
if (!installButtonShown) {
setTimeout(() => {
showInstallButton();
}, 5000);
installButtonShown = true;
}
});
// Show install button
function showInstallButton() {
const installDiv = document.createElement('div');
installDiv.className = 'install-prompt';
installDiv.innerHTML = `
<div class="install-content">
<span class="material-icons">get_app</span>
<span>Install MindShift for a better experience</span>
<button onclick="installApp()">Install</button>
<button onclick="dismissInstall(this)">Not now</button>
</div>
`;
document.body.appendChild(installDiv);
setTimeout(() => {
installDiv.classList.add('show');
}, 100);
}
// Install app
function installApp() {
if (deferredPrompt) {
deferredPrompt.prompt();
deferredPrompt.userChoice.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the install prompt');
// Track installation
if (typeof gtag !== 'undefined') {
gtag('event', 'pwa_install', {
'event_category': 'PWA',
'event_label': 'App Installed'
});
}
} else {
console.log('User dismissed the install prompt');
}
deferredPrompt = null;
});
}
dismissInstall(document.querySelector('.install-prompt button:nth-child(2)'));
}
// Dismiss install prompt
function dismissInstall(button) {
const prompt = button.closest('.install-prompt');
prompt.classList.remove('show');
setTimeout(() => {
prompt.remove();
}, 300);
}
// Service Worker Registration
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js')
.then(function(registration) {
console.log('ServiceWorker registration successful with scope: ', registration.scope);
// Check for updates
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
// Show update notification
showUpdateNotification();
}
});
});
})
.catch(function(err) {
console.log('ServiceWorker registration failed: ', err);
});
});
}
// Show update notification
function showUpdateNotification() {
const updateDiv = document.createElement('div');
updateDiv.className = 'update-notification';
updateDiv.innerHTML = `
<div class="update-content">
<span class="material-icons">system_update</span>
<span>A new version is available!</span>
<button onclick="updateApp()">Update</button>
<button onclick="dismissUpdate(this)">Dismiss</button>
</div>
`;
document.body.appendChild(updateDiv);
setTimeout(() => {
updateDiv.classList.add('show');
}, 100);
}
// Update app
function updateApp() {
window.location.reload();
}
// Dismiss update notification
function dismissUpdate(button) {
const notification = button.closest('.update-notification');
notification.classList.remove('show');
setTimeout(() => {
notification.remove();
}, 300);
}
</script>
<!-- Update Notification Styles -->
<style>
.update-notification {
position: fixed;
bottom: -100px;
left: 0;
right: 0;
background: linear-gradient(135deg, var(--primary), var(--secondary));
color: white;
padding: 16px;
box-shadow: 0 -4px 20px rgba(0,0,0,0.2);
transition: bottom 0.3s ease;
z-index: 1000;
}
.update-notification.show {
bottom: 0;
}
.update-content {
max-width: 600px;
margin: 0 auto;
display: flex;
align-items: center;
gap: 12px;
}
.update-content button {
background: rgba(255,255,255,0.2);
border: 1px solid rgba(255,255,255,0.3);
color: white;
padding: 8px 16px;
border-radius: 20px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
}
.update-content button:hover {
background: rgba(255,255,255,0.3);
}
.install-prompt {
position: fixed;
bottom: -100px;
left: 0;
right: 0;
background: linear-gradient(135deg, var(--tertiary), var(--peace));
color: white;
padding: 16px;
box-shadow: 0 -4px 20px rgba(0,0,0,0.2);
transition: bottom 0.3s ease;
z-index: 1000;
}
.install-prompt.show {
bottom: 0;
}
.install-content {
max-width: 600px;
margin: 0 auto;
display: flex;
align-items: center;
gap: 12px;
}
.install-content button {
background: rgba(255,255,255,0.2);
border: 1px solid rgba(255,255,255,0.3);
color: white;
padding: 8px 16px;
border-radius: 20px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
}
.install-content button:hover {
background: rgba(255,255,255,0.3);
}
.install-content button:first-of-type {
background: rgba(255,255,255,0.3);
font-weight: 500;
}
</style>
</body>
</html>