flare / flare-ui /src /app /dialogs /intent-edit-dialog /intent-edit-dialog.component.ts
ciyidogan's picture
Upload 118 files
9f79da5 verified
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<IntentEditDialogComponent>,
@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();
}
}