// project-edit-dialog.component.ts import { Component, Inject, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormBuilder, FormGroup, Validators, ReactiveFormsModule, FormArray } 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 { MatDividerModule } from '@angular/material/divider'; import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { ApiService } from '../../services/api.service'; import { LocaleManagerService, Locale } from '../../services/locale-manager.service'; export interface ProjectDialogData { mode: 'create' | 'edit'; project?: any; } @Component({ selector: 'app-project-edit-dialog', standalone: true, imports: [ CommonModule, ReactiveFormsModule, MatDialogModule, MatFormFieldModule, MatInputModule, MatSelectModule, MatCheckboxModule, MatButtonModule, MatIconModule, MatChipsModule, MatDividerModule, MatSnackBarModule, MatProgressSpinnerModule ], template: `

{{ data.mode === 'create' ? 'Create New Project' : 'Edit Project' }}

Name* Use only letters, numbers, and underscores Name is required Invalid characters in name Caption* Caption is required Icon @for (icon of projectIcons; track icon) { {{ icon }} {{ icon }} } Description Default Language @if (loadingLocales) { Loading languages... } @for (locale of availableLocales; track locale.code) { {{ locale.name }} {{ locale.code }} } translate Primary language for this project Supported Languages
@for (lang of form.get('supportedLanguages')?.value || []; track lang; let last = $last) { {{ getLocaleName(lang) }}@if (!last) {, } }
@for (locale of availableLocales; track locale.code) { {{ locale.name }} {{ locale.code }} }
language Languages available in this project
Timezone @for (tz of timezones; track tz) { {{ tz }} } Region
`, styleUrls: ['./project-edit-dialog.component.scss'] }) export default class ProjectEditDialogComponent implements OnInit { form!: FormGroup; saving = false; loadingLocales = true; availableLocales: Locale[] = []; projectIcons = ['folder', 'work', 'shopping_cart', 'school', 'local_hospital', 'restaurant', 'home', 'business']; timezones = [ 'Europe/Istanbul', 'Europe/London', 'Europe/Berlin', 'America/New_York', 'America/Los_Angeles', 'Asia/Tokyo' ]; constructor( private fb: FormBuilder, private apiService: ApiService, private localeManager: LocaleManagerService, private snackBar: MatSnackBar, public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: ProjectDialogData ) {} ngOnInit() { this.initializeForm(); this.loadAvailableLocales(); } initializeForm() { const defaultValues = this.data.mode === 'edit' && this.data.project ? { name: this.data.project.name, caption: this.data.project.caption || '', icon: this.data.project.icon || 'folder', description: this.data.project.description || '', defaultLanguage: this.data.project.default_language || 'Türkçe', supportedLanguages: this.data.project.supported_languages || ['tr-TR'], timezone: this.data.project.timezone || 'Europe/Istanbul', region: this.data.project.region || 'tr-TR' } : { name: '', caption: '', icon: 'folder', description: '', defaultLanguage: 'Türkçe', supportedLanguages: ['tr-TR'], timezone: 'Europe/Istanbul', region: 'tr-TR' }; this.form = this.fb.group({ name: [defaultValues.name, [Validators.required, Validators.pattern(/^[a-z0-9_]+$/)]], caption: [defaultValues.caption, Validators.required], icon: [defaultValues.icon], description: [defaultValues.description], defaultLanguage: [defaultValues.defaultLanguage], supportedLanguages: [defaultValues.supportedLanguages], timezone: [defaultValues.timezone], region: [defaultValues.region] }); // Disable name field in edit mode if (this.data.mode === 'edit') { this.form.get('name')?.disable(); } } loadAvailableLocales() { this.loadingLocales = true; this.localeManager.getAvailableLocales().subscribe({ next: (locales) => { this.availableLocales = locales; this.loadingLocales = false; this.validateSelectedLanguages(); }, error: (err) => { this.showMessage('Failed to load available languages', 'error'); this.loadingLocales = false; // Use fallback locales this.availableLocales = [ { code: 'tr-TR', name: 'Türkçe', english_name: 'Turkish' }, { code: 'en-US', name: 'English', english_name: 'English (US)' } ]; } }); } validateSelectedLanguages() { const availableCodes = this.availableLocales.map(l => l.code); const currentSupported = this.form.get('supportedLanguages')?.value || []; const currentDefault = this.form.get('defaultLanguage')?.value; // Filter out any unsupported languages const validSupported = currentSupported.filter((lang: string) => availableCodes.includes(lang) ); // Update form if any languages were removed if (validSupported.length !== currentSupported.length) { this.form.patchValue({ supportedLanguages: validSupported }); } // Ensure default language is valid if (!availableCodes.includes(currentDefault)) { const newDefault = availableCodes[0] || 'tr-TR'; this.form.patchValue({ defaultLanguage: newDefault, supportedLanguages: [...validSupported, newDefault] }); } } onDefaultLanguageChange() { // Default language değiştiğinde bir şey yapmaya gerek yok // Çünkü default_language (Türkçe) ve supported_languages (tr-TR) farklı tipte } onSupportedLanguagesChange() { // Supported languages değiştiğinde de bir şey yapmaya gerek yok // En az bir dil seçili olduğu sürece sorun yok const supportedLangs = this.form.get('supportedLanguages')?.value || []; if (supportedLangs.length === 0) { // En az bir dil seçilmeli this.form.patchValue({ supportedLanguages: ['tr-TR'] }); } } getLocaleName(code: string): string { const locale = this.availableLocales.find(l => l.code === code); return locale ? locale.name : code; } async save() { if (this.form.invalid) { this.form.markAllAsTouched(); return; } this.saving = true; try { const formValue = this.form.getRawValue(); // getRawValue to include disabled fields // Project data format matching backend expectations const projectData = { name: formValue.name, caption: formValue.caption, icon: formValue.icon, description: formValue.description, default_language: formValue.defaultLanguage, supported_languages: formValue.supportedLanguages, timezone: formValue.timezone, region: formValue.region }; let result; if (this.data.mode === 'create') { result = await this.apiService.createProject(projectData).toPromise(); this.showMessage('Project created successfully!'); } else { // Add last_update_date for edit mode const updateData = { ...projectData, last_update_date: this.data.project.last_update_date || '' }; result = await this.apiService.updateProject(this.data.project.id, updateData).toPromise(); this.showMessage('Project updated successfully!'); } this.dialogRef.close(result); } catch (error: any) { this.showMessage(error.error?.detail || 'Operation failed', 'error'); } finally { this.saving = false; } } close() { this.dialogRef.close(); } private showMessage(message: string, type: 'success' | 'error' = 'success') { this.snackBar.open(message, 'Close', { duration: 5000, panelClass: type === 'error' ? 'error-snackbar' : 'success-snackbar', horizontalPosition: 'right', verticalPosition: 'top' }); } }