import { Component, Inject, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormBuilder, FormGroup, FormArray, Validators, ReactiveFormsModule, FormsModule } from '@angular/forms'; import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatChipsModule } from '@angular/material/chips'; import { MatTableModule } from '@angular/material/table'; import { MatTabsModule } from '@angular/material/tabs'; import { MatExpansionModule } from '@angular/material/expansion'; import { MatListModule } from '@angular/material/list'; import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar'; import { MatDialog } from '@angular/material/dialog'; // Interfaces for multi-language support interface LocalizedExample { locale_code: string; example: string; } interface LocalizedCaption { locale_code: string; caption: string; } interface ParameterWithLocalizedCaption { name: string; caption: LocalizedCaption[]; type: string; required: boolean; variable_name: string; extraction_prompt?: string; validation_regex?: string; invalid_prompt?: string; type_error_prompt?: string; } @Component({ selector: 'app-intent-edit-dialog', standalone: true, imports: [ CommonModule, ReactiveFormsModule, FormsModule, MatDialogModule, MatFormFieldModule, MatInputModule, MatSelectModule, MatCheckboxModule, MatButtonModule, MatIconModule, MatChipsModule, MatTableModule, MatTabsModule, MatExpansionModule, MatListModule, MatSnackBarModule ], templateUrl: './intent-edit-dialog.component.html', styleUrls: ['./intent-edit-dialog.component.scss'] }) export default class IntentEditDialogComponent implements OnInit { form!: FormGroup; availableAPIs: any[] = []; parameterTypes = ['str', 'int', 'float', 'bool', 'date']; // Multi-language support supportedLocales: string[] = []; selectedExampleLocale: string = ''; examples: LocalizedExample[] = []; newExample = ''; constructor( private fb: FormBuilder, private snackBar: MatSnackBar, private dialog: MatDialog, public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: any ) { this.availableAPIs = data.apis || []; this.supportedLocales = data.project?.supported_locales || data.supportedLocales || ['tr']; this.selectedExampleLocale = data.project?.default_locale || data.defaultLocale || this.supportedLocales[0] || 'tr'; } ngOnInit() { this.initializeForm(); if (this.data.intent) { this.populateForm(this.data.intent); } } initializeForm() { this.form = this.fb.group({ name: ['', [Validators.required, Validators.pattern(/^[a-zA-Z0-9-]+$/)]], caption: ['', Validators.required], detection_prompt: ['', Validators.required], parameters: this.fb.array([]), action: ['', Validators.required], fallback_timeout_prompt: [''], fallback_error_prompt: [''] }); } populateForm(intent: any) { // Populate basic fields this.form.patchValue({ name: intent.name || '', caption: intent.caption || '', detection_prompt: intent.detection_prompt || '', action: intent.action || '', fallback_timeout_prompt: intent.fallback_timeout_prompt || '', fallback_error_prompt: intent.fallback_error_prompt || '' }); // Populate localized examples if (intent.examples && Array.isArray(intent.examples)) { if (intent.examples.length > 0 && typeof intent.examples[0] === 'object' && 'locale_code' in intent.examples[0]) { // New format with LocalizedExample this.examples = [...intent.examples]; } else if (typeof intent.examples[0] === 'string') { // Old format - convert to new format using default locale this.examples = intent.examples.map((ex: string) => ({ locale_code: this.selectedExampleLocale, example: ex })); } } // Populate parameters with localized captions if (intent.parameters && Array.isArray(intent.parameters)) { const paramsArray = this.form.get('parameters') as FormArray; paramsArray.clear(); intent.parameters.forEach((param: any) => { paramsArray.push(this.createParameterFormGroup(param)); }); } } createParameterFormGroup(param?: any): FormGroup { // Convert old caption format to new if needed let captionArray: LocalizedCaption[] = []; if (param?.caption) { if (Array.isArray(param.caption)) { captionArray = param.caption; } else if (typeof param.caption === 'string') { // Old format - convert to new captionArray = [{ locale_code: this.selectedExampleLocale, caption: param.caption }]; } } return this.fb.group({ name: [param?.name || '', Validators.required], caption: [captionArray], type: [param?.type || 'str', Validators.required], required: [param?.required !== false], variable_name: [param?.variable_name || '', Validators.required], extraction_prompt: [param?.extraction_prompt || ''], validation_regex: [param?.validation_regex || ''], invalid_prompt: [param?.invalid_prompt || ''], type_error_prompt: [param?.type_error_prompt || ''] }); } get parameters() { return this.form.get('parameters') as FormArray; } addParameter() { this.parameters.push(this.createParameterFormGroup()); } removeParameter(index: number) { this.parameters.removeAt(index); } // Multi-language example management getExamplesForCurrentLocale(): LocalizedExample[] { return this.examples.filter(ex => ex.locale_code === this.selectedExampleLocale); } addExample() { if (this.newExample.trim()) { const existingIndex = this.examples.findIndex( ex => ex.locale_code === this.selectedExampleLocale && ex.example === this.newExample.trim() ); if (existingIndex === -1) { this.examples.push({ locale_code: this.selectedExampleLocale, example: this.newExample.trim() }); this.newExample = ''; } else { this.snackBar.open('This example already exists for this locale', 'Close', { duration: 3000 }); } } } removeExample(example: LocalizedExample) { const index = this.examples.findIndex( ex => ex.locale_code === example.locale_code && ex.example === example.example ); if (index !== -1) { this.examples.splice(index, 1); } } // Test regex functionality testRegex(paramIndex: number) { const param = this.parameters.at(paramIndex); const regex = param.get('validation_regex')?.value; if (!regex) { this.snackBar.open('No regex pattern to test', 'Close', { duration: 2000 }); return; } // Simple test implementation const testValue = prompt('Enter a test value:'); if (testValue !== null) { try { const pattern = new RegExp(regex); const matches = pattern.test(testValue); this.snackBar.open( matches ? '✓ Pattern matches!' : '✗ Pattern does not match', 'Close', { duration: 3000 } ); } catch (e) { this.snackBar.open('Invalid regex pattern', 'Close', { duration: 3000 }); } } } // Move parameter up or down moveParameter(index: number, direction: 'up' | 'down') { const newIndex = direction === 'up' ? index - 1 : index + 1; if (newIndex < 0 || newIndex >= this.parameters.length) { return; } const currentItem = this.parameters.at(index); this.parameters.removeAt(index); this.parameters.insert(newIndex, currentItem); } // Parameter caption management getCaptionDisplay(captions: LocalizedCaption[]): string { if (!captions || captions.length === 0) return '(No caption)'; // Try to find caption for default locale const defaultCaption = captions.find(c => c.locale_code === (this.data.project?.default_locale || this.data.defaultLocale || 'tr')); if (defaultCaption) return defaultCaption.caption; // Return first available caption return captions[0].caption; } async openCaptionDialog(paramIndex: number) { const param = this.parameters.at(paramIndex); const currentCaptions = param.get('caption')?.value || []; // Import and open caption dialog const { default: CaptionDialogComponent } = await import('../caption-dialog/caption-dialog.component'); const dialogRef = this.dialog.open(CaptionDialogComponent, { width: '600px', data: { captions: [...currentCaptions], supportedLocales: this.supportedLocales, defaultLocale: this.data.project?.default_locale || this.data.defaultLocale } }); dialogRef.afterClosed().subscribe(result => { if (result) { param.patchValue({ caption: result }); } }); } // Locale helpers getLocaleName(localeCode: string): string { const localeNames: { [key: string]: string } = { 'tr': 'Türkçe', 'en': 'English', 'de': 'Deutsch', 'fr': 'Français', 'es': 'Español', 'ar': 'العربية', 'ru': 'Русский', 'zh': '中文', 'ja': '日本語', 'ko': '한국어' }; return localeNames[localeCode] || localeCode; } onSubmit() { if (this.form.valid) { const formValue = this.form.value; // Add examples to the result formValue.examples = this.examples; // Ensure all parameters have captions formValue.parameters = formValue.parameters.map((param: any) => { if (!param.caption || param.caption.length === 0) { // Create default caption if missing param.caption = [{ locale_code: this.data.project?.default_locale || this.data.defaultLocale || 'tr', caption: param.name }]; } return param; }); this.dialogRef.close(formValue); } else { this.snackBar.open('Please fill all required fields', 'Close', { duration: 3000 }); } } save() { this.onSubmit(); } cancel() { this.dialogRef.close(); } }