Initial commit of MindShift CBT Therapy app

This commit is contained in:
Gemini AI
2025-12-06 16:02:48 +04:00
Unverified
commit 0ba7af6489
23 changed files with 14352 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
node_modules/
.env
dist/
build/
*.log
.DS_Store
*.db
MindShift-CBT-Therapy-Windows-Source.zip

View File

@@ -0,0 +1,93 @@
# MindShift CBT Therapy - Windows 11 Version
A powerful Windows 11 application for Cognitive Behavioral Therapy (CBT) self-help, designed to help you shift negative moods into positive ones through structured exercises and mood tracking.
## Windows 11 Features
- **Native Windows 11 Integration**: Seamlessly integrates with Windows 11 taskbar and notification system
- **Mica Effect**: Supports Windows 11's Mica transparency effect on compatible systems
- **Jump List Tasks**: Quick access to breathing exercises directly from the taskbar
- **Native Notifications**: Windows 11 native notification system for reminders and achievements
- **Data Import/Export**: Easily backup and restore your therapy data
## Installation
### Running from Source (Recommended)
1. Ensure you have Node.js installed (https://nodejs.org/)
2. Download the `MindShift-CBT-Therapy-Windows-Source.zip` file
3. Extract to your desired location
4. Double-click `Start MindShift CBT Therapy.bat`
5. The app will automatically install dependencies and launch
Note: The first run may take a few minutes as it downloads and installs dependencies.
## Features
### Core CBT Exercises
- **Thought Records**: Challenge and reframe negative thoughts
- **Mindfulness Exercises**: Practice present-moment awareness
- **Gratitude Journal**: Cultivate positive thinking patterns
- **Breathing Exercises**: Quick anxiety relief through guided breathing
### Situation Builder
Build your current mood state from different dimensions:
- Emotional state
- Physical sensations
- Thought patterns
- Environmental factors
### Progress Tracking
- Daily mood tracking
- Weekly activity charts
- Mood trend analysis
- Personalized insights
## Data Management
### Export Your Data
1. Click the menu button (☰)
2. Select "Export Data"
3. Choose a location to save your backup file
### Import Your Data
1. Click the menu button (☰)
2. Select "Import Data"
3. Choose your backup file to restore
## System Requirements
- Windows 10 (version 1903) or Windows 11
- 4GB RAM minimum
- 100MB free disk space
- Internet connection for initial setup
## Troubleshooting
### App Won't Start
- Ensure Node.js is installed (Option 1 only)
- Try running as administrator
- Check Windows Defender isn't blocking the app
### Notifications Not Working
- Check Windows notification settings
- Ensure notifications are enabled for MindShift in Windows Settings
### Data Not Saving
- Check disk space
- Try exporting your data as a backup
- Restart the application
## Privacy
MindShift stores all your data locally on your Windows device. No data is sent to external servers. Your therapy journey remains completely private.
## Support
For support or feature requests, please contact:
- Email: support@mindshift.app
- Website: https://mindshift.app
---
© 2024 MindShift Team. All rights reserved.

View File

@@ -0,0 +1,59 @@
@echo off
title MindShift CBT Therapy - Windows 11
color 0A
echo.
echo ========================================
echo MindShift CBT Therapy for Windows 11
echo ========================================
echo.
REM Check if Node.js is installed
node --version >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Node.js is not installed.
echo.
echo Please install Node.js from https://nodejs.org/ and try again.
echo.
pause
exit /b 1
)
echo [SUCCESS] Node.js is installed
echo.
REM Check if dependencies are installed
if not exist "node_modules\electron" (
echo [INFO] Installing dependencies for the first time...
echo This may take a few minutes on first run...
echo.
call npm install
if %errorlevel% neq 0 (
echo.
echo [ERROR] Failed to install dependencies.
echo Please check your internet connection and try again.
echo.
pause
exit /b 1
)
echo.
echo [SUCCESS] Dependencies installed successfully!
echo.
)
REM Start the app
echo [INFO] Launching MindShift CBT Therapy...
echo.
call npm start
if %errorlevel% neq 0 (
echo.
echo [ERROR] Failed to start the application.
echo.
pause
exit /b 1
)
echo.
echo Thank you for using MindShift CBT Therapy!
echo.
pause

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square70x70logo src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNzAiIGhlaWdodD0iNzAiIHZpZXdCb3g9IjAgMCA3MCA3MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjcwIiBoZWlnaHQ9IjcwIiByeD0iMTAiIGZpbGw9InVybCgjZ3JhZGllbnQwX2xpbmVhcl8zXzMpIi8+CjxwYXRoIGQ9Ik0zNSAyMUMzMi45MjkgMjEgMzEuNSAyMi45MjkgMzEuNSAyNUMzMS41IDI3LjA3MSAzMi45MjkgMjkgMzUgMjlDMzcuMDcxIDI5IDM4LjUgMjcuMDcxIDM4LjUgMjVDMzguNSAyMi45MjkgMzcuMDcxIDIxIDM1IDIxWiIgZmlsbD0id2hpdGUiLz4KPHA+PC9wPgo8cGF0aCBkPSJNMzUgMzVDMzMuMDQzIDM1IDMxLjUgMzYuNTQzIDMxLjUgMzguNUMzMS41IDQwLjQ1NyAzMy4wNDMgNDIgMzUgNDJDMzYuOTU3IDQyIDM4LjUgNDAuNDU3IDM4LjUgMzguNUMzOC41IDM2LjU0MyAzNi45NTcgMzUgMzUgMzVaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJncmFkaWVudDBfbGluZWFyXzNfMyIgeDE9IjAiIHkxPSIwIiB4Mj0iNzAiIHkyPSI3MCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjRkY2QjZCIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI0ZGQjc0RCIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPg=="/>
<square150x150logo src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUwIiBoZWlnaHQ9IjE1MCIgdmlld0JveD0iMCAwIDE1MCAxNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNTAiIGhlaWdodD0iMTUwIiByeD0iMjAiIGZpbGw9InVybCgjZ3JhZGllbnQwX2xpbmVhcl80XzQpIi8+CjxwYXRoIGQ9Ik03NSA0NUM3MC41MTc5IDQ1IDY3IDQ4LjUxNzkgNjcgNTNDNjcgNTcuNDgyMSA3MC41MTc5IDYxIDc1IDYxQzc5LjQ4MjEgNjEgODMgNTcuNDgyMSA4MyA1M0M4MyA0OC41MTc5IDc5LjQ4MjEgNDUgNzUgNDVaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNjAgNzVDNjAuMTM2NyA3MC4yMzgzIDYyLjIzODIgNjUuNDc2NyA2NS4yMzIzIDYxLjgzMzNDNjguMjI2NCA1OC4xOSA3Mi4yMjMyIDU1Ljg5MjggNzYuNzYwNiA1NS44OTI4QzgxLjI5OCA1NS44OTI4IDg1LjI5NDYgNTguMTkgODguMjg4NyA2MS44MzMzQzkxLjI4MjggNjUuNDc2NyA5My4zNjMzIDcwLjIzODMgOTMuMzI1IDc1QzkzLjI4NjcgNzkuNzYxNyA5MS4yODI4IDg0LjUyMzMgODguMjg4NyA4OC4xNjY3Qzg1LjI5NDYgOTEuODEgODEuMjk4IDk0LjEwNzIgNzYuNzYwNiA5NC4xMDcyQzcyLjIyMzIgOTQuMTA3MiA2OC4yMjY0IDkxLjgxIDY1LjIzMjMgODguMTY2N0M2Mi4yMzgyIDg0LjUyMzMgNjAuMTM2NyA3OS43NjE3IDYwIDc1WiIgZmlsbD0id2hpdGUiLz4KPHA+PC9wPgo8cGF0aCBkPSJNNzUgMTA1QzY2LjcxNTcgMTA1IDYwIDk4LjI4NDMgNjAgOTBDNjAgODEuNzE1NyA2Ni43MTU3IDc1IDc1IDc1QzgzLjI4NDMgNzUgOTAgODEuNzE1NyA5MCA5MEM5MCA5OC4yODQzIDgzLjI4NDMgMTA1IDc1IDEwNVoiIGZpbGw9IndoaXRlIi8+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9ImdyYWRpZW50MF9saW5lYXJfNF80IiB4MT0iMCIgeTE9IjAiIHgyPSIxNTAiIHkyPSIxNTAiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0ZGNkI2QiIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRkI3NEQiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4="/>
<square310x310logo src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzEwIiBoZWlnaHQ9IjMxMCIgdmlld0JveD0iMCAwIDMxMCAzMTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIzMTAiIGhlaWdodD0iMzEwIiByeD0iNDAiIGZpbGw9InVybCgjZ3JhZGllbnQwX2xpbmVhcl81XzUpIi8+CjxwYXRoIGQ9Ik0xNTUgOTNDMTQ2LjE5MyA5MyAxMzkgMTAwLjE5MyAxMzkgMTA5QzEzOSAxMTcuODA3IDE0Ni4xOTMgMTI1IDE1NSAxMjVDMTYzLjgwNyAxMjUgMTcxIDExNy44MDcgMTcxIDEwOUMxNzEgMTAwLjE5MyAxNjMuODA3IDkzIDE1NSA5M1oiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xMjQgMTU1QzEyNC4yNzMgMTQ1LjQ3NyAxMjguNDc2IDEzNS45NTMgMTM0LjQ2NSAxMjguNjY3QzE0MC40NTMgMTIxLjM4IDE0OC40NjQgMTE2Ljc4NiAxNTcuNTIxIDExNi43ODZDMTY2LjU3OCAxMTYuNzg2IDE3NC41ODkgMTIxLjM4IDE4MC41NzggMTI4LjY2N0MxODYuNTY2IDEzNS45NTMgMTkwLjcyNyAxNDUuNDc3IDE5MC42NSAxNTVDMTkwLjU3MyAxNjQuNTIzIDE4Ni41NjYgMTc0LjA0NyAxODAuNTc4IDE4MS4zMzNDMTc0LjU4OSAxODguNjIgMTY2LjU3OCAxOTMuMjE0IDE1Ny41MjEgMTkzLjIxNEMxNDguNDY0IDE5My4yMTQgMTQwLjQ1MyAxODguNjIgMTM0LjQ2NSAxODEuMzMzQzEyOC40NzYgMTc0LjA0NyAxMjQuMjczIDE2NC41MjMgMTI0IDE1NVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xNTUgMjEwQzEzMy40MzEgMjEwIDExNiAxOTIuNTY5IDExNiAxNzFDMTE2IDE0OS40MzEgMTMzLjQzMSAxMzIgMTU1IDEzMkMxNzYuNTY5IDEzMiAxOTQgMTQ5LjQzMSAxOTQgMTcxQzE5NCAxOTIuNTY5IDE3Ni41NjkgMjEwIDE1NSAyMTBaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJncmFkaWVudDBfbGluZWFyXzVfNSIgeDE9IjAiIHkxPSIwIiB4Mj0iMzEwIiB5Mj0iMzEwIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiNGRjZCNkIiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjRkZCNzREIi8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+"/>
<TileColor>#FF6B6B</TileColor>
</tile>
</msapplication>
</browserconfig>

