py-match / src /app /user-preferences /user-preferences.component.ts
Swetha
fix
d1ee381
import { Component, OnInit, Renderer2, ElementRef, HostListener, ChangeDetectorRef } from '@angular/core';
import { FormBuilder, FormGroup, FormArray, FormControl, ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { Router, ActivatedRoute } from '@angular/router';
import { UserPreferencesService, ExpectationQuestion } from './user-preferences.service';
import { AuthService } from '../services/auth.service';
import { FormsModule } from '@angular/forms'; // Add this import
// Add Truncate Pipe
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'truncate',
standalone: true
})
export class TruncatePipe implements PipeTransform {
transform(value: string, limit: number = 100, trail: string = '...'): string {
if (!value) return '';
return value.length > limit ? value.substring(0, limit) + trail : value;
}
}
@Component({
selector: 'app-user-preferences',
standalone: true,
imports: [CommonModule, ReactiveFormsModule, TruncatePipe, FormsModule], // Add FormsModule here
templateUrl: './user-preferences.component.html',
styleUrls: ['./user-preferences.component.css']
})
export class UserPreferencesComponent implements OnInit {
questions: ExpectationQuestion[] = [];
form!: FormGroup;
user_id = 1;
role: string | null = null;
isLoading = true;
showModal = false;
progress = 0;
savedSuccessfully = false;
selectedReviewCategory: string = '';
// Mandatory fields tracking
mandatoryFields: { [key: string]: boolean } = {};
// Tutorial modal for mandatory fields
showTutorialModal = false;
// Property to track open dropdowns
openDropdowns: { [key: string]: boolean } = {};
// Text input popup properties
showTextInputPopup: boolean = false;
popupQuestion: ExpectationQuestion | null = null;
popupTextValue: string = '';
isEditingExistingPreferences = false;
existingPreferencesData: any = null;
categoriesMeta = [
{ key: 'personal_education', title: 'Personal & Education' },
{ key: 'family_values', title: 'Family & Values' },
{ key: 'lifestyle', title: 'Lifestyle & Habits' },
{ key: 'finances_dealbreakers', title: 'Finances & Deal Breakers' },
{ key: 'review', title: 'Review' }
];
selectedCategory: string = this.categoriesMeta[0]?.key || '';
currentTabIndex: number = 0;
constructor(
private service: UserPreferencesService,
private fb: FormBuilder,
private router: Router,
private route: ActivatedRoute,
private renderer: Renderer2,
private el: ElementRef,
private authService: AuthService,
private cdRef: ChangeDetectorRef
) { }
// Add the missing ngOnInit method
ngOnInit(): void {
this.route.queryParamMap.subscribe(params => {
const qpUser = params.get('user_id');
const qpRole = params.get('role');
if (qpUser) {
this.user_id = Number(qpUser);
} else {
this.user_id = this.authService.userId || 1;
}
this.role = qpRole;
console.log('πŸ” UserPreferences initialized:', {
user_id: this.user_id,
role: this.role
});
// Set default review category
const nonReviewCategories = this.categoriesMeta.filter(cat => cat.key !== 'review');
if (nonReviewCategories.length > 0) {
this.selectedReviewCategory = nonReviewCategories[0].key;
}
// Debug check for mandatory fields
console.log('πŸ” DEBUG: Checking current mandatory fields in database for user:', this.user_id);
this.service.checkMandatoryFields(this.user_id).subscribe({
next: (result) => {
console.log('πŸ” CURRENT MANDATORY FIELDS IN DB:', result);
},
error: (err) => {
console.error('❌ Failed to check mandatory fields:', err);
}
});
this.loadQuestionsAndPreferences();
});
}
private enhanceUserExperience(): void {
setTimeout(() => {
const formContainer = document.querySelector('.form-container');
if (formContainer) {
formContainer.scrollTop = 0;
}
}, 150);
}
getProgressBarWidth(): number {
if (this.selectedCategory === 'review') {
return 95;
}
const currentIndex = this.categoriesMeta.findIndex(cat => cat.key === this.selectedCategory);
if (currentIndex >= 0) {
return ((currentIndex + 1) / (this.categoriesMeta.length + 1)) * 100;
}
return 0;
}
getStepLabel(title: string): string {
const words = title.split(' ');
return words.length > 2 ? words[0] : title.split(' ')[0];
}
@HostListener('window:resize')
onWindowResize() {
// Trigger responsive behavior
}
// Text Input Popup Methods
isAdditionalRemarksField(question?: ExpectationQuestion): boolean {
if (question) {
// Check if this is specifically the "Summary of Expectations" or similar fields
const label = question.question?.toLowerCase();
const columnKey = question.column_key?.toLowerCase();
// Define which fields should use the popup
const popupFields = [
'summary of expectations',
'expectation summary',
'additional remarks',
'remark',
'remarks',
'summary',
'description'
];
return popupFields.some(field =>
label?.includes(field) || columnKey?.includes(field)
);
}
// For the popup overlay check (when no question parameter)
const currentLabel = this.popupQuestion?.question?.toLowerCase();
const currentColumnKey = this.popupQuestion?.column_key?.toLowerCase();
const popupFields = [
'summary of expectations',
'expectation summary',
'additional remarks',
'remark',
'remarks',
'summary',
'description'
];
return popupFields.some(field =>
currentLabel?.includes(field) || currentColumnKey?.includes(field)
);
}
openTextInputPopup(question: ExpectationQuestion): void {
// Only open popup for specific fields (like Summary of Expectations)
if (this.isAdditionalRemarksField(question)) {
this.popupQuestion = question;
this.popupTextValue = this.getFormValue(question.column_key) || '';
this.showTextInputPopup = true;
// Initialize textarea height after view updates
setTimeout(() => {
this.initializeTextareaHeight();
});
}
// For other fields, do nothing (no popup will open)
}
closeTextInputPopup(): void {
this.showTextInputPopup = false;
this.popupQuestion = null;
this.popupTextValue = '';
}
saveTextInputPopup(): void {
if (this.popupQuestion) {
const control = this.form.get(this.popupQuestion.column_key);
if (control) {
control.setValue(this.popupTextValue);
}
this.onInputChange(this.popupQuestion.column_key);
}
this.closeTextInputPopup();
}
// Auto-expanding textarea functionality for popup only
onTextareaInput(event: Event): void {
const textarea = event.target as HTMLTextAreaElement;
this.adjustTextareaHeight(textarea);
}
initializeTextareaHeight(): void {
const textarea = document.querySelector('.text-input-popup-field') as HTMLTextAreaElement;
if (textarea) {
this.adjustTextareaHeight(textarea);
textarea.focus();
// Set cursor to end of text
const length = textarea.value.length;
textarea.setSelectionRange(length, length);
}
}
adjustTextareaHeight(textarea: HTMLTextAreaElement): void {
// Reset height to auto to get the correct scrollHeight
textarea.style.height = 'auto';
// Set the height to scrollHeight to expand, with max limit
const maxHeight = 400;
const newHeight = Math.min(textarea.scrollHeight, maxHeight);
textarea.style.height = newHeight + 'px';
// Enable scrolling only when content exceeds max height
textarea.style.overflowY = textarea.scrollHeight > maxHeight ? 'auto' : 'hidden';
}
// Mandatory Fields Methods
isMandatory(columnKey: string): boolean {
return this.mandatoryFields[columnKey] === true;
}
onMandatoryChange(columnKey: string, value: string): void {
this.mandatoryFields[columnKey] = value === 'mandatory';
console.log(`🎯 Mandatory changed for ${columnKey}:`, this.mandatoryFields[columnKey]);
this.updateProgress();
this.cdRef.detectChanges();
}
getMandatoryFieldsCount(): number {
const count = Object.values(this.mandatoryFields).filter(isMandatory => isMandatory).length;
console.log(`πŸ“Š Mandatory fields count: ${count}`);
return count;
}
// In the UserPreferencesComponent class
openTutorialModal(): void {
this.showTutorialModal = true;
// Mark as seen when opened (optional)
localStorage.setItem('mandatory_tutorial_seen', 'true');
this.cdRef.detectChanges();
console.log('πŸ“š Opening mandatory fields tutorial');
}
closeTutorialModal(): void {
this.showTutorialModal = false;
this.cdRef.detectChanges();
}
// Event Handler Methods
onTextInputChange(event: any, columnKey: string): void {
const value = event.target.value;
this.form.get(columnKey)?.setValue(value);
this.updateProgress();
this.cdRef.detectChanges();
}
onSelectChange(event: any, columnKey: string): void {
const value = event.target.value;
this.form.get(columnKey)?.setValue(value);
this.updateProgress();
this.cdRef.detectChanges();
}
onRadioChange(event: any, columnKey: string): void {
const value = event.target.value;
this.form.get(columnKey)?.setValue(value);
this.updateProgress();
this.cdRef.detectChanges();
}
onCheckboxSingleChange(event: any, columnKey: string): void {
const value = event.target.checked;
this.form.get(columnKey)?.setValue(value);
this.updateProgress();
this.cdRef.detectChanges();
}
// Multi-select checkbox handler (used in both original and dropdown)
onCheckboxChange(event: any, columnKey: string): void {
const formArray: FormArray = this.form.get(columnKey) as FormArray;
const value = event.target.value;
const isChecked = event.target.checked;
if (isChecked) {
if (!formArray.value.includes(value)) {
formArray.push(new FormControl(value));
}
} else {
const index = formArray.controls.findIndex(x => x.value === value);
if (index >= 0) {
formArray.removeAt(index);
}
}
this.updateProgress();
this.cdRef.detectChanges();
}
// Dropdown multi-select specific methods
toggleDropdown(columnKey: string): void {
this.openDropdowns[columnKey] = !this.openDropdowns[columnKey];
}
isDropdownOpen(columnKey: string): boolean {
return this.openDropdowns[columnKey] === true;
}
getSelectedOptionsText(columnKey: string): string {
const formArray = this.form.get(columnKey) as FormArray;
if (!formArray || formArray.value.length === 0) {
return '';
}
const selectedValues = formArray.value;
if (selectedValues.length === 1) {
return selectedValues[0];
}
return `${selectedValues.length} options selected`;
}
// Close dropdown when clicking outside
@HostListener('document:click', ['$event'])
onDocumentClick(event: MouseEvent): void {
const target = event.target as HTMLElement;
if (!target.closest('.multiselect-dropdown-checkbox')) {
this.openDropdowns = {};
this.cdRef.detectChanges();
}
}
// Check if all options are selected
areAllOptionsSelected(question: ExpectationQuestion): boolean {
const formArray = this.form.get(question.column_key) as FormArray;
const options = this.getOptions(question);
return formArray && options.length > 0 && formArray.value.length === options.length;
}
// Toggle select all
toggleSelectAll(question: ExpectationQuestion): void {
const formArray: FormArray = this.form.get(question.column_key) as FormArray;
const options = this.getOptions(question);
formArray.clear();
if (!this.areAllOptionsSelected(question)) {
options.forEach(option => {
formArray.push(new FormControl(option));
});
}
this.updateProgress();
this.cdRef.detectChanges();
}
isRadioSelected(columnKey: string, option: any): boolean {
const value = this.getFormValue(columnKey);
return value === option;
}
isCheckboxSelected(columnKey: string, option: any): boolean {
const formArray = this.form.get(columnKey) as FormArray;
return formArray && formArray.value.includes(option);
}
getFormValue(columnKey: string): any {
const control = this.form?.get(columnKey);
if (!control) return '';
if (control instanceof FormArray) {
return control.value || [];
}
return control.value || '';
}
private cleanValue(value: string): string {
if (!value) return value;
let cleaned = value
.replace(/^\["+|"+]$/g, '')
.replace(/^"+|"+$/g, '')
.replace(/\\"/g, '"')
.trim();
if (cleaned.startsWith('[') || cleaned.startsWith('{')) {
try {
const parsed = JSON.parse(cleaned);
if (Array.isArray(parsed) && parsed.length > 0) {
return parsed.map(item => this.cleanValue(String(item))).join(', ');
}
return String(parsed);
} catch (e) {
// If parsing fails, return the cleaned version
}
}
return cleaned;
}
private loadQuestionsAndPreferences(): void {
this.isLoading = true;
console.log('πŸ“₯ Loading questions and preferences...');
this.service.getQuestions().subscribe({
next: (data) => {
this.questions = data;
console.log('βœ… Total questions loaded:', this.questions.length);
this.buildForm();
this.checkExistingPreferences();
},
error: (error) => {
console.error('❌ Error loading questions:', error);
this.isLoading = false;
this.cdRef.detectChanges();
}
});
}
private checkExistingPreferences(): void {
console.log('πŸ” Checking for existing preferences...');
this.service.getExistingPreferences(this.user_id).subscribe({
next: (existingData: any) => {
if (existingData && Object.keys(existingData).length > 0) {
this.isEditingExistingPreferences = true;
this.existingPreferencesData = existingData;
console.log('πŸ“ Loading existing preferences data:', existingData);
this.populateFormWithExistingData();
} else {
console.log('πŸ“ No existing preferences found, creating new ones');
}
this.finalizeInitialization();
},
error: (err) => {
console.log('πŸ“ No existing preferences found or error checking:', err);
this.finalizeInitialization();
}
});
}
private finalizeInitialization(): void {
this.setInitialSelectedCategory();
this.updateProgress();
this.isLoading = false;
this.debugCategoriesAndQuestions();
this.cdRef.detectChanges();
// Auto-show tutorial on first load
this.showMandatoryTutorial();
setTimeout(() => {
this.enhanceUserExperience();
}, 100);
console.log('βœ… UserPreferences component initialized');
}
private showMandatoryTutorial(): void {
// Check if user has seen the tutorial before
const tutorialSeen = localStorage.getItem('mandatory_tutorial_seen');
if (!tutorialSeen) {
// Show tutorial on first load with a slight delay
setTimeout(() => {
this.showTutorialModal = true;
// Mark as seen when shown automatically
localStorage.setItem('mandatory_tutorial_seen', 'true');
this.cdRef.detectChanges();
console.log('πŸ“š Auto-showing mandatory fields tutorial');
}, 800); // Slightly longer delay for better UX
}
}
private buildForm(): void {
const group: any = {};
this.questions.forEach(q => {
if (q.input_type === 'multi_select') {
group[q.column_key] = this.fb.array([]);
} else {
const existingValue = this.existingPreferencesData ? this.existingPreferencesData[q.column_key] : '';
let initialValue: any = '';
if (q.input_type === 'checkbox') {
initialValue = existingValue === true || existingValue === 'true' || existingValue === '1' || existingValue === 1;
} else if (q.input_type === 'radio' || q.input_type === 'select') {
if (Array.isArray(existingValue)) {
initialValue = existingValue[0] || '';
} else if (typeof existingValue === 'string') {
initialValue = this.cleanValue(existingValue);
} else {
initialValue = existingValue || '';
}
} else {
initialValue = existingValue || '';
}
group[q.column_key] = new FormControl(initialValue);
}
this.mandatoryFields[q.column_key] = false;
});
this.form = this.fb.group(group);
console.log('βœ… Form built with questions:', this.questions.length);
console.log('πŸ“‹ Form controls:', Object.keys(this.form.controls));
this.questions.forEach(q => {
const control = this.form.get(q.column_key);
console.log(`πŸ” Control for ${q.column_key}:`, control?.constructor.name, 'Type:', q.input_type);
});
this.updateProgress();
this.form.valueChanges.subscribe(() => {
this.updateProgress();
this.cdRef.detectChanges();
});
console.log('βœ… Form built with', this.questions.length, 'questions');
}
private populateFormWithExistingData(): void {
console.log('πŸ”„ Populating form with existing data...');
let existingMandatoryFields: { [key: string]: any } = {};
if (this.existingPreferencesData['_mandatory_fields']) {
try {
if (typeof this.existingPreferencesData['_mandatory_fields'] === 'string') {
existingMandatoryFields = JSON.parse(this.existingPreferencesData['_mandatory_fields']);
} else {
existingMandatoryFields = this.existingPreferencesData['_mandatory_fields'];
}
console.log('🎯 Parsed mandatory fields:', existingMandatoryFields);
} catch (e) {
console.error('❌ Error parsing mandatory fields:', e);
}
}
this.questions.forEach(q => {
const existingValue = this.existingPreferencesData[q.column_key];
const existingMandatory = existingMandatoryFields[q.column_key];
console.log(`πŸ“ Processing ${q.column_key}:`, {
existingValue,
existingMandatory,
inputType: q.input_type
});
if (q.input_type === 'multi_select') {
const formControl = this.form.get(q.column_key);
if (formControl && formControl instanceof FormArray) {
const formArray: FormArray = formControl;
let values: string[] = [];
if (existingValue) {
if (typeof existingValue === 'string') {
const cleanValue = existingValue.replace(/[\[\]"]/g, '');
values = cleanValue.split(',').map(item => item.trim()).filter(item => item.length > 0);
} else if (Array.isArray(existingValue)) {
values = existingValue.filter(item => item && item.length > 0);
}
}
console.log(`πŸ“‹ Multi-select values for ${q.column_key}:`, values);
formArray.clear();
values.forEach(value => {
formArray.push(new FormControl(value));
});
} else {
console.warn(`⚠️ FormArray not found for multi-select question: ${q.column_key}`);
}
} else {
const finalValue = existingValue || '';
console.log(`πŸ“‹ Setting ${q.column_key} to:`, finalValue);
const formControl = this.form.get(q.column_key);
if (formControl) {
formControl.setValue(finalValue);
} else {
console.warn(`⚠️ FormControl not found for question: ${q.column_key}`);
}
}
if (existingMandatory !== undefined && existingMandatory !== null) {
this.mandatoryFields[q.column_key] = existingMandatory === true || existingMandatory === 'mandatory' || existingMandatory === 'true';
console.log(`🎯 Mandatory for ${q.column_key}:`, this.mandatoryFields[q.column_key]);
} else {
this.mandatoryFields[q.column_key] = false;
}
});
console.log('βœ… Form populated with existing data');
console.log('πŸ“‹ Current form values:', this.form.value);
console.log('πŸ“‹ Current mandatory fields:', this.mandatoryFields);
setTimeout(() => {
this.updateProgress();
});
}
getOptions(q: ExpectationQuestion): string[] {
const opts = q.options;
if (Array.isArray(opts)) return (opts as string[]).filter(Boolean);
if (typeof opts === 'string') return opts.split(',').map(s => s.trim()).filter(Boolean);
return [];
}
getQuestionsByCategory(category: string): ExpectationQuestion[] {
const categoryMappings: { [key: string]: string[] } = {
'personal_education': ['demographics', 'educationcareer', 'education', 'career', 'personal'],
'family_values': ['family', 'values', 'family_values', 'family&values'],
'finances_dealbreakers': ['finances', 'dealbreakers', 'financial', 'deal_breakers', 'finances_dealbreakers'],
'lifestyle': ['lifestyle', 'habits', 'lifestyle_habits'],
'interests': ['interests', 'hobbies', 'social', 'interests_social']
};
const targetCategories = categoryMappings[category] || [category];
const filteredQuestions = this.questions.filter(q => {
if (!q.category) return false;
const qCategory = q.category.toLowerCase().trim();
return targetCategories.some(target =>
qCategory.includes(target.toLowerCase()) ||
target.toLowerCase().includes(qCategory)
);
});
return filteredQuestions;
}
private debugCategoriesAndQuestions(): void {
console.log('=== DEBUG: Available Categories and Questions ===');
const allCategories = [...new Set(this.questions.map(q => q.category))];
console.log('All categories in questions:', allCategories);
this.categoriesMeta.forEach(cat => {
if (cat.key !== 'review') {
const questions = this.getQuestionsByCategory(cat.key);
console.log(`Category ${cat.key} (${cat.title}): ${questions.length} questions`);
questions.forEach(q => {
console.log(` - ${q.column_key}: ${q.question}`);
});
}
});
console.log('=== END DEBUG ===');
}
selectCategory(key: string): void {
this.selectedCategory = key;
const categoryIndex = this.categoriesMeta.findIndex(cat => cat.key === key);
this.currentTabIndex = categoryIndex >= 0 ? categoryIndex : this.categoriesMeta.length;
// Initialize selected review category when entering review tab
if (key === 'review') {
const nonReviewCategories = this.categoriesMeta.filter(cat => cat.key !== 'review');
if (nonReviewCategories.length > 0) {
this.selectedReviewCategory = nonReviewCategories[0].key;
}
}
if (key === 'review') {
setTimeout(() => {
this.debugReviewData();
this.debugFormValues();
}, 100);
}
this.enhanceUserExperience();
this.cdRef.detectChanges();
}
selectReviewCategory(categoryKey: string): void {
this.selectedReviewCategory = categoryKey;
}
getReviewCategoryTitle(categoryKey: string): string {
const category = this.categoriesMeta.find(c => c.key === categoryKey);
return category?.title || categoryKey;
}
private setInitialSelectedCategory(): void {
const firstWithQuestions = this.categoriesMeta.find(c => c.key !== 'review' && this.getQuestionsByCategory(c.key).length > 0);
if (firstWithQuestions) {
this.selectedCategory = firstWithQuestions.key;
this.currentTabIndex = this.categoriesMeta.findIndex(c => c.key === firstWithQuestions.key);
}
}
getCategoryAnsweredCount(key: string): number {
const qs = this.getQuestionsByCategory(key);
return qs.reduce((acc, q) => {
return acc + (this.isAnswered(q) ? 1 : 0);
}, 0);
}
getCategoryPercent(key: string): number {
const total = this.getQuestionsByCategory(key).length || 1;
const answered = this.getCategoryAnsweredCount(key);
return Math.round((answered / total) * 100);
}
getCategoryIcon(categoryKey: string): string {
const iconMap: { [key: string]: string } = {
'personal_education': 'fa-user-graduate',
'family_values': 'fa-people-roof',
'lifestyle': 'fa-heart',
'finances_dealbreakers': 'fa-sack-dollar',
'interests': 'fa-bullseye',
'review': 'fa-check'
};
return iconMap[categoryKey] || 'fa-question';
}
getCurrentCategoryIcon(): string {
return this.getCategoryIcon(this.selectedCategory);
}
getCurrentCategoryTitle(): string {
const category = this.categoriesMeta.find(c => c.key === this.selectedCategory);
return category?.title || 'Category';
}
getCurrentCategoryDescription(): string {
const descriptions: Record<string, string> = {
'personal_education': 'Share your background, education, and career details',
'family_values': 'Tell us about your family expectations and core values',
'lifestyle': 'Describe your daily habits and lifestyle preferences',
'finances_dealbreakers': 'Set your financial expectations and deal breakers',
'interests': 'Share your hobbies, interests, and social preferences',
'review': 'Review all the information you have provided'
};
return descriptions[this.selectedCategory] || 'Complete this section to improve your matches';
}
onInputChange(columnKey: string): void {
this.updateProgress();
this.cdRef.detectChanges();
}
updateProgress(): void {
const total = Object.keys(this.form.controls).length || 1;
const filled = Object.values(this.form.value).filter(v => {
if (Array.isArray(v)) return v.length > 0;
return !!v && v.toString().trim().length > 0;
}).length;
this.progress = Math.round((filled / total) * 100);
}
submit(): void {
console.log('πŸš€ SUBMITTING USER PREFERENCES FORM...');
if (this.form.valid) {
this.savedSuccessfully = false;
this.showModal = true;
this.isLoading = true;
const payload: any = {
user_id: this.user_id,
...this.form.value,
_mandatory_fields: this.mandatoryFields
};
console.log('🎯 Mandatory fields to save:', this.mandatoryFields);
console.log('πŸ“Š Mandatory fields count:', this.getMandatoryFieldsCount());
console.log('πŸ“€ COMPLETE PAYLOAD:', JSON.stringify(payload, null, 2));
const submitObservable = this.isEditingExistingPreferences
? this.service.updatePreferences(this.user_id, payload)
: this.service.saveResponse(payload);
submitObservable.subscribe({
next: (response) => {
console.log('βœ… Successfully saved preferences with mandatory fields:', response);
this.savedSuccessfully = true;
this.isLoading = false;
},
error: (err) => {
console.error('❌ Error saving preferences:', err);
this.savedSuccessfully = false;
this.isLoading = false;
}
});
} else {
console.error('❌ Form is invalid!');
console.log('Form validation errors:', this.form.errors);
console.log('Form controls status:', this.form.status);
}
}
closeModal(): void {
this.showModal = false;
// Remove blur effects if any
const container = document.querySelector('.container');
if (container) {
container.classList.remove('blur-background');
}
}
redirectToHome(): void {
this.showModal = false;
this.router.navigate(['/']);
}
redirectToAssessment(): void {
this.showModal = false;
this.router.navigate(['/llmquiz'], {
queryParams: { user_id: this.user_id }
});
}
goToPreviousCategory(currentKey: string): void {
const currentIndex = this.categoriesMeta.findIndex(cat => cat.key === currentKey);
if (currentIndex > 0) {
this.selectedCategory = this.categoriesMeta[currentIndex - 1].key;
this.currentTabIndex = currentIndex - 1;
this.enhanceUserExperience();
this.cdRef.detectChanges();
}
}
goToNextCategory(currentKey: string): void {
const currentIndex = this.categoriesMeta.findIndex(cat => cat.key === currentKey);
if (currentIndex < this.categoriesMeta.length - 1) {
this.selectedCategory = this.categoriesMeta[currentIndex + 1].key;
this.currentTabIndex = currentIndex + 1;
} else {
this.selectedCategory = 'review';
this.currentTabIndex = this.categoriesMeta.length;
// Ensure review category is set when navigating to review
const nonReviewCategories = this.categoriesMeta.filter(cat => cat.key !== 'review');
if (nonReviewCategories.length > 0) {
this.selectedReviewCategory = nonReviewCategories[0].key;
}
}
if (this.selectedCategory === 'review') {
setTimeout(() => {
this.cdRef.detectChanges();
}, 50);
}
this.enhanceUserExperience();
this.cdRef.detectChanges();
}
trackQuestion(index: number, item: ExpectationQuestion): string {
return item.column_key;
}
// REVIEW TAB METHODS
get allFieldsAnswered(): boolean {
return this.questions.length > 0 && this.questions.every(q => this.isAnswered(q));
}
get unansweredCount(): number {
return this.questions.filter(q => !this.isAnswered(q)).length;
}
isAnswered(q: ExpectationQuestion): boolean {
const control = this.getFormControl(q.column_key);
if (!control) return false;
const value = control.value;
if (q.input_type === 'multi_select') {
return Array.isArray(value) && value.length > 0;
}
if (q.input_type === 'checkbox') {
return value === true || value === 'true' || value === '1' || value === 1;
}
return !!value && value.toString().trim().length > 0;
}
getAnswerDisplay(question: ExpectationQuestion): string {
const control = this.getFormControl(question.column_key);
if (!control) return '(Not answered)';
let value = control.value;
if (typeof value === 'string') {
value = this.cleanValue(value);
}
if (value === null || value === undefined || value === '') {
return '(Not answered)';
}
// For popup fields, show truncated preview
if (this.isAdditionalRemarksField(question) && typeof value === 'string' && value.length > 100) {
return value.substring(0, 100) + '...';
}
if (question.input_type === 'multi_select') {
if (Array.isArray(value)) {
const cleanedValues = value.map(v => {
if (typeof v === 'string') {
return this.cleanValue(v);
}
return v;
});
return cleanedValues.length > 0 ? cleanedValues.join(', ') : '(Not answered)';
}
return this.cleanValue(String(value)) || '(Not answered)';
}
if (question.input_type === 'checkbox') {
if (typeof value === 'boolean') {
return value ? 'Yes' : 'No';
}
if (typeof value === 'string') {
return value.toLowerCase() === 'true' || value === '1' ? 'Yes' : 'No';
}
if (typeof value === 'number') {
return value === 1 ? 'Yes' : 'No';
}
return value ? 'Yes' : 'No';
}
if (question.input_type === 'select' || question.input_type === 'radio') {
if (Array.isArray(value)) {
const firstValue = value[0];
return this.cleanValue(String(firstValue)) || '(Not answered)';
}
return this.cleanValue(String(value)) || '(Not answered)';
}
if (Array.isArray(value)) {
const cleanedValues = value.map(v => this.cleanValue(String(v)));
return cleanedValues.length > 0 ? cleanedValues.join(', ') : '(Not answered)';
}
if (typeof value === 'boolean') {
return value ? 'Yes' : 'No';
}
return this.cleanValue(String(value)) || '(Not answered)';
}
getFormControl(columnKey: string): FormControl | FormArray | null {
return this.form?.get(columnKey) as FormControl | FormArray | null;
}
// DEBUG METHODS
debugFormValues(): void {
console.log('=== USER PREFERENCES FORM VALUES DEBUG ===');
this.questions.forEach(q => {
const value = this.getFormControl(q.column_key)?.value;
const answered = this.isAnswered(q);
console.log(`${q.column_key}:`, value, `(Type: ${q.input_type}, Answered: ${answered})`);
});
console.log('=== END FORM VALUES DEBUG ===');
}
debugReviewData(): void {
console.log('=== USER PREFERENCES REVIEW TAB DEBUG ===');
console.log('Form valid:', this.form.valid);
console.log('Form values:', this.form.value);
console.log('All fields answered:', this.allFieldsAnswered);
console.log('Unanswered count:', this.unansweredCount);
console.log('Mandatory fields count:', this.getMandatoryFieldsCount());
this.categoriesMeta.forEach(cat => {
if (cat.key !== 'review') {
const questions = this.getQuestionsByCategory(cat.key);
console.log(`Category: ${cat.title} (${cat.key})`);
questions.forEach(q => {
const value = this.getFormControl(q.column_key)?.value;
const answered = this.isAnswered(q);
const display = this.getAnswerDisplay(q);
const mandatory = this.isMandatory(q.column_key);
console.log(` ${q.column_key}:`, value, `Answered: ${answered}, Mandatory: ${mandatory}, Display: "${display}"`);
});
}
});
console.log('=== END REVIEW DEBUG ===');
}
getReviewCategories(): any[] {
return this.categoriesMeta.filter(cat => cat.key !== 'review');
}
}