2651 lines
93 KiB
HTML
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> |