View File

@@ -0,0 +1,27 @@
{
"name": "MindShift - CBT Therapy App",
"short_name": "MindShift",
"description": "Your personal CBT therapy companion for mood management and mental wellness",
"start_url": "/",
"display": "standalone",
"background_color": "#FFB74D",
"theme_color": "#FF6B6B",
"orientation": "portrait-primary",
"icons": [
{
"src": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkyIiBoZWlnaHQ9IjE5MiIgdmlld0JveD0iMCAwIDE5MiAxOTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxOTIiIGhlaWdodD0iMTkyIiByeD0iMjQiIGZpbGw9InVybCgjZ3JhZGllbnQwX2xpbmVhcl8xXzEpIi8+CjxwYXRoIGQ9Ik05NiA0OEM5MC40NzcyIDQ4IDg2IDUyLjQ3NzIgODYgNjhDODYgNzMuNTIyOCA5MC40NzcyIDc4IDk2IDc4QzEwMS41MjIgNzggMTA2IDczLjUyMjggMTA2IDY4QzEwNiA2Mi40NzcyIDEwMS41MjIgNDggOTYgNDhaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNzYgOTZDNzYuMDc0NSA5MC4zMTc3IDc4LjMxNzcgODQuNjQ1MiA4Mi4xODU0IDgwLjE4NTRDODYuMDUzMSA3NS43MjU2IDkxLjI1OTEgNzIuNjg5NyA5Ni43MjA5IDcyLjY4OTdDMTAyLjE4MiA3Mi42ODk3IDEwNy4zODkgNzUuNzI1NiAxMTEuMjU2IDgwLjE4NTRDMTE1LjEyNCA4NC42NDUyIDExNy4zOTUgOTAuMzE3NyAxMTcuMzU1IDk2QzExNy4zMTUgMTAxLjY4MiAxMTUuMDQ0IDEwNy4zNTUgMTExLjE3NiAxMTEuODE0QzEwNy4zMDkgMTE2LjI3NCAxMDIuMTgyIDExOS4zMSA5Ni43MjA5IDExOS4zMUM5MS4yNTkxIDExOS4zMSA4Ni4wNTMxIDExNi4yNzQgODIuMTg1NCAxMTEuODE0Qzc4LjMxNzcgMTA3LjM1NSA3Ni4wNzQ1IDEwMS42ODIgNzYgOTZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNOTYgMTI0Qzg4LjI2ODAxIDEyNCA4MiAxMTcuNzMyIDgyIDExMEM4MiAxMDIuMjY4IDg4LjI2ODAxIDk2IDk2IDk2QzEwMy43MzIgOTYgMTEwIDEwMi4yNjggMTEwIDExMEMxMTAgMTE3LjczMiAxMDMuNzMyIDEyNCA5NiAxMjRaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJncmFkaWVudDBfbGluZWFyXzFfMSIgeDE9IjAiIHkxPSIwIiB4Mj0iMTkyIiB5Mj0iMTkyIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiNGRjZCNkIiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjRkZCNzREIi8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+",
"sizes": "192x192",
"type": "image/svg+xml"
},
{
"src": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiByeD0iNjQiIGZpbGw9InVybCgjZ3JhZGllbnQwX2xpbmVhcl8yXzIpIi8+CjxwYXRoIGQ9Ik0yNTYgMTI4QzI0MS4yNTUgMTI4IDIyOSAxNDAuMjU1IDIyOSAxNTVDMjI5IDE2OS43NDUgMjQxLjI1NSAxODIgMjU2IDE4MkMyNzAuNzQ1IDE4MiAyODMgMTY5Ljc0NSAyODMgMTU1QzI4MyAxNDAuMjU1IDI3MC43NDUgMTI4IDI1NiAxMjhaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMjA0IDI1NkMyMDQuMTk4IDI0MS44MzYgMjA4LjgzNiAyMjcuNjcyIDIxNi40NjkgMjE1LjEyMkMyMjQuMTAyIDIwMi41NzIgMjM0LjMwOSAxOTIuMTg5IDI0Ni4yNTYgMTkyLjE4OUMyNTguMjAzIDE5Mi4xODkgMjY4LjQxIDIwMi41NzIgMjc2LjA0MyAyMTUuMTIyQzI4My42NzYgMjI3LjY3MiAyODguMzE0IDI0MS44MzYgMjg4LjI1NiAyNTZDMjg4LjE5OCAyNzAuMTY0IDI4My41NiAyODQuMzI4IDI3NS45MjcgMjk2Ljg3OEMyNjguMjk0IDMwOS40MjggMjU4LjA4NyAzMTkuODExIDI0Ni4xNCAzMTkuODExQzIzNC4xOTMgMzE5LjgxMSAyMjMuOTg2IDMwOS40MjggMjE2LjM1MyAyOTYuODc4QzIwOC43MiAyODQuMzI4IDIwNC4wODIgMjcwLjE2NCAyMDQgMjU2WiIgZmlsbD0id2hpdGUiLz4KPHA+PC9wPgo8cGF0aCBkPSJNMjU2IDMzMkMyMzUuMDg4IDMzMiAyMTggMzE0LjkxMiAyMTggMjk0QzIxOCAyNzMuMDg4IDIzNS4wODggMjU2IDI1NiAyNTZDMjc2LjkxMiAyNTYgMjk0IDI3My4wODggMjk0IDI5NEMyOTQgMzE0LjkxMiAyNzYuOTEyIDMzMiAyNTYgMzMyWiIgZmlsbD0id2hpdGUiLz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0iZ3JhZGllbnQwX2xpbmVhcl8yXzIiIHgxPSIwIiB5MT0iMCIgeDI9IjUxMiIgeTI9IjUxMiIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjRkY2QjZCIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI0ZGQjc0RCIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPg==",
"sizes": "512x512",
"type": "image/svg+xml"
}
],
"categories": ["health", "fitness", "lifestyle"],
"lang": "en",
"dir": "ltr",
"scope": "/",
"prefer_related_applications": false
}

View File

@@ -0,0 +1,127 @@
const CACHE_NAME = 'mindshift-v1';
const urlsToCache = [
'/',
'/cbt-therapy-app-mockup.html',
'/manifest.json'
];
// Install event - cache resources
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
// Fetch event - serve from cache when offline
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
// Clone the request
var fetchRequest = event.request.clone();
return fetch(fetchRequest).then(
function(response) {
// Check if valid response
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// Clone the response
var responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
}
).catch(function() {
// Offline fallback for HTML pages
if (event.request.destination === 'document') {
return caches.match('/cbt-therapy-app-mockup.html');
}
});
})
);
});
// Activate event - clean up old caches
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
if (cacheName !== CACHE_NAME) {
console.log('Deleting old cache:', cacheName);
return caches.delete(cacheName);
}
})
);
})
);
});
// Background sync for offline actions
self.addEventListener('sync', function(event) {
if (event.tag === 'background-sync') {
event.waitUntil(doBackgroundSync());
}
});
function doBackgroundSync() {
// Handle syncing offline data when back online
return Promise.resolve();
}
// Push notification handler
self.addEventListener('push', function(event) {
const options = {
body: event.data ? event.data.text() : 'Time for your CBT exercise!',
icon: '/data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkyIiBoZWlnaHQ9IjE5MiIgdmlld0JveD0iMCAwIDE5MiAxOTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxOTIiIGhlaWdodD0iMTkyIiByeD0iMjQiIGZpbGw9InVybCgjZ3JhZGllbnQwX2xpbmVhcl8xXzEpIi8+CjxwYXRoIGQ9Ik05NiA0OEM5MC40NzcyIDQ4IDg2IDUyLjQ3NzIgODYgNjhDODYgNzMuNTIyOCA5MC40NzcyIDc4IDk2IDc4QzEwMS41MjIgNzggMTA2IDczLjUyMjggMTA2IDY4QzEwNiA2Mi40NzcyIDEwMS41MjIgNDggOTYgNDhaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNzYgOTZDNzYuMDc0NSA5MC4zMTc3IDc4LjMxNzcgODQuNjQ1MiA4Mi4xODU0IDgwLjE4NTRDODYuMDUzMSA3NS43MjU2IDkxLjI1OTEgNzIuNjg5NyA5Ni43MjA5IDcyLjY4OTdDMTAyLjE4MiA3Mi42ODk3IDEwNy4zODkgNzUuNzI1NiAxMTEuMjU2IDgwLjE4NTRDMTE1LjEyNCA4NC42NDUyIDExNy4zOTUgOTAuMzE3NyAxMTcuMzU1IDk2QzExNy4zMTUgMTAxLjY4MiAxMTUuMDQ0IDEwNy4zNTUgMTExLjE3NiAxMTEuODE0QzEwNy4zMDkgMTE2LjI3NCAxMDIuMTgyIDExOS4zMSA5Ni43MjA5IDExOS4zMUM5MS4yNTkxIDExOS4zMSA4Ni4wNTMxIDExNi4yNzQgODIuMTg1NCAxMTEuODE0Qzc4LjMxNzcgMTA3LjM1NSA3Ni4wNzQ1IDEwMS42ODIgNzYgOTZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNOTYgMTI0Qzg4LjI2ODAxIDEyNCA4MiAxMTcuNzMyIDgyIDExMEM4MiAxMDIuMjY4IDg4LjI2ODAxIDk2IDk2IDk2QzEwMy43MzIgOTYgMTEwIDEwMi4yNjggMTEwIDExMEMxMTAgMTE3LjczMiAxMDMuNzMyIDEyNCA5NiAxMjRaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJncmFkaWVudDBfbGluZWFyXzFfMSIgeDE9IjAiIHkxPSIwIiB4Mj0iMTkyIiB5Mj0iMTkyIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiNGRjZCNkIiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjRkZCNzREIi8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+',
badge: '/data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOTYiIGhlaWdodD0iOTYiIHZpZXdCb3g9IjAgMCA5NiA5NiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9Ijk2IiBoZWlnaHQ9Ijk2IiByeD0iMTIiIGZpbGw9IiNGRjZCNkIiLz4KPC9zdmc+',
vibrate: [100, 50, 100],
data: {
dateOfArrival: Date.now(),
primaryKey: 1
},
actions: [
{
action: 'explore',
title: 'Open App',
icon: '/data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE2IDJMMjggMTZMMTYgMzBMMCAxNkwxNiAyWiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+'
},
{
action: 'close',
title: 'Dismiss',
icon: '/data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTI0IDhMMTcgMTVMMjQgMjJMMjIgMjRMMTUgMTdMOCAyNEwxMCAyMkwxNyAxNUwxMCA4TDEyIDZMMTkgMTNMMTIgNkwyNCA4WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+'
}
]
};
event.waitUntil(
self.registration.showNotification('MindShift', options)
);
});
// Notification click handler
self.addEventListener('notificationclick', function(event) {
event.notification.close();
if (event.action === 'explore') {
// Open the app
event.waitUntil(
clients.openWindow('/')
);
}
});

