|
|
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';
|
|
|
|
|
|
|
|
|
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],
|
|
|
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 = '';
|
|
|
|
|
|
|
|
|
mandatoryFields: { [key: string]: boolean } = {};
|
|
|
|
|
|
|
|
|
showTutorialModal = false;
|
|
|
|
|
|
|
|
|
openDropdowns: { [key: string]: boolean } = {};
|
|
|
|
|
|
|
|
|
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
|
|
|
) { }
|
|
|
|
|
|
|
|
|
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
|
|
|
});
|
|
|
|
|
|
|
|
|
const nonReviewCategories = this.categoriesMeta.filter(cat => cat.key !== 'review');
|
|
|
if (nonReviewCategories.length > 0) {
|
|
|
this.selectedReviewCategory = nonReviewCategories[0].key;
|
|
|
}
|
|
|
|
|
|
|
|
|
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() {
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
isAdditionalRemarksField(question?: ExpectationQuestion): boolean {
|
|
|
if (question) {
|
|
|
|
|
|
const label = question.question?.toLowerCase();
|
|
|
const columnKey = question.column_key?.toLowerCase();
|
|
|
|
|
|
|
|
|
const popupFields = [
|
|
|
'summary of expectations',
|
|
|
'expectation summary',
|
|
|
'additional remarks',
|
|
|
'remark',
|
|
|
'remarks',
|
|
|
'summary',
|
|
|
'description'
|
|
|
];
|
|
|
|
|
|
return popupFields.some(field =>
|
|
|
label?.includes(field) || columnKey?.includes(field)
|
|
|
);
|
|
|
}
|
|
|
|
|
|
|
|
|
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 {
|
|
|
|
|
|
if (this.isAdditionalRemarksField(question)) {
|
|
|
this.popupQuestion = question;
|
|
|
this.popupTextValue = this.getFormValue(question.column_key) || '';
|
|
|
this.showTextInputPopup = true;
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
this.initializeTextareaHeight();
|
|
|
});
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
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();
|
|
|
}
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
|
const length = textarea.value.length;
|
|
|
textarea.setSelectionRange(length, length);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
adjustTextareaHeight(textarea: HTMLTextAreaElement): void {
|
|
|
|
|
|
textarea.style.height = 'auto';
|
|
|
|
|
|
|
|
|
const maxHeight = 400;
|
|
|
const newHeight = Math.min(textarea.scrollHeight, maxHeight);
|
|
|
textarea.style.height = newHeight + 'px';
|
|
|
|
|
|
|
|
|
textarea.style.overflowY = textarea.scrollHeight > maxHeight ? 'auto' : 'hidden';
|
|
|
}
|
|
|
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
openTutorialModal(): void {
|
|
|
this.showTutorialModal = true;
|
|
|
|
|
|
localStorage.setItem('mandatory_tutorial_seen', 'true');
|
|
|
this.cdRef.detectChanges();
|
|
|
console.log('π Opening mandatory fields tutorial');
|
|
|
}
|
|
|
|
|
|
closeTutorialModal(): void {
|
|
|
this.showTutorialModal = false;
|
|
|
this.cdRef.detectChanges();
|
|
|
}
|
|
|
|
|
|
|
|
|
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();
|
|
|
}
|
|
|
|
|
|
|
|
|
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();
|
|
|
}
|
|
|
|
|
|
|
|
|
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`;
|
|
|
}
|
|
|
|
|
|
|
|
|
@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();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
|
this.showMandatoryTutorial();
|
|
|
|
|
|
setTimeout(() => {
|
|
|
this.enhanceUserExperience();
|
|
|
}, 100);
|
|
|
|
|
|
console.log('β
UserPreferences component initialized');
|
|
|
}
|
|
|
|
|
|
private showMandatoryTutorial(): void {
|
|
|
|
|
|
const tutorialSeen = localStorage.getItem('mandatory_tutorial_seen');
|
|
|
|
|
|
if (!tutorialSeen) {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
this.showTutorialModal = true;
|
|
|
|
|
|
localStorage.setItem('mandatory_tutorial_seen', 'true');
|
|
|
this.cdRef.detectChanges();
|
|
|
console.log('π Auto-showing mandatory fields tutorial');
|
|
|
}, 800);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
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;
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
|
|
|
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)';
|
|
|
}
|
|
|
|
|
|
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
|
|
|
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');
|
|
|
}
|
|
|
}
|
|
|
|