import { Component, OnInit, OnDestroy, ChangeDetectorRef, Injector, HostListener } from '@angular/core'; import { Router, RouterOutlet, NavigationEnd } from '@angular/router'; import { CommonModule } from '@angular/common'; import { Subscription } from 'rxjs'; import { SignupService } from './auth/sign-up/sign-up.service'; import { AuthService } from './services/auth.service'; import { SignInComponent } from './auth/sign-in/sign-in.component'; import { SignUpComponent } from './auth/sign-up/sign-up.component'; import { LlmQaService } from './services/llm-qa.service'; @Component({ selector: 'app-root', standalone: true, imports: [RouterOutlet, CommonModule, SignInComponent, SignUpComponent], templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit, OnDestroy { title = 'py-match'; showMarriageSubnav = false; showRoleSubnav = false; activeRoleMenu: string | null = null; isSignedIn = false; authModal: 'signin' | 'signup' | null = null; userFullName: string = ''; userInitials: string = ''; showChatbot = false; // Progress tracking properties profileCompleted = false; expectationsCompleted = false; assessmentCompleted = false; matchingCompleted = false; // Assessment popup state showAssessmentPopup = false; // NEW: Sign out confirmation popup showSignOutPopup = false; // Mobile menu properties showMobileNav = false; activeRoleMenuMobile: string | null = null; private authSubscription!: Subscription; private routerEventsSub!: Subscription; constructor( private router: Router, private signupService: SignupService, private authService: AuthService, private changeDetectorRef: ChangeDetectorRef, private injector: Injector ) { // Expose component instance for direct access (window as any).appComponentRef = this; } ngOnInit() { // Listen for auth state changes from other components window.addEventListener('authStateChanged', () => { console.log('?? Auth state change detected in AppComponent'); this.changeDetectorRef.detectChanges(); }); // Subscribe to authentication state changes from AuthService this.authSubscription = this.authService.userId$.subscribe(userId => { this.isSignedIn = !!userId; if (userId) { this.loadUserData(userId); console.log('? User signed in, loading data for user:', userId); } else { this.userFullName = ''; this.userInitials = ''; // Load progress from localStorage when signed out this.loadProgressFromAnySource(); console.log('?? User signed out, loading preserved progress from localStorage'); } // Force UI update when auth state changes this.changeDetectorRef.detectChanges(); }); // Listen for progress updates from other components this.setupProgressListeners(); // Also listen for storage events to sync across tabs window.addEventListener('storage', (event) => { if (event.key?.startsWith('user_progress_')) { this.syncProgressFromStorage(); } }); // Load progress from localStorage on initial app load (for returning users) this.loadInitialProgress(); // Expose methods globally for direct access (window as any).markProfileCompleted = () => { this.markStepCompleted('profile'); }; (window as any).markExpectationsCompleted = () => { this.markStepCompleted('expectations'); }; (window as any).markAssessmentCompleted = () => { this.markStepCompleted('assessment'); }; (window as any).markMatchingCompleted = () => { this.markStepCompleted('matching'); }; // Listen to router events to toggle body scroll this.routerEventsSub = this.router.events.subscribe(event => { if (event instanceof NavigationEnd) { this.toggleBodyScroll(); // Close mobile nav on navigation this.showMobileNav = false; } }); // Initial check this.toggleBodyScroll(); } ngOnDestroy() { if (this.authSubscription) { this.authSubscription.unsubscribe(); } // Clean up global methods if ((window as any).markProfileCompleted) { delete (window as any).markProfileCompleted; } if ((window as any).markExpectationsCompleted) { delete (window as any).markExpectationsCompleted; } if ((window as any).markAssessmentCompleted) { delete (window as any).markAssessmentCompleted; } if ((window as any).markMatchingCompleted) { delete (window as any).markMatchingCompleted; } if ((window as any).appComponentRef) { delete (window as any).appComponentRef; } if (this.routerEventsSub) { this.routerEventsSub.unsubscribe(); } // Remove event listeners window.removeEventListener('authStateChanged', () => { }); window.removeEventListener('storage', () => { }); } // NEW: Load progress on initial app load (for returning users) private loadInitialProgress(): void { // Always load progress on app initialization, regardless of auth state this.loadProgressFromAnySource(); console.log('?? Initial progress loaded:', { profile: this.profileCompleted, expectations: this.expectationsCompleted, assessment: this.assessmentCompleted, matching: this.matchingCompleted, isSignedIn: this.isSignedIn }); } // NEW: Try to load progress from any user in localStorage private tryToLoadProgressFromAnyUser(): void { // Look for any user progress in localStorage for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key?.startsWith('user_progress_')) { try { const progressData = JSON.parse(localStorage.getItem(key) || '{}'); this.profileCompleted = progressData.profileCompleted || false; this.expectationsCompleted = progressData.expectationsCompleted || false; this.assessmentCompleted = progressData.assessmentCompleted || false; this.matchingCompleted = progressData.matchingCompleted || false; console.log('?? Loaded progress from localStorage (no user):', progressData); break; } catch (e) { console.error('Error parsing progress data:', e); } } } } private syncProgressFromStorage(): void { const userId = this.authService.userId; if (userId) { const progressData = localStorage.getItem(`user_progress_${userId}`); if (progressData) { try { const progress = JSON.parse(progressData); this.profileCompleted = progress.profileCompleted || false; this.expectationsCompleted = progress.expectationsCompleted || false; this.assessmentCompleted = progress.assessmentCompleted || false; this.matchingCompleted = progress.matchingCompleted || false; console.log('?? Progress synced from localStorage:', progress); } catch (e) { console.error('Error parsing progress data:', e); } } else { console.log('?? No progress data found in localStorage for user:', userId); } } } private setupProgressListeners(): void { // Listen for profile completion events window.addEventListener('profileCompleted', (event: any) => { console.log('?? PROFILE COMPLETED EVENT RECEIVED - AppComponent'); console.log('?? Event details:', event.detail); this.profileCompleted = true; this.saveProgress(); console.log('? Progress after update:', { profile: this.profileCompleted, expectations: this.expectationsCompleted, assessment: this.assessmentCompleted, matching: this.matchingCompleted }); // Force UI update this.changeDetectorRef.detectChanges(); }); // Listen for expectations completion events window.addEventListener('expectationsCompleted', (event: any) => { console.log('?? EXPECTATIONS COMPLETED EVENT RECEIVED - AppComponent'); this.expectationsCompleted = true; this.saveProgress(); this.changeDetectorRef.detectChanges(); }); // Listen for assessment completion events window.addEventListener('assessmentCompleted', (event: any) => { console.log('?? ASSESSMENT COMPLETED EVENT RECEIVED - AppComponent'); this.assessmentCompleted = true; this.saveProgress(); this.changeDetectorRef.detectChanges(); }); // Listen for matching completion events window.addEventListener('matchingCompleted', (event: any) => { console.log('?? MATCHING COMPLETED EVENT RECEIVED - AppComponent'); this.matchingCompleted = true; this.saveProgress(); this.changeDetectorRef.detectChanges(); }); } // UPDATED: Load user data and check database progress private loadUserData(userId: number): void { this.getUserInfo(userId); this.checkDatabaseProgress(userId); } private getUserInfo(userId: number): void { const userData = sessionStorage.getItem('user_data'); if (userData) { try { const user = JSON.parse(userData); this.userFullName = user.name || ''; this.userInitials = this.getInitials(this.userFullName); return; } catch (e) { console.error('Error parsing user data from storage:', e); } } this.userFullName = 'User'; this.userInitials = 'US'; } private getInitials(fullName: string): string { if (!fullName) return 'US'; const names = fullName.trim().split(' '); if (names.length === 1) { return names[0].substring(0, 2).toUpperCase(); } else { return (names[0].substring(0, 1) + names[names.length - 1].substring(0, 1)).toUpperCase(); } } // UPDATED: Check database progress for all steps - FIXED to properly handle responses private checkDatabaseProgress(userId: number): void { if (!userId) return; console.log('?? Checking database progress for user:', userId); // 1. Check Marriage profile completion this.signupService.checkMarriageProfile(userId).subscribe({ next: (profileRes: any) => { const profileExists = profileRes.exists || false; console.log('? Marriage profile check response:', profileExists); // Only change state if the database explicitly confirms completion // We do NOT set it to false here to prevent UI locking during network lag if (profileExists) { this.profileCompleted = true; this.saveProgress(); this.changeDetectorRef.detectChanges(); } }, error: (err: any) => { // In production (Hugging Face), requests may timeout or fail due to CORS/Latency // We keep the local state (from localStorage) instead of forcing it to false console.error('? Marriage profile check API error:', err); } }); // 2. Check Expectations completion this.signupService.checkExpectations(userId).subscribe({ next: (expectationsRes: any) => { const expectationsExists = expectationsRes.exists || false; console.log('? Expectations check response:', expectationsExists); if (expectationsExists) { this.expectationsCompleted = true; this.saveProgress(); this.changeDetectorRef.detectChanges(); } }, error: (err: any) => { console.error('? Expectations check API error:', err); } }); // 3. Check Assessment completion this.signupService.checkAssessment(userId).subscribe({ next: (assessmentRes: any) => { const assessmentExists = assessmentRes.exists || false; console.log('? Assessment check response:', assessmentExists); if (assessmentExists) { this.assessmentCompleted = true; this.matchingCompleted = true; this.saveProgress(); this.changeDetectorRef.detectChanges(); } }, error: (err: any) => { console.error('? Assessment check API error:', err); } }); } private saveProgress(): void { const userId = this.authService.userId; const progressData = { profileCompleted: this.profileCompleted, expectationsCompleted: this.expectationsCompleted, assessmentCompleted: this.assessmentCompleted, matchingCompleted: this.matchingCompleted, lastUpdated: new Date().toISOString() }; if (userId) { // Save user-specific progress localStorage.setItem(`user_progress_${userId}`, JSON.stringify(progressData)); console.log('?? Progress saved to user localStorage:', progressData); } else { // Save anonymous progress localStorage.setItem('anonymous_progress', JSON.stringify(progressData)); console.log('?? Progress saved to anonymous localStorage:', progressData); } } get isIntroPage(): boolean { const url = (this.router.url || '').split('?')[0]; return url === '/' || url.startsWith('/intro'); } get isMatchingPage(): boolean { const url = (this.router.url || '').split('?')[0]; return url.startsWith('/matchinglist'); } // ? ADDED: Calculate progress percentage getProgressPercentage(): number { const totalSteps = 4; const completedSteps = [ this.profileCompleted, this.expectationsCompleted, this.assessmentCompleted, this.matchingCompleted ].filter(Boolean).length; return (completedSteps / totalSteps) * 100; } // ? ADDED: Navigation methods goHome(): void { // If already on intro/home page, just scroll to top smoothly if (this.isIntroPage) { try { window.scrollTo({ top: 0, behavior: 'smooth' }); } catch (e) { // fallback window.scrollTo(0, 0); } return; } this.router.navigate(['/']); } // Scroll methods scrollToFeatures(): void { const featuresElement = document.getElementById('features'); if (featuresElement) { featuresElement.scrollIntoView({ behavior: 'smooth' }); } } scrollToJourney(): void { const journeyElement = document.getElementById('journey'); if (journeyElement) { journeyElement.scrollIntoView({ behavior: 'smooth' }); } } /** * Scroll to FAQ section (specialTitle) from any page. * If not on intro page, navigate to intro then scroll. */ scrollToFAQ(): void { const scrollToFaqElement = (el: Element | null) => { if (!el) return; try { el.scrollIntoView({ behavior: 'smooth', block: 'start' }); } catch (e) { // fallback (el as HTMLElement).scrollIntoView(); } }; const findFaqTarget = (): Element | null => { // Prefer the section container over the heading const sectionEl = document.querySelector('.special-content'); if (sectionEl) return sectionEl; // fallback to heading id if present return document.getElementById('specialTitle'); }; // If already on intro page, scroll directly if (this.isIntroPage) { const faqSection = findFaqTarget(); if (faqSection) { scrollToFaqElement(faqSection); } } else { // Navigate to intro page, then scroll after navigation this.router.navigate(['/']).then(() => { setTimeout(() => { const faqSection = findFaqTarget(); if (faqSection) { scrollToFaqElement(faqSection); } }, 300); }); } } // Role subnavigation methods toggleRoleSubnav(): void { this.showRoleSubnav = !this.showRoleSubnav; if (!this.showRoleSubnav) { this.activeRoleMenu = null; } } hideRoleSubnav(): void { this.showRoleSubnav = false; this.activeRoleMenu = null; } toggleRoleMenu(menu: string): void { this.activeRoleMenu = this.activeRoleMenu === menu ? null : menu; } // Mobile menu methods toggleRoleMenuMobile(menu: string): void { this.activeRoleMenuMobile = this.activeRoleMenuMobile === menu ? null : menu; } closeMobileNav(): void { this.showMobileNav = false; this.activeRoleMenuMobile = null; } // UPDATED: Progress step validation - FIXED to properly check database progress isStepEnabled(step: string): boolean { console.log(`?? Checking if step "${step}" is enabled:`, { profile: this.profileCompleted, expectations: this.expectationsCompleted, assessment: this.assessmentCompleted, matching: this.matchingCompleted, isSignedIn: this.isSignedIn }); // If user is not signed in, only allow profile step if (!this.isSignedIn) { return step === 'profile'; } // For signed-in users, steps remain enabled once completed switch (step) { case 'profile': return true; // Always enabled for signed-in users case 'expectations': // Enabled if profile is completed OR if expectations were already completed return this.profileCompleted || this.expectationsCompleted; case 'assessment': // Enabled if expectations are completed OR if assessment was already completed return this.expectationsCompleted || this.assessmentCompleted; case 'matching': // Enabled if assessment is completed OR if matching was already completed return this.assessmentCompleted || this.matchingCompleted; default: return false; } } // UPDATED: Navigation method that directly navigates to profile navigateToProfile(role: string): void { const userId = this.authService.userId; if (!userId) { this.authModal = 'signin'; return; } // Close mobile nav if open if (this.showMobileNav) { this.showMobileNav = false; } // Directly navigate to profile, don't use next step logic console.log('?? AppComponent - Direct navigation to profile for role:', role); this.router.navigate(['/question-answer'], { queryParams: { role: role, user_id: userId } }); } // Helper method to determine next step (same logic as IntroPageComponent) private getNextAvailableStep(): string { console.log('?? AppComponent - Progress data for navigation:', { profile: this.profileCompleted, expectations: this.expectationsCompleted, assessment: this.assessmentCompleted, matching: this.matchingCompleted }); // Use the same logic as sub-nav to determine next step if (this.isStepEnabled('profile') && !this.profileCompleted) { console.log('?? Next step: Profile (enabled and not completed)'); return 'profile'; } else if (this.isStepEnabled('expectations') && !this.expectationsCompleted) { console.log('?? Next step: Expectations (enabled and not completed)'); return 'expectations'; } else if (this.isStepEnabled('assessment') && !this.assessmentCompleted) { console.log('?? Next step: Assessment (enabled and not completed)'); return 'assessment'; } else if (this.isStepEnabled('matching') && !this.matchingCompleted) { console.log('?? Next step: Matching (enabled and not completed)'); return 'matching'; } else { // If all steps are completed, default to matching console.log('?? Next step: Matching (all steps completed or default)'); return 'matching'; } } // Helper method to navigate to specific step private navigateToStep(step: string, role: string): void { const userId = this.authService.userId; if (!userId) return; switch (step) { case 'profile': this.router.navigate(['/question-answer'], { queryParams: { role: role, user_id: userId } }); break; case 'expectations': this.router.navigate(['/userpreferences'], { queryParams: { user_id: userId } }); break; case 'assessment': // Direct navigation to assessment without checking completion this.router.navigate(['/llmquiz'], { queryParams: { user_id: userId, show_welcome: 'true' } }); break; case 'matching': this.router.navigate(['/matchinglist'], { queryParams: { user_id: userId } }); break; default: // Default to profile if something goes wrong this.router.navigate(['/question-answer'], { queryParams: { role: role, user_id: userId } }); } } navigateToExpectations(role: string): void { const userId = this.authService.userId; console.log('?? Navigate to expectations - User ID:', userId); if (!userId) { console.log('User not authenticated, showing signin modal'); this.authModal = 'signin'; return; } if (!this.isStepEnabled('expectations')) { alert('Please complete your Profile first before setting expectations.'); return; } console.log('? Navigating to user preferences...'); this.hideRoleSubnav(); // Close mobile nav if open if (this.showMobileNav) { this.showMobileNav = false; } this.router.navigate(['/userpreferences'], { queryParams: { user_id: userId } }).then(success => { console.log('? Navigation successful:', success); }).catch(error => { console.error('? Navigation failed:', error); }); } // UPDATED: navigateToAssessment method to show popup instead of direct navigation navigateToAssessment(role: string): void { const userId = this.authService.userId; if (!userId) { this.authModal = 'signin'; return; } if (!this.isStepEnabled('assessment')) { alert('Please complete Your Expectations first before taking the assessment.'); return; } // Close mobile nav if open if (this.showMobileNav) { this.showMobileNav = false; } // ? FIXED: Use injected LlmQaService const llmQaService = this.injector.get(LlmQaService); llmQaService.checkAssessmentCompletion(userId.toString()).subscribe({ next: (response: any) => { if (response.has_taken_assessment) { // User already took assessment, show popup instead of navigating console.log('?? User has already taken assessment, showing popup'); this.showAssessmentPopup = true; this.hideRoleSubnav(); } else { // User hasn't taken assessment, proceed to quiz this.hideRoleSubnav(); this.router.navigate(['/llmquiz'], { queryParams: { user_id: userId, show_welcome: 'true' } }); } }, error: (err: any) => { console.error('Error checking assessment completion:', err); // If check fails, allow navigation to quiz this.hideRoleSubnav(); this.router.navigate(['/llmquiz'], { queryParams: { user_id: userId, show_welcome: 'true' } }); } }); } navigateToMatchingProfile(role: string): void { const userId = this.authService.userId; if (!userId) { this.authModal = 'signin'; return; } if (!this.isStepEnabled('matching')) { alert('Please complete the Assessment first to view your matching profile.'); return; } this.hideRoleSubnav(); // Close mobile nav if open if (this.showMobileNav) { this.showMobileNav = false; } this.router.navigate(['/matchinglist'], { queryParams: { user_id: userId } }); } // NEW: Method to handle popup actions closeAssessmentPopup(): void { this.showAssessmentPopup = false; } navigateToMatchingFromPopup(): void { this.showAssessmentPopup = false; const userId = this.authService.userId; if (userId) { this.router.navigate(['/matchinglist'], { queryParams: { user_id: userId } }); } } goHomeFromPopup(): void { this.showAssessmentPopup = false; this.goHome(); } // Other navigation methods remain the same... navigateToJobRequirements(): void { const userId = this.authService.userId; if (!userId) { this.authModal = 'signin'; return; } this.hideRoleSubnav(); // Close mobile nav if open if (this.showMobileNav) { this.showMobileNav = false; } console.log('Navigate to job requirements'); this.router.navigate(['/job-requirements'], { queryParams: { user_id: userId } }); } navigateToInterviewAssessment(): void { const userId = this.authService.userId; if (!userId) { this.authModal = 'signin'; return; } this.hideRoleSubnav(); // Close mobile nav if open if (this.showMobileNav) { this.showMobileNav = false; } console.log('Navigate to interview assessment'); this.router.navigate(['/interview-assessment'], { queryParams: { user_id: userId } }); } navigateToCandidateMatching(): void { const userId = this.authService.userId; if (!userId) { this.authModal = 'signin'; return; } this.hideRoleSubnav(); // Close mobile nav if open if (this.showMobileNav) { this.showMobileNav = false; } console.log('Navigate to candidate matching'); this.router.navigate(['/candidate-matching'], { queryParams: { user_id: userId } }); } navigateToBusinessGoals(): void { const userId = this.authService.userId; if (!userId) { this.authModal = 'signin'; return; } this.hideRoleSubnav(); // Close mobile nav if open if (this.showMobileNav) { this.showMobileNav = false; } console.log('Navigate to business goals'); this.router.navigate(['/business-goals'], { queryParams: { user_id: userId } }); } navigateToPartnershipAssessment(): void { const userId = this.authService.userId; if (!userId) { this.authModal = 'signin'; return; } this.hideRoleSubnav(); // Close mobile nav if open if (this.showMobileNav) { this.showMobileNav = false; } console.log('Navigate to partnership assessment'); this.router.navigate(['/partnership-assessment'], { queryParams: { user_id: userId } }); } navigateToPartnerMatching(): void { const userId = this.authService.userId; if (!userId) { this.authModal = 'signin'; return; } this.hideRoleSubnav(); // Close mobile nav if open if (this.showMobileNav) { this.showMobileNav = false; } console.log('Navigate to partner matching'); this.router.navigate(['/partner-matching'], { queryParams: { user_id: userId } }); } // Legacy methods toggleMarriageSubnav(): void { this.showMarriageSubnav = !this.showMarriageSubnav; } hideMarriageSubnav(): void { this.showMarriageSubnav = false; } onSelectRole(role: string): void { const userId = this.authService.userId; if (!userId) { this.authModal = 'signin'; return; } if (role === 'marriage') { this.router.navigate(['/question-answer'], { queryParams: { role: 'marriage', user_id: userId } }); } else { this.router.navigate(['/question-answer'], { queryParams: { role, user_id: userId } }); } this.hideMarriageSubnav(); } // Auth methods onOpenSignIn(): void { this.authModal = 'signin'; // Close mobile nav if open if (this.showMobileNav) { this.showMobileNav = false; } } onOpenSignUp(): void { this.authModal = 'signup'; // Close mobile nav if open if (this.showMobileNav) { this.showMobileNav = false; } } closeAuthModal(evt?: MouseEvent): void { if (evt && (evt.target as HTMLElement).classList.contains('modal-backdrop')) { this.authModal = null; } else if (!evt) { this.authModal = null; } } // Replace the existing onSignOut method with this: onSignOut(): void { console.log('?? Saving progress before sign out...'); this.saveProgress(); // Close mobile nav if open if (this.showMobileNav) { this.showMobileNav = false; } // Show the sign-out popup first this.showSignOutPopup = true; this.hideMarriageSubnav(); this.hideRoleSubnav(); console.log('?? Showing sign out popup, will logout after animation'); console.log('?? Current progress state:', { profile: this.profileCompleted, expectations: this.expectationsCompleted, assessment: this.assessmentCompleted, matching: this.matchingCompleted }); // Wait for the tick animation to complete (4 seconds) before actually logging out setTimeout(() => { this.showSignOutPopup = false; // Now perform the actual logout this.authService.logout(); this.isSignedIn = false; this.userFullName = ''; this.userInitials = ''; this.changeDetectorRef.detectChanges(); console.log('? Sign out completed after animation'); }, 4000); this.changeDetectorRef.detectChanges(); } // UPDATED: Auth success handlers onSignInSuccess(): void { this.isSignedIn = true; this.authModal = null; const userId = this.authService.userId; if (userId) { this.loadUserData(userId); this.mergeProgressOnSignIn(userId); } this.router.navigate(['/']); } onSignUpSuccess(): void { // After a successful sign-up, prompt the user to sign in. // Navigate home then open the sign-in modal so the user can complete authentication. this.router.navigate(['/']).then(() => { this.authModal = 'signin'; // Ensure UI updates this.changeDetectorRef.detectChanges(); }); } // Legacy navigation methods navigateToProfileLegacy(): void { this.navigateToProfile('marriage'); } navigateToExpectationsLegacy(): void { this.navigateToExpectations('marriage'); } navigateToAssessmentLegacy(): void { this.navigateToAssessment('marriage'); } navigateToMatchingProfileLegacy(): void { this.navigateToMatchingProfile('marriage'); } navigateToLikedProfiles(): void { const userId = this.authService.userId; if (!userId) { this.authModal = 'signin'; return; } this.router.navigate(['/matchinglist'], { queryParams: { user_id: userId, view: 'liked' } }); } // Role selection selectRole(role: string): void { const userId = this.authService.userId; if (!userId) { this.authModal = 'signin'; return; } this.signupService.assignRole(userId, role).subscribe({ next: (res: any) => { console.log('Role saved:', res); this.router.navigate(['/question-answer'], { queryParams: { role, user_id: userId } }); }, error: (err: any) => { console.error('Role assign failed:', err); alert('Failed to assign role.'); } }); } // UPDATED: Chatbot methods to match IntroPageComponent functionality openChatbot(): void { // Close mobile nav if open if (this.showMobileNav) { this.showMobileNav = false; } this.showChatbot = true; document.body.style.overflow = 'hidden'; this.changeDetectorRef.detectChanges(); } closeChatbot(evt?: MouseEvent): void { if (evt && (evt.target as HTMLElement).classList.contains('modal-backdrop')) { this.showChatbot = false; } else if (!evt) { this.showChatbot = false; } if (!this.authModal) { document.body.style.overflow = ''; } this.changeDetectorRef.detectChanges(); } askQuickQuestion(question: string): void { console.log('Quick question asked:', question); this.addUserMessage(question); // Simulate bot response after delay setTimeout(() => { let response = ''; switch (question) { case 'How does the matching process work?': response = 'Our matching process uses adaptive assessment technology combined with established psychological frameworks. You\'ll complete a 15-20 minute assessment, and our system analyzes your responses to find compatible matches based on behavioral patterns and compatibility factors.'; break; case 'What personality models do you use?': response = 'We use several established psychological frameworks including the Big Five personality model (Openness, Conscientiousness, Extraversion, Agreeableness, Neuroticism), along with complementary models for specific contexts like marriage compatibility and workplace dynamics.'; break; case 'How do I reset my password?': response = 'To reset your password, click on the "Sign In" button, then select "Forgot Password" on the login screen. You\'ll receive an email with instructions to reset your password. If you need further assistance, contact our support team at support@pykara.com.'; break; case 'Contact support team': response = 'You can reach our human support team by emailing info@pykara.ai or calling +91 99941 90964. Our support hours are Monday-Friday, 9 AM - 6 PM EST. For urgent matters, please include "URGENT" in your email subject line.'; break; default: response = 'Thank you for your question. Our support team will get back to you shortly. For immediate assistance, please contact us at support@pykara.com'; } this.addBotMessage(response); }, 1000); } sendMessage(chatInput: any): void { const message = chatInput.value.trim(); if (message) { console.log('Message sent:', message); this.addUserMessage(message); setTimeout(() => { this.addBotMessage('Thank you for your message. Our support team will review your question and get back to you within 24 hours. For immediate assistance, please contact us at info@pykara.ai or call +91 99941 90964.'); }, 1000); chatInput.value = ''; } } private addUserMessage(message: string): void { const chatMessages = document.querySelector('.chat-messages'); if (chatMessages) { const userMessage = document.createElement('div'); userMessage.className = 'message user-message'; userMessage.innerHTML = `
`; chatMessages.appendChild(userMessage); this.scrollToBottom(); } } private addBotMessage(message: string): void { const chatMessages = document.querySelector('.chat-messages'); if (chatMessages) { const botMessage = document.createElement('div'); botMessage.className = 'message bot-message'; botMessage.innerHTML = ` `; chatMessages.appendChild(botMessage); this.scrollToBottom(); } } private scrollToBottom(): void { setTimeout(() => { const chatMessages = document.querySelector('.chat-messages'); if (chatMessages) { chatMessages.scrollTop = chatMessages.scrollHeight; } }, 100); } // CORRECTED: Single onEsc method that handles all cases // Update onEsc method to close mobile nav @HostListener('document:keydown.escape') onEsc(): void { if (this.showSignOutPopup) { return; } if (this.showMobileNav) { this.showMobileNav = false; this.activeRoleMenuMobile = null; } else if (this.showChatbot) { this.closeChatbot(); } else if (this.authModal) { this.closeAuthModal(); } else if (this.showAssessmentPopup) { this.showAssessmentPopup = false; } } // Add click outside to close mobile nav @HostListener('document:click', ['$event']) onClickOutside(event: MouseEvent): void { const target = event.target as HTMLElement; if (this.showMobileNav && !target.closest('.mobile-nav-overlay') && !target.closest('.mobile-menu-toggle') && !target.closest('.mobile-nav-link')) { this.showMobileNav = false; this.activeRoleMenuMobile = null; } } // Method to manually update progress updateProgress(step: string, completed: boolean): void { console.log(`?? Updating progress: ${step} = ${completed}`); switch (step) { case 'profile': this.profileCompleted = completed; break; case 'expectations': this.expectationsCompleted = completed; break; case 'assessment': this.assessmentCompleted = completed; break; case 'matching': this.matchingCompleted = completed; break; } this.saveProgress(); this.changeDetectorRef.detectChanges(); } // Add this method to trigger progress updates from other components markStepCompleted(step: string): void { console.log(`?? Marking step as completed: ${step}`); this.updateProgress(step, true); this.saveProgress(); this.changeDetectorRef.detectChanges(); const event = new CustomEvent(`${step}Completed`, { detail: { timestamp: Date.now() } }); window.dispatchEvent(event); console.log(`? Step ${step} marked as completed`); } // Add a method to force progress refresh refreshProgress(): void { this.syncProgressFromStorage(); this.changeDetectorRef.detectChanges(); } // Debug method to check current state debugProgress(): void { console.log('?? DEBUG - Current Progress State:', { profileCompleted: this.profileCompleted, expectationsCompleted: this.expectationsCompleted, assessmentCompleted: this.assessmentCompleted, matchingCompleted: this.matchingCompleted, userId: this.authService.userId, localStorage: localStorage.getItem(`user_progress_${this.authService.userId}`) }); } // NEW: Load progress from any available source (user-specific or general) private loadProgressFromAnySource(): void { const currentUserId = this.authService.userId; if (currentUserId) { this.syncProgressFromStorage(); } else { const genericProgress = localStorage.getItem('anonymous_progress'); if (genericProgress) { try { const progressData = JSON.parse(genericProgress); this.profileCompleted = progressData.profileCompleted || false; this.expectationsCompleted = progressData.expectationsCompleted || false; this.assessmentCompleted = progressData.assessmentCompleted || false; this.matchingCompleted = progressData.matchingCompleted || false; console.log('?? Loaded anonymous progress from localStorage:', progressData); } catch (e) { console.error('Error parsing anonymous progress data:', e); } } else { this.tryToLoadProgressFromAnyUser(); } } } // NEW: Merge anonymous progress with user progress when signing in private mergeProgressOnSignIn(userId: number): void { const anonymousProgress = localStorage.getItem('anonymous_progress'); let anonymousData: any = {}; if (anonymousProgress) { try { anonymousData = JSON.parse(anonymousProgress); console.log('?? Found anonymous progress to merge:', anonymousData); } catch (e) { console.error('Error parsing anonymous progress:', e); } } const userProgress = localStorage.getItem(`user_progress_${userId}`); let userData: any = {}; if (userProgress) { try { userData = JSON.parse(userProgress); console.log('?? Found existing user progress:', userData); } catch (e) { console.error('Error parsing user progress:', e); } } const mergedProgress = { profileCompleted: userData.profileCompleted || anonymousData.profileCompleted || false, expectationsCompleted: userData.expectationsCompleted || anonymousData.expectationsCompleted || false, assessmentCompleted: userData.assessmentCompleted || anonymousData.assessmentCompleted || false, matchingCompleted: userData.matchingCompleted || anonymousData.matchingCompleted || false, lastUpdated: new Date().toISOString() }; localStorage.setItem(`user_progress_${userId}`, JSON.stringify(mergedProgress)); this.profileCompleted = mergedProgress.profileCompleted; this.expectationsCompleted = mergedProgress.expectationsCompleted; this.assessmentCompleted = mergedProgress.assessmentCompleted; this.matchingCompleted = mergedProgress.matchingCompleted; localStorage.removeItem('anonymous_progress'); console.log('? Progress merged after sign in:', mergedProgress); this.changeDetectorRef.detectChanges(); } private toggleBodyScroll(): void { const url = (this.router.url || '').split('?')[0]; if (url === '/' || url.startsWith('/intro')) { document.body.classList.add('body-scrollable'); } else { document.body.classList.remove('body-scrollable'); } } }