View File

@@ -0,0 +1,30 @@
const express = require('express');
const path = require('path');
const app = express();
const PORT = 12005;
// Serve static files from the src directory
app.use(express.static(path.join(__dirname, 'src')));
// Serve the main app
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'src', 'index.html'));
});
// Start the dev server
const server = app.listen(PORT, () => {
console.log(`Frontend dev server running on http://localhost:${PORT}`);
});
// Keep the server running
process.on('SIGINT', () => {
console.log('Shutting down dev server...');
server.close(() => {
process.exit(0);
});
});
// Prevent the process from exiting
setInterval(() => {
// Keep alive
}, 1000);

5086
MindShift-Windows/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,73 @@
{
"name": "mindshift-cbt-therapy",
"version": "1.0.0",
"description": "MindShift - Your personal CBT therapy companion for Windows 11",
"main": "src/main.js",
"homepage": "./",
"author": {
"name": "MindShift Team",
"email": "support@mindshift.app"
},
"license": "MIT",
"private": true,
"scripts": {
"start": "electron .",
"dev": "concurrently \"npm run dev-server\" \"electron . --dev\"",
"dev-server": "node dev-server.js",
"build": "electron-builder",
"build-win": "electron-builder --win",
"dist": "npm run build",
"pack": "electron-builder --dir",
"clean": "rimraf dist"
},
"build": {
"appId": "com.mindshift.cbt.therapy",
"productName": "MindShift CBT Therapy",
"directories": {
"output": "build"
},
"files": [
"src/**/*",
"assets/**/*",
"node_modules/**/*",
"package.json"
],
"win": {
"target": [
{
"target": "nsis",
"arch": [
"x64"
]
}
],
"requestedExecutionLevel": "asInvoker",
"publisherName": "MindShift Team",
"signDlls": false
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"createDesktopShortcut": true,
"createStartMenuShortcut": true,
"shortcutName": "MindShift CBT Therapy",
"include": "installer.nsh"
},
"msi": {
"oneClick": false,
"createDesktopShortcut": true,
"createStartMenuShortcut": true,
"shortcutName": "MindShift CBT Therapy"
}
},
"dependencies": {
"electron-updater": "^6.1.7"
},
"devDependencies": {
"concurrently": "^9.2.1",
"electron": "^28.1.0",
"electron-builder": "^24.9.1",
"express": "^5.2.1",
"rimraf": "^5.0.5"
}
}

View File

@@ -0,0 +1,185 @@
// API Configuration
const API_BASE_URL = 'http://localhost:12004/api';
// Authentication token management
let authToken = localStorage.getItem('authToken');
// API helper function
async function apiCall(endpoint, options = {}) {
const url = `${API_BASE_URL}${endpoint}`;
const config = {
headers: {
'Content-Type': 'application/json',
...(authToken && { 'Authorization': `Bearer ${authToken}` }),
...options.headers
},
...options
};
try {
const response = await fetch(url, config);
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'API request failed');
}
return data;
} catch (error) {
console.error('API Error:', error);
throw error;
}
}
// Authentication API
export const authAPI = {
async register(name, email, password) {
const data = await apiCall('/auth/register', {
method: 'POST',
body: JSON.stringify({ name, email, password })
});
authToken = data.token;
localStorage.setItem('authToken', authToken);
return data;
},
async login(email, password) {
const data = await apiCall('/auth/login', {
method: 'POST',
body: JSON.stringify({ email, password })
});
authToken = data.token;
localStorage.setItem('authToken', authToken);
return data;
},
async logout() {
await apiCall('/auth/logout', { method: 'POST' });
authToken = null;
localStorage.removeItem('authToken');
},
async getProfile() {
return await apiCall('/auth/profile');
}
};
// Mood tracking API
export const moodAPI = {
async trackMood(moodType, intensity, notes) {
return await apiCall('/mood/track', {
method: 'POST',
body: JSON.stringify({ moodType, intensity, notes })
});
},
async getMoodHistory() {
return await apiCall('/mood/history');
}
};
// Thought record API
export const thoughtAPI = {
async saveThoughtRecord(thoughtData) {
return await apiCall('/thoughts', {
method: 'POST',
body: JSON.stringify(thoughtData)
});
},
async getThoughtRecords() {
return await apiCall('/thoughts');
},
async updateThoughtRecord(id, thoughtData) {
return await apiCall(`/thoughts/${id}`, {
method: 'PUT',
body: JSON.stringify(thoughtData)
});
},
async deleteThoughtRecord(id) {
return await apiCall(`/thoughts/${id}`, {
method: 'DELETE'
});
}
};
// Gratitude API
export const gratitudeAPI = {
async saveGratitudeEntry(entry) {
return await apiCall('/gratitude', {
method: 'POST',
body: JSON.stringify(entry)
});
},
async getGratitudeEntries() {
return await apiCall('/gratitude');
},
async updateGratitudeEntry(id, entry) {
return await apiCall(`/gratitude/${id}`, {
method: 'PUT',
body: JSON.stringify(entry)
});
},
async deleteGratitudeEntry(id) {
return await apiCall(`/gratitude/${id}`, {
method: 'DELETE'
});
}
};
// Progress API
export const progressAPI = {
async getProgressStats() {
return await apiCall('/progress/stats');
},
async getProgressHistory() {
return await apiCall('/progress/history');
}
};
// Notification API
export const notificationAPI = {
async getNotifications() {
return await apiCall('/notifications');
},
async markAsRead(notificationId) {
return await apiCall(`/notifications/${notificationId}/read`, {
method: 'PUT'
});
},
async deleteNotification(notificationId) {
return await apiCall(`/notifications/${notificationId}`, {
method: 'DELETE'
});
},
async addNotification(notification) {
return await apiCall('/notifications', {
method: 'POST',
body: JSON.stringify(notification)
});
}
};
// Check if user is authenticated
export function isAuthenticated() {
return !!authToken;
}
// Initialize API with token check
export function initializeAPI() {
if (!authToken) {
// Redirect to login or show login modal
console.log('User not authenticated');
return false;
}
return true;
}

1106
MindShift-Windows/src/app.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,111 @@
<!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">
<!-- Styles -->
<link rel="stylesheet" href="styles.css">
</head>
<body>
<!-- Animated Background Orbs -->
<div class="background-orbs">
<div class="orb orb-1"></div>
<div class="orb orb-2"></div>
<div class="orb orb-3"></div>
</div>
<!-- Success Ping Container -->
<div id="success-ping" class="success-ping"></div>
<!-- Proactive Badge -->
<div id="proactive-badge" class="proactive-badge">✨ Time for a vibe check?</div>
<!-- Welcome Overlay -->
<div id="welcome-overlay" class="welcome-overlay">
<div class="welcome-emoji">🧠</div>
<h1 class="welcome-title">MindShift</h1>
<p class="welcome-subtitle">Your personal CBT companion</p>
</div>
<!-- Header -->
<header class="app-header">
<div class="header-content">
<div class="app-title">
<span class="material-icons">self_improvement</span>
MindShift
</div>
<div class="header-actions">
<div style="position: relative;">
<button class="nav-item" onclick="toggleNotifications()" style="color: white;">
<span class="material-icons">notifications</span>
<span id="notification-badge" class="notification-badge" style="display: none;">0</span>
</button>
<div id="notification-list" class="notification-dropdown">
<!-- Notifications will be loaded here -->
</div>
</div>
<button class="nav-item" onclick="logout()" style="color: white;">
<span class="material-icons">logout</span>
</button>
</div>
</div>
</header>
<!-- Main Content -->
<main id="main-content" class="main-content">
<!-- Content will be dynamically loaded here -->
</main>
<!-- Bottom Navigation -->
<nav class="bottom-nav">
<button class="nav-item active" onclick="navigateTo('home')">
<span class="material-icons">home</span>
<span class="nav-label">Home</span>
</button>
<button class="nav-item" onclick="navigateTo('mood')">
<span class="material-icons">mood</span>
<span class="nav-label">Mood</span>
</button>
<button class="nav-item" onclick="navigateTo('thoughts')">
<span class="material-icons">psychology</span>
<span class="nav-label">Thoughts</span>
</button>
<button class="nav-item" onclick="navigateTo('gratitude')">
<span class="material-icons">favorite</span>
<span class="nav-label">Gratitude</span>
</button>
<button class="nav-item" onclick="navigateTo('progress')">
<span class="material-icons">insights</span>
<span class="nav-label">Progress</span>
</button>
</nav>
<!-- Floating Action Button -->
<button class="fab" onclick="showQuickActionMenu()">
<span class="material-icons">add</span>
</button>
<!-- Scripts -->
<script type="module" src="api.js"></script>
<script type="module" src="app.js"></script>
</body>
</html>

View File

