| | document.addEventListener('DOMContentLoaded', () => { |
| | |
| | const initializeStorage = () => { |
| | if (!localStorage.getItem('beAppData')) { |
| | const initialData = { |
| | reflections: [], |
| | decisions: [], |
| | mindData: { |
| | values: [], |
| | thinkingPatterns: [], |
| | growthAreas: [] |
| | }, |
| | streak: 0, |
| | daysActive: 0, |
| | reflectionsCount: 0, |
| | insightsCount: 0, |
| | decisionsCount: 0 |
| | }; |
| | localStorage.setItem('beAppData', JSON.stringify(initialData)); |
| | } |
| | return JSON.parse(localStorage.getItem('beAppData')); |
| | }; |
| |
|
| | |
| | let appData = initializeStorage(); |
| | updateStatistics(); |
| |
|
| | |
| | const resetDataBtn = document.getElementById('reset-data-btn'); |
| | if (resetDataBtn) { |
| | resetDataBtn.addEventListener('click', () => { |
| | if (confirm('Are you sure you want to reset all data? This action cannot be undone.')) { |
| | localStorage.removeItem('beAppData'); |
| | appData = initializeStorage(); |
| | updateStatistics(); |
| | updateUI(); |
| | showNotification('All data has been reset successfully!'); |
| | } |
| | }); |
| | } |
| |
|
| | |
| | const bottomNavBtns = document.querySelectorAll('.bottom-nav-btn'); |
| | const tabContents = document.querySelectorAll('.tab-content'); |
| |
|
| | bottomNavBtns.forEach(btn => { |
| | btn.addEventListener('click', () => { |
| | const targetTab = btn.getAttribute('data-tab'); |
| |
|
| | |
| | bottomNavBtns.forEach(b => b.classList.remove('active')); |
| | btn.classList.add('active'); |
| |
|
| | |
| | tabContents.forEach(content => { |
| | if (content.id === targetTab) { |
| | content.classList.add('active'); |
| | } else { |
| | content.classList.remove('active'); |
| | } |
| | }); |
| |
|
| | |
| | window.scrollTo({ top: 0, behavior: 'smooth' }); |
| | }); |
| | }); |
| |
|
| | |
| | const saveReflectionBtn = document.getElementById('save-reflection-btn'); |
| | const reflectionInput = document.getElementById('reflection-input'); |
| | |
| | if (saveReflectionBtn && reflectionInput) { |
| | saveReflectionBtn.addEventListener('click', () => { |
| | const reflection = reflectionInput.value.trim(); |
| | if (reflection) { |
| | |
| | appData.reflections.unshift({ |
| | text: reflection, |
| | date: new Date().toISOString(), |
| | mood: 'Reflective' |
| | }); |
| | |
| | |
| | appData.reflectionsCount++; |
| | appData.daysActive = Math.min(appData.daysActive + 1, appData.reflections.length); |
| | appData.streak++; |
| | |
| | localStorage.setItem('beAppData', JSON.stringify(appData)); |
| | |
| | |
| | reflectionInput.value = ''; |
| | |
| | |
| | updateStatistics(); |
| | updateReflections(); |
| | |
| | |
| | showNotification('Reflection saved successfully!'); |
| | } else { |
| | alert('Please enter your reflection before saving.'); |
| | } |
| | }); |
| | } |
| |
|
| | |
| | const decisionSteps = document.querySelectorAll('.decision-step'); |
| | const decisionTypeSelector = document.getElementById('decision-type-selector'); |
| | let selectedDecisionType = null; |
| |
|
| | |
| | if (decisionTypeSelector) { |
| | decisionTypeSelector.addEventListener('click', (e) => { |
| | const button = e.target.closest('.decision-type-btn'); |
| | if (button) { |
| | |
| | decisionTypeSelector.querySelectorAll('.decision-type-btn').forEach(btn => { |
| | btn.classList.remove('selected', 'border-indigo-500', 'bg-indigo-100'); |
| | }); |
| | |
| | button.classList.add('selected', 'border-indigo-500', 'bg-indigo-100'); |
| | selectedDecisionType = button.getAttribute('data-type'); |
| | } |
| | }); |
| | } |
| |
|
| | const navigateToStep = (targetStepId) => { |
| | decisionSteps.forEach(step => { |
| | if (step.id === targetStepId) { |
| | step.classList.remove('hidden'); |
| | } else { |
| | step.classList.add('hidden'); |
| | } |
| | }); |
| | |
| | |
| | const deciderCard = document.querySelector('#decider .bg-white'); |
| | if (deciderCard) { |
| | deciderCard.scrollIntoView({ behavior: 'smooth', block: 'start' }); |
| | } |
| | }; |
| |
|
| | |
| | document.getElementById('to-step-2')?.addEventListener('click', () => { |
| | const description = document.getElementById('decision-description').value.trim(); |
| | if (!description) { |
| | alert('Please describe the decision you are facing.'); |
| | return; |
| | } |
| | if (!selectedDecisionType) { |
| | alert('Please select a decision type.'); |
| | return; |
| | } |
| | navigateToStep('decision-step-2'); |
| | }); |
| | |
| | document.getElementById('to-step-3')?.addEventListener('click', () => { |
| | |
| | const options = document.querySelectorAll('.option-item'); |
| | let valid = true; |
| | |
| | options.forEach(option => { |
| | const title = option.querySelector('.option-title').value.trim(); |
| | const description = option.querySelector('.option-description').value.trim(); |
| | |
| | if (!title || !description) { |
| | valid = false; |
| | } |
| | }); |
| | |
| | if (!valid) { |
| | alert('Please complete all option details before proceeding.'); |
| | return; |
| | } |
| | |
| | |
| | generateAnalysis(); |
| | navigateToStep('decision-step-3'); |
| | }); |
| |
|
| | |
| | document.querySelectorAll('.back-btn').forEach(button => { |
| | button.addEventListener('click', () => { |
| | const targetStepId = button.getAttribute('data-target'); |
| | navigateToStep(targetStepId); |
| | }); |
| | }); |
| |
|
| | |
| | const addOptionBtn = document.getElementById('add-option'); |
| | const optionContainer = document.getElementById('option-container'); |
| | let optionCounter = 2; |
| |
|
| | if (addOptionBtn && optionContainer) { |
| | addOptionBtn.addEventListener('click', () => { |
| | optionCounter++; |
| | const newOption = document.createElement('div'); |
| | newOption.className = 'option-item p-4 border border-gray-300 rounded-lg bg-gray-50 relative slide-in'; |
| | newOption.innerHTML = ` |
| | <input type="text" class="option-title w-full p-2 border-b border-gray-300 mb-2 focus:outline-none focus:border-indigo-500 font-medium bg-transparent" placeholder="Option ${optionCounter} Title"> |
| | <textarea class="option-description w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 h-20 resize-none mt-1 transition duration-150 ease-in-out" placeholder="Describe this option..."></textarea> |
| | <button class="remove-option absolute top-2 right-2 text-gray-400 hover:text-red-500 text-sm p-1" title="Remove option">×</button> |
| | `; |
| | optionContainer.appendChild(newOption); |
| | |
| | |
| | newOption.querySelector('.remove-option').addEventListener('click', handleRemoveOption); |
| | }); |
| | } |
| |
|
| | |
| | const handleRemoveOption = (event) => { |
| | if (optionContainer.querySelectorAll('.option-item').length > 2) { |
| | event.target.closest('.option-item').remove(); |
| | } else { |
| | alert("At least two options are required."); |
| | } |
| | }; |
| |
|
| | document.querySelectorAll('.remove-option').forEach(btn => { |
| | btn.addEventListener('click', handleRemoveOption); |
| | }); |
| |
|
| | |
| | const generateAnalysis = () => { |
| | const options = []; |
| | document.querySelectorAll('.option-item').forEach(item => { |
| | const title = item.querySelector('.option-title').value || 'Unnamed Option'; |
| | const description = item.querySelector('.option-description').value || 'No description provided.'; |
| | options.push({ title, description }); |
| | }); |
| |
|
| | const decisionDesc = document.getElementById('decision-description')?.value || 'the decision'; |
| | const decisionType = selectedDecisionType || 'General'; |
| |
|
| | |
| | let prosConsHtml = `<p>Comparing options for <strong>${decisionDesc}</strong> (Type: ${decisionType}):</p>`; |
| | let valueHtml = `<p>Considering your values related to a <strong>${decisionType}</strong> decision:</p>`; |
| | let recommendationHtml = `<p>Recommendation for <strong>${decisionDesc}</strong>:</p>`; |
| |
|
| | options.forEach((opt, index) => { |
| | |
| | const pros = []; |
| | const cons = []; |
| | |
| | |
| | const words = opt.description.toLowerCase().split(/\s+/); |
| | if (words.some(w => ['good', 'great', 'benefit', 'advantage', 'positive', 'pro'].includes(w))) { |
| | pros.push('Positive aspects mentioned'); |
| | } |
| | if (words.some(w => ['bad', 'drawback', 'downside', 'negative', 'con', 'concern'].includes(w))) { |
| | cons.push('Concerns or drawbacks noted'); |
| | } |
| | |
| | |
| | if (pros.length === 0) pros.push('Potential benefits could be explored further'); |
| | if (cons.length === 0) cons.push('Consider any potential downsides'); |
| | |
| | prosConsHtml += ` |
| | <div class="mt-2 p-2 border-l-2 border-indigo-200"> |
| | <strong>${opt.title}:</strong> |
| | <div class="mt-1"> |
| | <span class="text-green-600 font-medium">Pros:</span> ${pros.join(', ')} |
| | </div> |
| | <div class="mt-1"> |
| | <span class="text-red-600 font-medium">Cons:</span> ${cons.join(', ')} |
| | </div> |
| | </div> |
| | `; |
| | |
| | |
| | const values = appData.mindData.values.length > 0 ? |
| | appData.mindData.values : |
| | ['Growth', 'Balance', 'Security', 'Freedom', 'Connection']; |
| | const alignedValue = values[index % values.length]; |
| | |
| | valueHtml += `<p class="mt-1">- <strong>${opt.title}</strong> appears to align with your value of <strong>${alignedValue}</strong> based on the description.</p>`; |
| | }); |
| | |
| | |
| | const recommendedOption = options[0]; |
| | recommendationHtml += ` |
| | <p class="mt-2">Based on the analysis, <strong>${recommendedOption.title}</strong> appears to be a strong option, particularly considering your values.</p> |
| | <p class="mt-1"><strong>Considerations:</strong> Before finalizing your decision, reflect on how each option affects your long-term goals and whether there are any aspects you haven't fully explored.</p> |
| | `; |
| |
|
| | |
| | document.getElementById('pros-cons-analysis').innerHTML = prosConsHtml; |
| | document.getElementById('value-alignment-analysis').innerHTML = valueHtml; |
| | document.getElementById('ai-recommendation').innerHTML = recommendationHtml; |
| | }; |
| |
|
| | |
| | document.getElementById('save-decision')?.addEventListener('click', () => { |
| | const decisionDesc = document.getElementById('decision-description')?.value || 'Unnamed Decision'; |
| | const decisionType = selectedDecisionType || 'General'; |
| | |
| | |
| | const options = []; |
| | document.querySelectorAll('.option-item').forEach(item => { |
| | const title = item.querySelector('.option-title').value || 'Unnamed Option'; |
| | const description = item.querySelector('.option-description').value || 'No description'; |
| | options.push({ title, description }); |
| | }); |
| | |
| | |
| | appData.decisions.unshift({ |
| | title: decisionDesc, |
| | type: decisionType, |
| | options: options, |
| | date: new Date().toISOString(), |
| | status: 'Pending' |
| | }); |
| | |
| | appData.decisionsCount++; |
| | localStorage.setItem('beAppData', JSON.stringify(appData)); |
| | |
| | |
| | updateStatistics(); |
| | updateDecisions(); |
| | |
| | |
| | showNotification('Decision analysis saved successfully!'); |
| | navigateToStep('decision-step-1'); |
| | document.getElementById('decision-description').value = ''; |
| | decisionTypeSelector.querySelectorAll('.decision-type-btn').forEach(btn => { |
| | btn.classList.remove('selected', 'border-indigo-500', 'bg-indigo-100'); |
| | }); |
| | selectedDecisionType = null; |
| | }); |
| |
|
| | |
| | const chatMessages = document.getElementById('chat-messages'); |
| | const chatInput = document.getElementById('chat-input'); |
| | const sendChatBtn = document.getElementById('send-chat-btn'); |
| |
|
| | const addChatMessage = (message, isUser = true) => { |
| | if (!message.trim()) return; |
| |
|
| | const messageElement = document.createElement('div'); |
| | messageElement.classList.add('chat-message', isUser ? 'user-message' : 'ai-message', 'slide-in'); |
| |
|
| | const contentElement = document.createElement('p'); |
| | contentElement.classList.add('text-gray-800'); |
| | contentElement.textContent = message; |
| |
|
| | const timeElement = document.createElement('span'); |
| | timeElement.classList.add('text-xs', 'text-gray-500', 'mt-1', 'block', 'text-right'); |
| | timeElement.textContent = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); |
| |
|
| | messageElement.appendChild(contentElement); |
| | messageElement.appendChild(timeElement); |
| | |
| | if (chatMessages) { |
| | chatMessages.appendChild(messageElement); |
| | |
| | chatMessages.scrollTop = chatMessages.scrollHeight; |
| | } |
| | }; |
| |
|
| | const handleSendChat = () => { |
| | if (!chatInput) return; |
| | |
| | const message = chatInput.value; |
| | if (message.trim()) { |
| | addChatMessage(message, true); |
| | chatInput.value = ''; |
| |
|
| | |
| | setTimeout(() => { |
| | |
| | let aiResponse = ''; |
| | const lowerMessage = message.toLowerCase(); |
| | |
| | if (lowerMessage.includes('career') || lowerMessage.includes('job') || lowerMessage.includes('work')) { |
| | aiResponse = `It sounds like you're thinking about your career path. Professional transitions can be challenging but also exciting opportunities for growth. What specific aspects of your career are you looking to change?`; |
| | } else if (lowerMessage.includes('relationship') || lowerMessage.includes('partner') || lowerMessage.includes('friend')) { |
| | aiResponse = `Relationships are such an important part of our wellbeing. I notice you're thinking about connections in your life. Would you like to explore how your values might guide your approach to this relationship?`; |
| | } else if (lowerMessage.includes('stress') || lowerMessage.includes('anxiety') || lowerMessage.includes('worry')) { |
| | aiResponse = `I hear that you're experiencing some challenging emotions. Remember that it's normal to feel this way sometimes. Would it help to discuss some simple mindfulness techniques that could help manage these feelings in the moment?`; |
| | } else { |
| | aiResponse = `Thanks for sharing that. I'm curious to learn more about how this relates to your personal growth journey. Could you tell me what outcome you're hoping for?`; |
| | } |
| | |
| | addChatMessage(aiResponse, false); |
| | }, 1000); |
| | } |
| | }; |
| |
|
| | if (sendChatBtn && chatInput) { |
| | sendChatBtn.addEventListener('click', handleSendChat); |
| | chatInput.addEventListener('keypress', (e) => { |
| | if (e.key === 'Enter') { |
| | handleSendChat(); |
| | } |
| | }); |
| | } |
| |
|
| | |
| | function updateStatistics() { |
| | document.getElementById('streak-count').textContent = appData.streak; |
| | document.getElementById('reflections-count').textContent = appData.reflectionsCount; |
| | document.getElementById('insights-count').textContent = appData.insightsCount; |
| | document.getElementById('decisions-count').textContent = appData.decisionsCount; |
| | document.getElementById('days-active-count').textContent = appData.daysActive; |
| | |
| | |
| | const progressCircle = document.querySelector('.progress-circle-value'); |
| | const progressText = document.querySelector('.progress-circle-text'); |
| | let progress = 0; |
| | |
| | if (appData.streak > 0) { |
| | progress = Math.min(Math.round((appData.streak / 7) * 100), 100); |
| | progressCircle.style.strokeDashoffset = 100 - progress; |
| | progressText.textContent = progress + '%'; |
| | } else { |
| | progressCircle.style.strokeDashoffset = 100; |
| | progressText.textContent = '0%'; |
| | } |
| | } |
| |
|
| | function updateReflections() { |
| | const container = document.getElementById('past-reflections'); |
| | const noReflectionsMsg = document.getElementById('no-reflections-message'); |
| | |
| | if (container) { |
| | |
| | Array.from(container.children).forEach(child => { |
| | if (child.id !== 'no-reflections-message') { |
| | child.remove(); |
| | } |
| | }); |
| | |
| | if (appData.reflections.length > 0) { |
| | |
| | if (noReflectionsMsg) noReflectionsMsg.classList.add('hidden'); |
| | |
| | |
| | appData.reflections.slice(0, 6).forEach(reflection => { |
| | const date = new Date(reflection.date); |
| | const timeAgo = getTimeAgo(date); |
| | |
| | const card = document.createElement('div'); |
| | card.className = 'bg-white border border-gray-200 rounded-lg p-4 shadow-sm hover:shadow-md transition-shadow'; |
| | card.innerHTML = ` |
| | <div class="flex justify-between items-start mb-3"> |
| | <span class="text-xs text-gray-500">${timeAgo}</span> |
| | <span class="text-xs font-medium text-indigo-600">Mood: ${reflection.mood || 'Reflective'}</span> |
| | </div> |
| | <p class="text-gray-700 text-sm">${reflection.text}</p> |
| | `; |
| | container.appendChild(card); |
| | }); |
| | } else { |
| | |
| | if (noReflectionsMsg) noReflectionsMsg.classList.remove('hidden'); |
| | } |
| | } |
| | } |
| |
|
| | function updateDecisions() { |
| | const container = document.getElementById('recent-decisions'); |
| | const noDecisionsMsg = document.getElementById('no-decisions-message'); |
| | |
| | if (container) { |
| | |
| | Array.from(container.children).forEach(child => { |
| | if (child.id !== 'no-decisions-message') { |
| | child.remove(); |
| | } |
| | }); |
| | |
| | if (appData.decisions.length > 0) { |
| | |
| | if (noDecisionsMsg) noDecisionsMsg.classList.add('hidden'); |
| | |
| | |
| | appData.decisions.slice(0, 4).forEach((decision, index) => { |
| | const date = new Date(decision.date); |
| | const timeAgo = getTimeAgo(date); |
| | |
| | const card = document.createElement('div'); |
| | card.className = 'decision-card bg-white border border-gray-200 rounded-lg p-5 shadow hover:shadow-md'; |
| | card.innerHTML = ` |
| | <div class="flex justify-between items-start mb-3"> |
| | <h3 class="font-semibold text-lg text-gray-800">${decision.title}</h3> |
| | <span class="text-xs text-gray-500">${timeAgo}</span> |
| | </div> |
| | <p class="text-gray-600 text-sm mb-3">Analysis of ${decision.type} decision with ${decision.options.length} options.</p> |
| | <div class="flex justify-between items-center mt-4"> |
| | <span class="px-3 py-1 bg-${index % 2 === 0 ? 'green' : 'yellow'}-100 text-${index % 2 === 0 ? 'green' : 'yellow'}-800 rounded-full text-xs font-medium">${index % 2 === 0 ? 'Resolved' : 'Pending'}</span> |
| | <button class="text-indigo-600 hover:text-indigo-800 text-sm font-medium focus:outline-none transition duration-150 ease-in-out">View Details</button> |
| | </div> |
| | `; |
| | container.appendChild(card); |
| | }); |
| | } else { |
| | |
| | if (noDecisionsMsg) noDecisionsMsg.classList.remove('hidden'); |
| | } |
| | } |
| | } |
| |
|
| | function updateMindData() { |
| | const valuesList = document.getElementById('values-list'); |
| | const thinkingPatternsList = document.getElementById('thinking-patterns-list'); |
| | |
| | if (valuesList) { |
| | valuesList.innerHTML = ''; |
| | |
| | const values = appData.mindData.values.length > 0 ? |
| | appData.mindData.values : |
| | ['Start reflecting to discover your values']; |
| | |
| | values.forEach(value => { |
| | const li = document.createElement('li'); |
| | li.textContent = value; |
| | valuesList.appendChild(li); |
| | }); |
| | } |
| | |
| | if (thinkingPatternsList) { |
| | thinkingPatternsList.innerHTML = ''; |
| | |
| | const patterns = appData.mindData.thinkingPatterns.length > 0 ? |
| | appData.mindData.thinkingPatterns : |
| | ['Make decisions to identify your thinking patterns']; |
| | |
| | patterns.forEach(pattern => { |
| | const li = document.createElement('li'); |
| | li.textContent = pattern; |
| | thinkingPatternsList.appendChild(li); |
| | }); |
| | } |
| | } |
| |
|
| | function updateUI() { |
| | updateStatistics(); |
| | updateReflections(); |
| | updateDecisions(); |
| | updateMindData(); |
| | } |
| |
|
| | |
| | function getTimeAgo(date) { |
| | const now = new Date(); |
| | const diffInSeconds = Math.floor((now - date) / 1000); |
| | |
| | if (diffInSeconds < 60) return 'Just now'; |
| | if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)} minutes ago`; |
| | if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)} hours ago`; |
| | if (diffInSeconds < 604800) return `${Math.floor(diffInSeconds / 86400)} days ago`; |
| | |
| | return date.toLocaleDateString(); |
| | } |
| |
|
| | function showNotification(message) { |
| | |
| | const existingNotification = document.querySelector('.notification'); |
| | if (existingNotification) { |
| | existingNotification.remove(); |
| | } |
| | |
| | |
| | const notification = document.createElement('div'); |
| | notification.className = 'notification'; |
| | notification.textContent = message; |
| | document.body.appendChild(notification); |
| | |
| | |
| | setTimeout(() => { |
| | if (notification && notification.parentNode) { |
| | notification.remove(); |
| | } |
| | }, 3000); |
| | } |
| |
|
| | |
| | updateUI(); |
| | }); |
| |
|