@@ -0,0 +1,348 @@
const { app, BrowserWindow, Menu, ipcMain, shell, dialog, nativeImage } = require('electron');
const path = require('path');
const fs = require('fs');
// Keep a global reference of the window object
let mainWindow;
let isDev = process.argv.includes('--dev');
// Windows 11 specific configurations
const windowsConfig = {
icon: path.join(__dirname, '../assets/icon.ico'),
show: false,
autoHideMenuBar: true,
frame: true,
titleBarStyle: 'default',
minWidth: 800,
minHeight: 600,
// DPI and scaling optimizations
backgroundColor: '#667eea',
hasShadow: true,
thickFrame: true,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
enableRemoteModule: false,
preload: path.join(__dirname, 'preload.js'),
// Enable zoom and scaling
zoomFactor: 1.0,
// Better rendering
experimentalFeatures: true,
// Enable smooth scrolling
smoothScroll: true
}
};
function createWindow() {
// Get primary display for DPI scaling
const { screen } = require('electron');
const primaryDisplay = screen.getPrimaryDisplay();
const scaleFactor = primaryDisplay.scaleFactor;
// Calculate window size based on DPI
const baseWidth = 1200;
const baseHeight = 800;
const scaledWidth = Math.floor(baseWidth * scaleFactor);
const scaledHeight = Math.floor(baseHeight * scaleFactor);
// Create the browser window with Windows 11 styling
mainWindow = new BrowserWindow({
...windowsConfig,
width: scaledWidth,
height: scaledHeight,
// DPI awareness
deviceScaleFactor: scaleFactor,
webPreferences: {
...windowsConfig.webPreferences,
// Enable features for Windows 11
experimentalFeatures: true,
spellcheck: true,
// DPI scaling
zoomFactor: 1.0 / scaleFactor
}
});
// Load the app
if (isDev) {
// Load from dev server on port 12005 in development
mainWindow.loadURL('http://localhost:12005');
} else {
// Load from local file in production
mainWindow.loadFile('src/index.html');
}
// Show window when ready to prevent visual flash
mainWindow.once('ready-to-show', () => {
mainWindow.show();
// Focus the window on show
if (isDev) {
mainWindow.webContents.openDevTools();
}
});
// Handle window closed
mainWindow.on('closed', () => {
mainWindow = null;
});
// Set Windows 11 specific handlers
setupWindowsHandlers();
}
function setupWindowsHandlers() {
// Handle Windows notifications
mainWindow.webContents.on('did-finish-load', () => {
// Inject Windows 11 specific styles
mainWindow.webContents.insertCSS(`
@media (prefers-color-scheme: dark) {
:root {
--surface: #1e1e1e;
--surface-variant: #2d2d2d;
--on-surface: #ffffff;
--on-surface-variant: #cccccc;
}
}
/* Windows 11 specific styling */
.titlebar {
-webkit-app-region: drag;
height: 32px;
background: transparent;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 10000;
}
.titlebar button {
-webkit-app-region: no-drag;
}
/* Windows 11 Mica effect (if supported) */
@supports (backdrop-filter: blur(20px)) {
.app-header {
backdrop-filter: blur(20px) saturate(180%);
background: rgba(255, 255, 255, 0.7);
}
@media (prefers-color-scheme: dark) {
.app-header {
background: rgba(30, 30, 30, 0.7);
}
}
}
`);
});
// Handle external links
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
shell.openExternal(url);
return { action: 'deny' };
});
}
// Create application menu for Windows
function createMenu() {
const template = [
{
label: 'File',
submenu: [
{
label: 'Export Data',
accelerator: 'CmdOrCtrl+E',
click: () => {
mainWindow.webContents.send('export-data');
}
},
{
label: 'Import Data',
accelerator: 'CmdOrCtrl+I',
click: () => {
mainWindow.webContents.send('import-data');
}
},
{ type: 'separator' },
{
label: 'Exit',
accelerator: process.platform === 'win32' ? 'Alt+F4' : 'CmdOrCtrl+Q',
click: () => {
app.quit();
}
}
]
},
{
label: 'Edit',
submenu: [
{ role: 'undo', label: 'Undo' },
{ role: 'redo', label: 'Redo' },
{ type: 'separator' },
{ role: 'cut', label: 'Cut' },
{ role: 'copy', label: 'Copy' },
{ role: 'paste', label: 'Paste' }
]
},
{
label: 'View',
submenu: [
{ role: 'reload', label: 'Reload' },
{ role: 'forceReload', label: 'Force Reload' },
{ role: 'toggleDevTools', label: 'Toggle Developer Tools' },
{ type: 'separator' },
{ role: 'resetZoom', label: 'Actual Size' },
{ role: 'zoomIn', label: 'Zoom In' },
{ role: 'zoomOut', label: 'Zoom Out' },
{ type: 'separator' },
{ role: 'togglefullscreen', label: 'Toggle Fullscreen' }
]
},
{
label: 'Window',
submenu: [
{ role: 'minimize', label: 'Minimize' },
{ role: 'close', label: 'Close' }
]
},
{
label: 'Help',
submenu: [
{
label: 'About MindShift',
click: () => {
dialog.showMessageBox(mainWindow, {
type: 'info',
title: 'About MindShift',
message: 'MindShift CBT Therapy',
detail: 'Version 1.0.0\n\nYour personal CBT therapy companion for mood management and mental wellness.\n\n© 2024 MindShift Team',
buttons: ['OK']
});
}
}
]
}
];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
}
// App event handlers
app.whenReady().then(() => {
createWindow();
createMenu();
// Handle Windows 11 taskbar integration
if (process.platform === 'win32') {
app.setUserTasks([
{
program: process.execPath,
arguments: '--quick-relax',
title: 'Quick Relax',
description: 'Start a quick relaxation exercise',
iconPath: process.execPath,
iconIndex: 0
},
{
program: process.execPath,
arguments: '--mood-check',
title: 'Mood Check',
description: 'Check your current mood',
iconPath: process.execPath,
iconIndex: 0
}
]);
}
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
// Handle Windows 11 notifications
ipcMain.handle('show-notification', (event, title, body) => {
const { Notification } = require('electron');
if (Notification.isSupported()) {
const notification = new Notification({
title: title,
body: body,
icon: path.join(__dirname, '../assets/icon.ico'),
silent: false
});
notification.show();
return true;
}
return false;
});
// Handle file operations
ipcMain.handle('save-file', async (event, data, filename) => {
try {
const result = await dialog.showSaveDialog(mainWindow, {
defaultPath: filename,
filters: [
{ name: 'JSON Files', extensions: ['json'] },
{ name: 'CSV Files', extensions: ['csv'] },
{ name: 'All Files', extensions: ['*'] }
]
});
if (!result.canceled) {
fs.writeFileSync(result.filePath, data);
return { success: true, path: result.filePath };
}
return { success: false, cancelled: true };
} catch (error) {
return { success: false, error: error.message };
}
});
ipcMain.handle('open-file', async (event) => {
try {
const result = await dialog.showOpenDialog(mainWindow, {
properties: ['openFile'],
filters: [
{ name: 'JSON Files', extensions: ['json'] },
{ name: 'CSV Files', extensions: ['csv'] },
{ name: 'All Files', extensions: ['*'] }
]
});
if (!result.canceled && result.filePaths.length > 0) {
const data = fs.readFileSync(result.filePaths[0], 'utf8');
return { success: true, data: data, path: result.filePaths[0] };
}
return { success: false, cancelled: true };
} catch (error) {
return { success: false, error: error.message };
}
});
// Handle protocol for deep linking
app.setAsDefaultProtocolClient('mindshift');
// Handle Windows 11 system theme changes
if (process.platform === 'win32') {
const { systemPreferences } = require('electron');
systemPreferences.on('color-changed', () => {
// Notify renderer about theme change
if (mainWindow) {
mainWindow.webContents.send('theme-changed', systemPreferences.isDarkMode());
}
});
}

View File

@@ -0,0 +1,70 @@
const { contextBridge, ipcRenderer } = require('electron');
// Expose protected methods that allow the renderer process to use
// the ipcRenderer without exposing the entire object
contextBridge.exposeInMainWorld('electronAPI', {
// Notification handling
showNotification: (title, body) => ipcRenderer.invoke('show-notification', title, body),
// File operations
saveFile: (data, filename) => ipcRenderer.invoke('save-file', data, filename),
openFile: () => ipcRenderer.invoke('open-file'),
// Theme handling
onThemeChange: (callback) => ipcRenderer.on('theme-changed', callback),
// Data export/import
exportData: () => ipcRenderer.invoke('export-data'),
importData: () => ipcRenderer.invoke('import-data'),
// App info
getVersion: () => process.env.npm_package_version || '1.0.0',
getPlatform: () => process.platform
});
// Expose localStorage operations for persistence
contextBridge.exposeInMainWorld('storage', {
get: (key) => localStorage.getItem(key),
set: (key, value) => localStorage.setItem(key, value),
remove: (key) => localStorage.removeItem(key),
clear: () => localStorage.clear(),
getAll: () => {
const data = {};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
data[key] = localStorage.getItem(key);
}
return data;
}
});
// Windows 11 specific API
contextBridge.exposeInMainWorld('windowsAPI', {
// Check if running on Windows
isWindows: () => process.platform === 'win32',
// Get Windows version info
getWindowsVersion: () => {
if (process.platform === 'win32') {
return {
platform: 'win32',
arch: process.arch,
version: process.getSystemVersion?.() || 'Unknown'
};
}
return null;
},
// Check for Windows 11 features
supportsMica: () => {
// Check if Windows 11 (build 22000+) for Mica effect
if (process.platform === 'win32') {
const version = process.getSystemVersion?.();
if (version) {
const buildNumber = parseInt(version.split('.')?.[2] || '0');
return buildNumber >= 22000;
}
}
return false;
}
});

View File

@@ -0,0 +1,674 @@
/* Base Styles & Variables */
: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: rgba(255, 255, 255, 0.9);
--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;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Roboto', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #FF6B6B 100%);
background-size: 400% 400%;
animation: gradientBG 15s ease infinite;
color: var(--on-surface);
line-height: 1.5;
min-height: 100vh;
position: relative;
overflow-x: hidden;
}
@keyframes gradientBG {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
/* Animated Background Orbs */
.background-orbs {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 0;
overflow: hidden;
}
.orb {
position: absolute;
border-radius: 50%;
filter: blur(60px);
opacity: 0.6;
animation: orbFloat 20s infinite alternate cubic-bezier(0.45, 0.05, 0.55, 0.95);
}
.orb-1 {
width: 400px;
height: 400px;
background: var(--primary);
top: -100px;
left: -100px;
animation-duration: 25s;
}
.orb-2 {
width: 300px;
height: 300px;
background: var(--secondary);
bottom: -50px;
right: -50px;
animation-duration: 18s;
animation-delay: -5s;
}
.orb-3 {
width: 250px;
height: 250px;
background: var(--tertiary);
top: 40%;
left: 60%;
animation-duration: 22s;
animation-delay: -10s;
}
@keyframes orbFloat {
0% { transform: translate(0, 0) rotate(0deg); }
33% { transform: translate(50px, 100px) rotate(120deg); }
66% { transform: translate(-50px, 50px) rotate(240deg); }
100% { transform: translate(0, 0) rotate(360deg); }
}
/* 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: 2000;
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: rgba(255, 107, 107, 0.95);
color: var(--on-primary);
padding: 16px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
position: sticky;
top: 0;
z-index: 100;
backdrop-filter: blur(10px);
transition: all 0.3s ease;
}
.app-header:hover {
background-color: rgba(255, 107, 107, 1);
box-shadow: 0 6px 24px rgba(0,0,0,0.15);
}
.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 Pulse */
.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;
animation: pulseBadge 2s infinite;
}
@keyframes pulseBadge {
0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(255, 183, 77, 0.7); }
70% { transform: scale(1.1); box-shadow: 0 0 0 6px rgba(255, 183, 77, 0); }
100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(255, 183, 77, 0); }
}
.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;
animation: slideInDown 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes slideInDown {
from { transform: translateY(-10px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
/* Navigation */
.bottom-nav {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
box-shadow: 0 -4px 20px rgba(0,0,0,0.1);
display: flex;
justify-content: space-around;
padding: 12px 0;
z-index: 100;
border-top-left-radius: 20px;
border-top-right-radius: 20px;
}
.nav-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 8px 16px;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
border: none;
background: none;
color: var(--on-surface-variant);
position: relative;
}
.nav-item.active {
color: var(--primary);
transform: translateY(-4px);
}
.nav-item.active::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 4px;
height: 4px;
background: var(--primary);
border-radius: 50%;
box-shadow: 0 0 10px var(--primary);
}
.nav-item:hover {
color: var(--primary);
transform: translateY(-2px);
}
.nav-item .material-icons {
font-size: 24px;
transition: transform 0.3s ease;
}
.nav-item:hover .material-icons {
transform: scale(1.2);
}
/* Main Content & Cards */
.main-content {
max-width: 1200px;
margin: 0 auto;
padding: 16px;
padding-bottom: 100px;
min-height: calc(100vh - 120px);
position: relative;
z-index: 1;
}
.card {
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(12px);
border-radius: 24px;
padding: 24px;
margin-bottom: 24px;
box-shadow: 0 8px 32px rgba(0,0,0,0.1);
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.5);
}
.card:hover {
transform: translateY(-8px) scale(1.01);
box-shadow: 0 12px 40px rgba(0,0,0,0.2);
background: rgba(255, 255, 255, 0.95);
}
.card-title {
font-size: 22px;
font-weight: 600;
margin-bottom: 16px;
color: var(--on-surface);
position: relative;
display: inline-block;
}
.card-title::after {
content: '';
position: absolute;
bottom: -4px;
left: 0;
width: 100%;
height: 2px;
background: linear-gradient(90deg, var(--primary), transparent);
transform: scaleX(0);
transform-origin: left;
transition: transform 0.4s ease;
}
.card:hover .card-title::after {
transform: scaleX(1);
}
/* Mood Selector - Enhanced */
.mood-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
gap: 16px;
margin: 20px 0;
}
.mood-card {
background: rgba(255, 255, 255, 0.8);
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: 2px solid transparent;
position: relative;
overflow: hidden;
}
.mood-card:hover {
transform: translateY(-8px) scale(1.05) rotate(2deg);
box-shadow: 0 15px 35px rgba(0,0,0,0.2);
background: rgba(255, 255, 255, 0.95);
}
.mood-card.selected {
border-color: var(--primary);
background: linear-gradient(135deg, var(--primary-container), white);
transform: scale(1.05);
box-shadow: 0 0 0 4px rgba(255, 107, 107, 0.2);
}
.mood-emoji {
font-size: 48px;
margin-bottom: 12px;
display: block;
transition: transform 0.3s ease;
}
.mood-card:hover .mood-emoji {
animation: moodBounce 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
@keyframes moodBounce {
0% { transform: scale(1); }
50% { transform: scale(1.4); }
100% { transform: scale(1.2); }
}
/* Buttons - Alive */
.btn {
padding: 16px 32px;
border: none;
border-radius: 50px;
font-size: 18px;
font-weight: 700;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 12px;
position: relative;
overflow: hidden;
text-transform: uppercase;
letter-spacing: 1px;
}
/* Shimmer Effect */
.btn::after {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
transition: 0.5s;
}
.btn:hover::after {
left: 100%;
transition: 0.7s ease-in-out;
}
.btn-primary {
background: linear-gradient(45deg, var(--primary), var(--secondary));
color: white;
box-shadow: 0 8px 20px rgba(255, 107, 107, 0.4);
animation: breathBtn 3s infinite ease-in-out;
}
@keyframes breathBtn {
0% { transform: scale(1); box-shadow: 0 8px 20px rgba(255, 107, 107, 0.4); }
50% { transform: scale(1.02); box-shadow: 0 12px 24px rgba(255, 107, 107, 0.6); }
100% { transform: scale(1); box-shadow: 0 8px 20px rgba(255, 107, 107, 0.4); }
}
.btn-primary:hover {
transform: translateY(-4px) scale(1.05);
box-shadow: 0 15px 30px rgba(255, 107, 107, 0.5);
animation: none; /* Stop breathing on hover to focus */
}
.btn-secondary {
background: linear-gradient(45deg, var(--tertiary), var(--peace));
color: white;
box-shadow: 0 8px 20px rgba(79, 195, 247, 0.4);
}
.btn-secondary:hover {
transform: translateY(-4px) scale(1.05);
box-shadow: 0 15px 30px rgba(79, 195, 247, 0.5);
}
/* Inputs & Sliders */
.slider {
width: 100%;
height: 8px;
border-radius: 4px;
background: rgba(0,0,0,0.1);
outline: none;
-webkit-appearance: none;
transition: all 0.3s ease;
}
.slider:hover {
height: 10px;
background: rgba(0,0,0,0.15);
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 24px;
height: 24px;
border-radius: 50%;
background: var(--primary);
cursor: pointer;
box-shadow: 0 4px 10px rgba(255, 107, 107, 0.4);
transition: all 0.3s ease;
}
.slider:hover::-webkit-slider-thumb {
transform: scale(1.2);
box-shadow: 0 6px 15px rgba(255, 107, 107, 0.6);
}
textarea, input[type="text"] {
width: 100%;
padding: 16px;
border-radius: 16px;
border: 2px solid rgba(0,0,0,0.1);
background: rgba(255, 255, 255, 0.9);
font-size: 16px;
font-family: 'Roboto', sans-serif;
transition: all 0.3s ease;
}
textarea:focus, input[type="text"]:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 4px rgba(255, 107, 107, 0.1);
transform: scale(1.01);
}
/* Proactive Badges */
.proactive-badge {
position: absolute;
background: var(--joy);
color: white;
padding: 8px 16px;
border-radius: 20px;
font-size: 14px;
font-weight: bold;
box-shadow: 0 4px 15px rgba(171, 71, 188, 0.4);
animation: floatBadge 3s ease-in-out infinite;
z-index: 10;
pointer-events: none;
opacity: 0;
transform: translateY(20px);
transition: opacity 0.5s, transform 0.5s;
}
.proactive-badge.visible {
opacity: 1;
transform: translateY(0);
}
@keyframes floatBadge {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
/* Breathing Exercise - Super Alive */
.breathing-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 400px;
}
.breathing-circle {
width: 250px;
height: 250px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, var(--tertiary), var(--primary));
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 60px rgba(79, 195, 247, 0.4);
position: relative;
transition: all 4s cubic-bezier(0.45, 0.05, 0.55, 0.95);
}
.breathing-circle::before {
content: '';
position: absolute;
top: -20px;
left: -20px;
right: -20px;
bottom: -20px;
border-radius: 50%;
border: 2px solid rgba(255, 255, 255, 0.3);
animation: ripple 4s infinite;
}
.breathing-circle.inhale {
transform: scale(1.5);
box-shadow: 0 0 100px rgba(79, 195, 247, 0.6);
filter: hue-rotate(30deg);
}
.breathing-circle.exhale {
transform: scale(0.8);
box-shadow: 0 0 30px rgba(79, 195, 247, 0.2);
filter: hue-rotate(0deg);
}
.breathing-text {
font-size: 32px;
font-weight: 700;
color: white;
text-shadow: 0 2px 10px rgba(0,0,0,0.2);
z-index: 2;
}
@keyframes ripple {
0% { transform: scale(0.8); opacity: 1; }
100% { transform: scale(1.5); opacity: 0; }
}
/* FAB - Alive */
.fab {
position: fixed;
bottom: 90px;
right: 24px;
width: 64px;
height: 64px;
border-radius: 50%;
background: linear-gradient(135deg, var(--primary), var(--secondary));
color: white;
border: none;
box-shadow: 0 8px 25px rgba(255, 107, 107, 0.5);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
z-index: 99;
}
.fab:hover {
transform: scale(1.15) rotate(90deg);
box-shadow: 0 12px 35px rgba(255, 107, 107, 0.6);
}
.fab .material-icons {
font-size: 32px;
}
/* Success Ping */
.success-ping {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
z-index: 2000;
}
.ping-circle {
position: absolute;
border-radius: 50%;
background: rgba(102, 187, 106, 0.6);
animation: pingExpand 1s ease-out forwards;
}
@keyframes pingExpand {
0% { width: 0; height: 0; opacity: 1; }
100% { width: 500px; height: 500px; opacity: 0; }
}
/* Mobile Adaptation */
@media (max-width: 480px) {
.welcome-title { font-size: 36px; }
.card { padding: 16px; border-radius: 20px; }
.btn { padding: 14px 24px; width: 100%; }
.mood-emoji { font-size: 40px; }
.breathing-circle { width: 180px; height: 180px; }
.bottom-nav { padding: 8px 0; }
.nav-item { padding: 6px 12px; }
.nav-label { font-size: 10px; }
}
/* Desktop Sidebar */
@media (min-width: 1024px) {
.bottom-nav { display: none; }
/* ... (keep existing desktop styles if needed, but adapt to new look) ... */
}

2899
backend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

24
backend/package.json Normal file
View File

@@ -0,0 +1,24 @@
{
"name": "mindshift-backend",
"version": "1.0.0",
"description": "Backend API for MindShift CBT Therapy App",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
},
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5",
"bcryptjs": "^2.4.3",
"jsonwebtoken": "^9.0.2",
"sqlite3": "^5.1.6",
"uuid": "^9.0.0",
"helmet": "^7.0.0",
"express-rate-limit": "^6.8.1",
"dotenv": "^16.3.1"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}

515
backend/server.js Normal file
View File

@@ -0,0 +1,515 @@
const express = require('express');
const cors = require('cors');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const sqlite3 = require('sqlite3').verbose();
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const path = require('path');
const { v4: uuidv4 } = require('uuid');
const app = express();
const PORT = process.env.PORT || 12004;
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
// Middleware
app.use(helmet());
app.use(cors());
app.use(express.json());
app.use(express.static(path.join(__dirname, '..')));
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
});
app.use('/api/', limiter);
// Initialize SQLite database
const db = new sqlite3.Database('./mindshift.db');
// Create tables
db.serialize(() => {
// Users table
db.run(`CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
name TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
last_login DATETIME
)`);
// Mood tracking table
db.run(`CREATE TABLE IF NOT EXISTS mood_entries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
mood_type TEXT NOT NULL,
intensity INTEGER NOT NULL,
notes TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id)
)`);
// Thoughts table
db.run(`CREATE TABLE IF NOT EXISTS thoughts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
situation TEXT NOT NULL,
automatic_thought TEXT NOT NULL,
emotion TEXT NOT NULL,
emotion_intensity INTEGER NOT NULL,
cognitive_distortion TEXT,
alternative_thought TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id)
)`);
// Gratitude entries table
db.run(`CREATE TABLE IF NOT EXISTS gratitude_entries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
entry TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id)
)`);
// Progress tracking table
db.run(`CREATE TABLE IF NOT EXISTS progress (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
date DATE NOT NULL,
mood_score INTEGER,
thoughts_count INTEGER DEFAULT 0,
gratitude_count INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE(user_id, date),
FOREIGN KEY (user_id) REFERENCES users (id)
)`);
// Notifications table
db.run(`CREATE TABLE IF NOT EXISTS notifications (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
message TEXT NOT NULL,
type TEXT DEFAULT 'info',
read BOOLEAN DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id)
)`);
});
// JWT authentication middleware
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Access token required' });
}
jwt.verify(token, JWT_SECRET, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Invalid token' });
}
req.user = user;
next();
});
}
// Auth routes
app.post('/api/auth/register', async (req, res) => {
try {
const { email, password, name } = req.body;
if (!email || !password || !name) {
return res.status(400).json({ error: 'Email, password, and name are required' });
}
// Check if user already exists
db.get('SELECT id FROM users WHERE email = ?', [email], async (err, user) => {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
if (user) {
return res.status(400).json({ error: 'User already exists' });
}
// Hash password
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(password, saltRounds);
// Insert new user
db.run('INSERT INTO users (email, password, name) VALUES (?, ?, ?)',
[email, hashedPassword, name], function(err) {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
// Generate JWT
const token = jwt.sign(
{ id: this.lastID, email, name },
JWT_SECRET,
{ expiresIn: '24h' }
);
res.status(201).json({
message: 'User created successfully',
token,
user: {
id: this.lastID,
email,
name
}
});
});
});
} catch (error) {
res.status(500).json({ error: 'Server error' });
}
});
app.post('/api/auth/login', (req, res) => {
try {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({ error: 'Email and password are required' });
}
// Find user
db.get('SELECT * FROM users WHERE email = ?', [email], async (err, user) => {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Check password
const validPassword = await bcrypt.compare(password, user.password);
if (!validPassword) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Update last login
db.run('UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = ?', [user.id]);
// Generate JWT
const token = jwt.sign(
{ id: user.id, email: user.email, name: user.name },
JWT_SECRET,
{ expiresIn: '24h' }
);
res.json({
message: 'Login successful',
token,
user: {
id: user.id,
email: user.email,
name: user.name
}
});
});
} catch (error) {
res.status(500).json({ error: 'Server error' });
}
});
// Get user profile
app.get('/api/user/profile', authenticateToken, (req, res) => {
db.get('SELECT id, email, name, created_at, last_login FROM users WHERE id = ?', [req.user.id], (err, user) => {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json(user);
});
});
// Mood tracking
app.post('/api/mood/track', authenticateToken, (req, res) => {
try {
const { moodType, intensity, notes } = req.body;
if (!moodType || !intensity || intensity < 1 || intensity > 10) {
return res.status(400).json({ error: 'Invalid mood data' });
}
// Insert mood entry
db.run('INSERT INTO mood_entries (user_id, mood_type, intensity, notes) VALUES (?, ?, ?, ?)',
[req.user.id, moodType, intensity, notes || null], function(err) {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
// Update or create progress entry for today
const today = new Date().toISOString().split('T')[0];
db.run(`INSERT OR REPLACE INTO progress (user_id, date, mood_score, thoughts_count, gratitude_count)
VALUES (?, ?, COALESCE((SELECT mood_score FROM progress WHERE user_id = ? AND date = ?), ?),
COALESCE((SELECT thoughts_count FROM progress WHERE user_id = ? AND date = ?), 0),
COALESCE((SELECT gratitude_count FROM progress WHERE user_id = ? AND date = ?), 0))`,
[req.user.id, today, req.user.id, today, intensity, req.user.id, today, req.user.id, today],
function(err) {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
res.status(201).json({
message: 'Mood tracked successfully',
entryId: this.lastID
});
});
});
} catch (error) {
res.status(500).json({ error: 'Server error' });
}
});
// Get mood history
app.get('/api/mood/history', authenticateToken, (req, res) => {
const limit = parseInt(req.query.limit) || 30;
db.all('SELECT * FROM mood_entries WHERE user_id = ? ORDER BY created_at DESC LIMIT ?',
[req.user.id, limit], (err, entries) => {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
res.json(entries);
});
});
// Thought recording
app.post('/api/thoughts', authenticateToken, (req, res) => {
try {
const { situation, automaticThought, emotion, emotionIntensity, cognitiveDistortion, alternativeThought } = req.body;
if (!situation || !automaticThought || !emotion || !emotionIntensity || emotionIntensity < 1 || emotionIntensity > 10) {
return res.status(400).json({ error: 'Invalid thought data' });
}
// Insert thought entry
db.run('INSERT INTO thoughts (user_id, situation, automatic_thought, emotion, emotion_intensity, cognitive_distortion, alternative_thought) VALUES (?, ?, ?, ?, ?, ?, ?)',
[req.user.id, situation, automaticThought, emotion, emotionIntensity, cognitiveDistortion || null, alternativeThought || null], function(err) {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
// Update progress entry for today
const today = new Date().toISOString().split('T')[0];
db.run(`INSERT OR REPLACE INTO progress (user_id, date, mood_score, thoughts_count, gratitude_count)
VALUES (?, ?, COALESCE((SELECT mood_score FROM progress WHERE user_id = ? AND date = ?), 0),
COALESCE((SELECT thoughts_count FROM progress WHERE user_id = ? AND date = ?), 0) + 1,
COALESCE((SELECT gratitude_count FROM progress WHERE user_id = ? AND date = ?), 0))`,
[req.user.id, today, req.user.id, today, req.user.id, today, req.user.id, today],
function(err) {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
res.status(201).json({
message: 'Thought recorded successfully',
entryId: this.lastID
});
});
});
} catch (error) {
res.status(500).json({ error: 'Server error' });
}
});
// Get thoughts history
app.get('/api/thoughts', authenticateToken, (req, res) => {
const limit = parseInt(req.query.limit) || 30;
db.all('SELECT * FROM thoughts WHERE user_id = ? ORDER BY created_at DESC LIMIT ?',
[req.user.id, limit], (err, entries) => {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
res.json(entries);
});
});
// Gratitude entries
app.post('/api/gratitude', authenticateToken, (req, res) => {
try {
const { entry } = req.body;
if (!entry || entry.trim() === '') {
return res.status(400).json({ error: 'Gratitude entry is required' });
}
// Insert gratitude entry
db.run('INSERT INTO gratitude_entries (user_id, entry) VALUES (?, ?)',
[req.user.id, entry], function(err) {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
// Update progress entry for today
const today = new Date().toISOString().split('T')[0];
db.run(`INSERT OR REPLACE INTO progress (user_id, date, mood_score, thoughts_count, gratitude_count)
VALUES (?, ?, COALESCE((SELECT mood_score FROM progress WHERE user_id = ? AND date = ?), 0),
COALESCE((SELECT thoughts_count FROM progress WHERE user_id = ? AND date = ?), 0),
COALESCE((SELECT gratitude_count FROM progress WHERE user_id = ? AND date = ?), 0) + 1)`,
[req.user.id, today, req.user.id, today, req.user.id, today, req.user.id, today],
function(err) {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
res.status(201).json({
message: 'Gratitude entry added successfully',
entryId: this.lastID
});
});
});
} catch (error) {
res.status(500).json({ error: 'Server error' });
}
});
// Get gratitude entries
app.get('/api/gratitude', authenticateToken, (req, res) => {
const limit = parseInt(req.query.limit) || 30;
db.all('SELECT * FROM gratitude_entries WHERE user_id = ? ORDER BY created_at DESC LIMIT ?',
[req.user.id, limit], (err, entries) => {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
res.json(entries);
});
});
// Get progress data
app.get('/api/progress', authenticateToken, (req, res) => {
const days = parseInt(req.query.days) || 30;
db.all('SELECT * FROM progress WHERE user_id = ? ORDER BY date DESC LIMIT ?',
[req.user.id, days], (err, entries) => {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
res.json(entries);
});
});
// Get dashboard stats
app.get('/api/dashboard/stats', authenticateToken, (req, res) => {
const today = new Date().toISOString().split('T')[0];
// Get today's progress
db.get('SELECT * FROM progress WHERE user_id = ? AND date = ?', [req.user.id, today], (err, todayProgress) => {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
// Get this week's mood entries
const weekAgo = new Date();
weekAgo.setDate(weekAgo.getDate() - 7);
const weekAgoStr = weekAgo.toISOString().split('T')[0];
db.all('SELECT AVG(intensity) as avgMood, COUNT(*) as count FROM mood_entries WHERE user_id = ? AND created_at >= ?',
[req.user.id, weekAgoStr], (err, weekMood) => {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
// Get total counts
db.get(`SELECT
(SELECT COUNT(*) FROM mood_entries WHERE user_id = ?) as totalMoods,
(SELECT COUNT(*) FROM thoughts WHERE user_id = ?) as totalThoughts,
(SELECT COUNT(*) FROM gratitude_entries WHERE user_id = ?) as totalGratitude`,
[req.user.id, req.user.id, req.user.id], (err, totals) => {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
res.json({
today: todayProgress || { mood_score: 0, thoughts_count: 0, gratitude_count: 0 },
week: {
avgMood: weekMood[0].avgMood || 0,
moodCount: weekMood[0].count || 0
},
totals: totals
});
});
});
});
});
// Notifications
app.get('/api/notifications', authenticateToken, (req, res) => {
db.all('SELECT * FROM notifications WHERE user_id = ? ORDER BY created_at DESC', [req.user.id], (err, notifications) => {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
res.json(notifications);
});
});
app.post('/api/notifications', authenticateToken, (req, res) => {
const { title, message, type } = req.body;
if (!title || !message) {
return res.status(400).json({ error: 'Title and message are required' });
}
db.run('INSERT INTO notifications (user_id, title, message, type) VALUES (?, ?, ?, ?)',
[req.user.id, title, message, type || 'info'], function(err) {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
// Return the created notification
db.get('SELECT * FROM notifications WHERE id = ?', [this.lastID], (err, notification) => {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
res.status(201).json(notification);
});
});
});
app.put('/api/notifications/:id/read', authenticateToken, (req, res) => {
const notificationId = req.params.id;
db.run('UPDATE notifications SET read = 1 WHERE id = ? AND user_id = ?',
[notificationId, req.user.id], function(err) {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
if (this.changes === 0) {
return res.status(404).json({ error: 'Notification not found' });
}
res.json({ success: true });
});
});
app.delete('/api/notifications/:id', authenticateToken, (req, res) => {
const notificationId = req.params.id;
db.run('DELETE FROM notifications WHERE id = ? AND user_id = ?',
[notificationId, req.user.id], function(err) {
if (err) {
return res.status(500).json({ error: 'Database error' });
}
if (this.changes === 0) {
return res.status(404).json({ error: 'Notification not found' });
}
res.json({ success: true });
});
});
// Start server
app.listen(PORT, () => {
console.log(`MindShift server running on port ${PORT}`);
});

11
browserconfig.xml Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square70x70logo src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNzAiIGhlaWdodD0iNzAiIHZpZXdCb3g9IjAgMCA3MCA3MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjcwIiBoZWlnaHQ9IjcwIiByeD0iMTAiIGZpbGw9InVybCgjZ3JhZGllbnQwX2xpbmVhcl8zXzMpIi8+CjxwYXRoIGQ9Ik0zNSAyMUMzMi45MjkgMjEgMzEuNSAyMi45MjkgMzEuNSAyNUMzMS41IDI3LjA3MSAzMi45MjkgMjkgMzUgMjlDMzcuMDcxIDI5IDM4LjUgMjcuMDcxIDM4LjUgMjVDMzguNSAyMi45MjkgMzcuMDcxIDIxIDM1IDIxWiIgZmlsbD0id2hpdGUiLz4KPHA+PC9wPgo8cGF0aCBkPSJNMzUgMzVDMzMuMDQzIDM1IDMxLjUgMzYuNTQzIDMxLjUgMzguNUMzMS41IDQwLjQ1NyAzMy4wNDMgNDIgMzUgNDJDMzYuOTU3IDQyIDM4LjUgNDAuNDU3IDM4LjUgMzguNUMzOC41IDM2LjU0MyAzNi45NTcgMzUgMzUgMzVaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJncmFkaWVudDBfbGluZWFyXzNfMyIgeDE9IjAiIHkxPSIwIiB4Mj0iNzAiIHkyPSI3MCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjRkY2QjZCIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI0ZGQjc0RCIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPg=="/>
<square150x150logo src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTUwIiBoZWlnaHQ9IjE1MCIgdmlld0JveD0iMCAwIDE1MCAxNTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNTAiIGhlaWdodD0iMTUwIiByeD0iMjAiIGZpbGw9InVybCgjZ3JhZGllbnQwX2xpbmVhcl80XzQpIi8+CjxwYXRoIGQ9Ik03NSA0NUM3MC41MTc5IDQ1IDY3IDQ4LjUxNzkgNjcgNTNDNjcgNTcuNDgyMSA3MC41MTc5IDYxIDc1IDYxQzc5LjQ4MjEgNjEgODMgNTcuNDgyMSA4MyA1M0M4MyA0OC41MTc5IDc5LjQ4MjEgNDUgNzUgNDVaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNjAgNzVDNjAuMTM2NyA3MC4yMzgzIDYyLjIzODIgNjUuNDc2NyA2NS4yMzIzIDYxLjgzMzNDNjguMjI2NCA1OC4xOSA3Mi4yMjMyIDU1Ljg5MjggNzYuNzYwNiA1NS44OTI4QzgxLjI5OCA1NS44OTI4IDg1LjI5NDYgNTguMTkgODguMjg4NyA2MS44MzMzQzkxLjI4MjggNjUuNDc2NyA5My4zNjMzIDcwLjIzODMgOTMuMzI1IDc1QzkzLjI4NjcgNzkuNzYxNyA5MS4yODI4IDg0LjUyMzMgODguMjg4NyA4OC4xNjY3Qzg1LjI5NDYgOTEuODEgODEuMjk4IDk0LjEwNzIgNzYuNzYwNiA5NC4xMDcyQzcyLjIyMzIgOTQuMTA3MiA2OC4yMjY0IDkxLjgxIDY1LjIzMjMgODguMTY2N0M2Mi4yMzgyIDg0LjUyMzMgNjAuMTM2NyA3OS43NjE3IDYwIDc1WiIgZmlsbD0id2hpdGUiLz4KPHA+PC9wPgo8cGF0aCBkPSJNNzUgMTA1QzY2LjcxNTcgMTA1IDYwIDk4LjI4NDMgNjAgOTBDNjAgODEuNzE1NyA2Ni43MTU3IDc1IDc1IDc1QzgzLjI4NDMgNzUgOTAgODEuNzE1NyA5MCA5MEM5MCA5OC4yODQzIDgzLjI4NDMgMTA1IDc1IDEwNVoiIGZpbGw9IndoaXRlIi8+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9ImdyYWRpZW50MF9saW5lYXJfNF80IiB4MT0iMCIgeTE9IjAiIHgyPSIxNTAiIHkyPSIxNTAiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0ZGNkI2QiIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRkI3NEQiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4="/>
<square310x310logo src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzEwIiBoZWlnaHQ9IjMxMCIgdmlld0JveD0iMCAwIDMxMCAzMTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIzMTAiIGhlaWdodD0iMzEwIiByeD0iNDAiIGZpbGw9InVybCgjZ3JhZGllbnQwX2xpbmVhcl81XzUpIi8+CjxwYXRoIGQ9Ik0xNTUgOTNDMTQ2LjE5MyA5MyAxMzkgMTAwLjE5MyAxMzkgMTA5QzEzOSAxMTcuODA3IDE0Ni4xOTMgMTI1IDE1NSAxMjVDMTYzLjgwNyAxMjUgMTcxIDExNy44MDcgMTcxIDEwOUMxNzEgMTAwLjE5MyAxNjMuODA3IDkzIDE1NSA5M1oiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xMjQgMTU1QzEyNC4yNzMgMTQ1LjQ3NyAxMjguNDc2IDEzNS45NTMgMTM0LjQ2NSAxMjguNjY3QzE0MC40NTMgMTIxLjM4IDE0OC40NjQgMTE2Ljc4NiAxNTcuNTIxIDExNi43ODZDMTY2LjU3OCAxMTYuNzg2IDE3NC41ODkgMTIxLjM4IDE4MC41NzggMTI4LjY2N0MxODYuNTY2IDEzNS45NTMgMTkwLjcyNyAxNDUuNDc3IDE5MC42NSAxNTVDMTkwLjU3MyAxNjQuNTIzIDE4Ni41NjYgMTc0LjA0NyAxODAuNTc4IDE4MS4zMzNDMTc0LjU4OSAxODguNjIgMTY2LjU3OCAxOTMuMjE0IDE1Ny41MjEgMTkzLjIxNEMxNDguNDY0IDE5My4yMTQgMTQwLjQ1MyAxODguNjIgMTM0LjQ2NSAxODEuMzMzQzEyOC40NzYgMTc0LjA0NyAxMjQuMjczIDE2NC41MjMgMTI0IDE1NVoiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik0xNTUgMjEwQzEzMy40MzEgMjEwIDExNiAxOTIuNTY5IDExNiAxNzFDMTE2IDE0OS40MzEgMTMzLjQzMSAxMzIgMTU1IDEzMkMxNzYuNTY5IDEzMiAxOTQgMTQ5LjQzMSAxOTQgMTcxQzE5NCAxOTIuNTY5IDE3Ni41NjkgMjEwIDE1NSAyMTBaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJncmFkaWVudDBfbGluZWFyXzVfNSIgeDE9IjAiIHkxPSIwIiB4Mj0iMzEwIiB5Mj0iMzEwIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiNGRjZCNkIiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjRkZCNzREIi8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+"/>
<TileColor>#FF6B6B</TileColor>
</tile>
</msapplication>
</browserconfig>

2651
cbt-therapy-app-mockup.html Normal file

File diff suppressed because it is too large Load Diff

27
manifest.json Normal file
View File

@@ -0,0 +1,27 @@
{
"name": "MindShift - CBT Therapy App",
"short_name": "MindShift",
"description": "Your personal CBT therapy companion for mood management and mental wellness",
"start_url": "/",
"display": "standalone",
"background_color": "#FFB74D",
"theme_color": "#FF6B6B",
"orientation": "portrait-primary",
"icons": [
{
"src": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkyIiBoZWlnaHQ9IjE5MiIgdmlld0JveD0iMCAwIDE5MiAxOTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxOTIiIGhlaWdodD0iMTkyIiByeD0iMjQiIGZpbGw9InVybCgjZ3JhZGllbnQwX2xpbmVhcl8xXzEpIi8+CjxwYXRoIGQ9Ik05NiA0OEM5MC40NzcyIDQ4IDg2IDUyLjQ3NzIgODYgNjhDODYgNzMuNTIyOCA5MC40NzcyIDc4IDk2IDc4QzEwMS41MjIgNzggMTA2IDczLjUyMjggMTA2IDY4QzEwNiA2Mi40NzcyIDEwMS41MjIgNDggOTYgNDhaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNzYgOTZDNzYuMDc0NSA5MC4zMTc3IDc4LjMxNzcgODQuNjQ1MiA4Mi4xODU0IDgwLjE4NTRDODYuMDUzMSA3NS43MjU2IDkxLjI1OTEgNzIuNjg5NyA5Ni43MjA5IDcyLjY4OTdDMTAyLjE4MiA3Mi42ODk3IDEwNy4zODkgNzUuNzI1NiAxMTEuMjU2IDgwLjE4NTRDMTE1LjEyNCA4NC42NDUyIDExNy4zOTUgOTAuMzE3NyAxMTcuMzU1IDk2QzExNy4zMTUgMTAxLjY4MiAxMTUuMDQ0IDEwNy4zNTUgMTExLjE3NiAxMTEuODE0QzEwNy4zMDkgMTE2LjI3NCAxMDIuMTgyIDExOS4zMSA5Ni43MjA5IDExOS4zMUM5MS4yNTkxIDExOS4zMSA4Ni4wNTMxIDExNi4yNzQgODIuMTg1NCAxMTEuODE0Qzc4LjMxNzcgMTA3LjM1NSA3Ni4wNzQ1IDEwMS42ODIgNzYgOTZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNOTYgMTI0Qzg4LjI2ODAxIDEyNCA4MiAxMTcuNzMyIDgyIDExMEM4MiAxMDIuMjY4IDg4LjI2ODAxIDk2IDk2IDk2QzEwMy43MzIgOTYgMTEwIDEwMi4yNjggMTEwIDExMEMxMTAgMTE3LjczMiAxMDMuNzMyIDEyNCA5NiAxMjRaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJncmFkaWVudDBfbGluZWFyXzFfMSIgeDE9IjAiIHkxPSIwIiB4Mj0iMTkyIiB5Mj0iMTkyIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiNGRjZCNkIiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjRkZCNzREIi8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+",
"sizes": "192x192",
"type": "image/svg+xml"
},
{
"src": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiByeD0iNjQiIGZpbGw9InVybCgjZ3JhZGllbnQwX2xpbmVhcl8yXzIpIi8+CjxwYXRoIGQ9Ik0yNTYgMTI4QzI0MS4yNTUgMTI4IDIyOSAxNDAuMjU1IDIyOSAxNTVDMjI5IDE2OS43NDUgMjQxLjI1NSAxODIgMjU2IDE4MkMyNzAuNzQ1IDE4MiAyODMgMTY5Ljc0NSAyODMgMTU1QzI4MyAxNDAuMjU1IDI3MC43NDUgMTI4IDI1NiAxMjhaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMjA0IDI1NkMyMDQuMTk4IDI0MS44MzYgMjA4LjgzNiAyMjcuNjcyIDIxNi40NjkgMjE1LjEyMkMyMjQuMTAyIDIwMi41NzIgMjM0LjMwOSAxOTIuMTg5IDI0Ni4yNTYgMTkyLjE4OUMyNTguMjAzIDE5Mi4xODkgMjY4LjQxIDIwMi41NzIgMjc2LjA0MyAyMTUuMTIyQzI4My42NzYgMjI3LjY3MiAyODguMzE0IDI0MS44MzYgMjg4LjI1NiAyNTZDMjg4LjE5OCAyNzAuMTY0IDI4My41NiAyODQuMzI4IDI3NS45MjcgMjk2Ljg3OEMyNjguMjk0IDMwOS40MjggMjU4LjA4NyAzMTkuODExIDI0Ni4xNCAzMTkuODExQzIzNC4xOTMgMzE5LjgxMSAyMjMuOTg2IDMwOS40MjggMjE2LjM1MyAyOTYuODc4QzIwOC43MiAyODQuMzI4IDIwNC4wODIgMjcwLjE2NCAyMDQgMjU2WiIgZmlsbD0id2hpdGUiLz4KPHA+PC9wPgo8cGF0aCBkPSJNMjU2IDMzMkMyMzUuMDg4IDMzMiAyMTggMzE0LjkxMiAyMTggMjk0QzIxOCAyNzMuMDg4IDIzNS4wODggMjU2IDI1NiAyNTZDMjc2LjkxMiAyNTYgMjk0IDI3My4wODggMjk0IDI5NEMyOTQgMzE0LjkxMiAyNzYuOTEyIDMzMiAyNTYgMzMyWiIgZmlsbD0id2hpdGUiLz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0iZ3JhZGllbnQwX2xpbmVhcl8yXzIiIHgxPSIwIiB5MT0iMCIgeDI9IjUxMiIgeTI9IjUxMiIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjRkY2QjZCIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI0ZGQjc0RCIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPg==",
"sizes": "512x512",
"type": "image/svg+xml"
}
],
"categories": ["health", "fitness", "lifestyle"],
"lang": "en",
"dir": "ltr",
"scope": "/",
"prefer_related_applications": false
}

127
sw.js Normal file
View File

@@ -0,0 +1,127 @@
const CACHE_NAME = 'mindshift-v1';
const urlsToCache = [
'/',
'/cbt-therapy-app-mockup.html',
'/manifest.json'
];
// Install event - cache resources
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
// Fetch event - serve from cache when offline
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
// Clone the request
var fetchRequest = event.request.clone();
return fetch(fetchRequest).then(
function(response) {
// Check if valid response
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// Clone the response
var responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
}
).catch(function() {
// Offline fallback for HTML pages
if (event.request.destination === 'document') {
return caches.match('/cbt-therapy-app-mockup.html');
}
});
})
);
});
// Activate event - clean up old caches
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
if (cacheName !== CACHE_NAME) {
console.log('Deleting old cache:', cacheName);
return caches.delete(cacheName);
}
})
);
})
);
});
// Background sync for offline actions
self.addEventListener('sync', function(event) {
if (event.tag === 'background-sync') {
event.waitUntil(doBackgroundSync());
}
});
function doBackgroundSync() {
// Handle syncing offline data when back online
return Promise.resolve();
}
// Push notification handler
self.addEventListener('push', function(event) {
const options = {
body: event.data ? event.data.text() : 'Time for your CBT exercise!',
icon: '/data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTkyIiBoZWlnaHQ9IjE5MiIgdmlld0JveD0iMCAwIDE5MiAxOTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxOTIiIGhlaWdodD0iMTkyIiByeD0iMjQiIGZpbGw9InVybCgjZ3JhZGllbnQwX2xpbmVhcl8xXzEpIi8+CjxwYXRoIGQ9Ik05NiA0OEM5MC40NzcyIDQ4IDg2IDUyLjQ3NzIgODYgNjhDODYgNzMuNTIyOCA5MC40NzcyIDc4IDk2IDc4QzEwMS41MjIgNzggMTA2IDczLjUyMjggMTA2IDY4QzEwNiA2Mi40NzcyIDEwMS41MjIgNDggOTYgNDhaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNzYgOTZDNzYuMDc0NSA5MC4zMTc3IDc4LjMxNzcgODQuNjQ1MiA4Mi4xODU0IDgwLjE4NTRDODYuMDUzMSA3NS43MjU2IDkxLjI1OTEgNzIuNjg5NyA5Ni43MjA5IDcyLjY4OTdDMTAyLjE4MiA3Mi42ODk3IDEwNy4zODkgNzUuNzI1NiAxMTEuMjU2IDgwLjE4NTRDMTE1LjEyNCA4NC42NDUyIDExNy4zOTUgOTAuMzE3NyAxMTcuMzU1IDk2QzExNy4zMTUgMTAxLjY4MiAxMTUuMDQ0IDEwNy4zNTUgMTExLjE3NiAxMTEuODE0QzEwNy4zMDkgMTE2LjI3NCAxMDIuMTgyIDExOS4zMSA5Ni43MjA5IDExOS4zMUM5MS4yNTkxIDExOS4zMSA4Ni4wNTMxIDExNi4yNzQgODIuMTg1NCAxMTEuODE0Qzc4LjMxNzcgMTA3LjM1NSA3Ni4wNzQ1IDEwMS42ODIgNzYgOTZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNOTYgMTI0Qzg4LjI2ODAxIDEyNCA4MiAxMTcuNzMyIDgyIDExMEM4MiAxMDIuMjY4IDg4LjI2ODAxIDk2IDk2IDk2QzEwMy43MzIgOTYgMTEwIDEwMi4yNjggMTEwIDExMEMxMTAgMTE3LjczMiAxMDMuNzMyIDEyNCA5NiAxMjRaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJncmFkaWVudDBfbGluZWFyXzFfMSIgeDE9IjAiIHkxPSIwIiB4Mj0iMTkyIiB5Mj0iMTkyIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiNGRjZCNkIiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjRkZCNzREIi8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+',
badge: '/data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOTYiIGhlaWdodD0iOTYiIHZpZXdCb3g9IjAgMCA5NiA5NiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9Ijk2IiBoZWlnaHQ9Ijk2IiByeD0iMTIiIGZpbGw9IiNGRjZCNkIiLz4KPC9zdmc+',
vibrate: [100, 50, 100],
data: {
dateOfArrival: Date.now(),
primaryKey: 1
},
actions: [
{
action: 'explore',
title: 'Open App',
icon: '/data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE2IDJMMjggMTZMMTYgMzBMMCAxNkwxNiAyWiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+'
},
{
action: 'close',
title: 'Dismiss',
icon: '/data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTI0IDhMMTcgMTVMMjQgMjJMMjIgMjRMMTUgMTdMOCAyNEwxMCAyMkwxNyAxNUwxMCA4TDEyIDZMMTkgMTNMMTIgNkwyNCA4WiIgZmlsbD0id2hpdGUiLz4KPC9zdmc+'
}
]
};
event.waitUntil(
self.registration.showNotification('MindShift', options)
);
});
// Notification click handler
self.addEventListener('notificationclick', function(event) {
event.notification.close();
if (event.action === 'explore') {
// Open the app
event.waitUntil(
clients.openWindow('/')
);
}
});

90
test-api.js Normal file
View File

@@ -0,0 +1,90 @@
// Node.js 18+ has built-in fetch
async function testAPI() {
try {
// Test login (user already exists)
console.log('Testing login...');
const loginResponse = await fetch('http://localhost:12004/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: 'test@example.com',
password: 'test123'
})
});
if (loginResponse.ok) {
const loginData = await loginResponse.json();
console.log('Login successful:', loginData);
const token = loginData.token;
// Test mood tracking
console.log('\nTesting mood tracking...');
const moodResponse = await fetch('http://localhost:12004/api/mood/track', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
moodType: 'happy',
intensity: 8,
notes: 'Feeling good today!'
})
});
if (moodResponse.ok) {
console.log('Mood tracking successful');
} else {
console.log('Mood tracking failed');
}
// Test notifications
console.log('\nTesting notifications...');
const notifResponse = await fetch('http://localhost:12004/api/notifications', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
title: 'Test Notification',
message: 'This is a test notification',
type: 'info'
})
});
if (notifResponse.ok) {
console.log('Notification creation successful');
const notifData = await notifResponse.json();
console.log('Notification data:', notifData);
} else {
console.log('Notification creation failed');
const errorData = await notifResponse.json();
console.log('Error:', errorData);
}
// Get notifications
const getNotifResponse = await fetch('http://localhost:12004/api/notifications', {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (getNotifResponse.ok) {
const notifications = await getNotifResponse.json();
console.log('Notifications retrieved:', notifications.length);
}
} else {
console.log('Registration failed');
}
} catch (error) {
console.error('API test error:', error);
}
}
testAPI();