Spaces:
Running
Running
| import { Component, OnInit } from '@angular/core'; | |
| import { CommonModule } from '@angular/common'; | |
| import { FormsModule } from '@angular/forms'; | |
| import { MatDialog, MatDialogModule } from '@angular/material/dialog'; | |
| import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar'; | |
| import { MatTableModule } from '@angular/material/table'; | |
| import { MatProgressBarModule } from '@angular/material/progress-bar'; | |
| import { MatButtonModule } from '@angular/material/button'; | |
| import { MatCheckboxModule } from '@angular/material/checkbox'; | |
| import { MatFormFieldModule } from '@angular/material/form-field'; | |
| import { MatInputModule } from '@angular/material/input'; | |
| import { MatButtonToggleModule } from '@angular/material/button-toggle'; | |
| import { MatCardModule } from '@angular/material/card'; | |
| import { MatChipsModule } from '@angular/material/chips'; | |
| import { MatIconModule } from '@angular/material/icon'; | |
| import { MatMenuModule } from '@angular/material/menu'; | |
| import { MatDividerModule } from '@angular/material/divider'; | |
| import { ApiService, Project } from '../../services/api.service'; | |
| import ProjectEditDialogComponent from '../../dialogs/project-edit-dialog/project-edit-dialog.component'; | |
| import VersionEditDialogComponent from '../../dialogs/version-edit-dialog/version-edit-dialog.component'; | |
| import ConfirmDialogComponent from '../../dialogs/confirm-dialog/confirm-dialog.component'; | |
| ({ | |
| selector: 'app-projects', | |
| standalone: true, | |
| imports: [ | |
| CommonModule, | |
| FormsModule, | |
| MatTableModule, | |
| MatProgressBarModule, | |
| MatButtonModule, | |
| MatCheckboxModule, | |
| MatFormFieldModule, | |
| MatInputModule, | |
| MatButtonToggleModule, | |
| MatCardModule, | |
| MatChipsModule, | |
| MatIconModule, | |
| MatMenuModule, | |
| MatDividerModule, | |
| MatDialogModule, | |
| MatSnackBarModule | |
| ], | |
| templateUrl: './projects.component.html', | |
| styleUrls: ['./projects.component.scss'] | |
| }) | |
| export class ProjectsComponent implements OnInit { | |
| projects: Project[] = []; | |
| filteredProjects: Project[] = []; | |
| searchTerm = ''; | |
| showDeleted = false; | |
| viewMode: 'list' | 'card' = 'card'; | |
| loading = false; | |
| message = ''; | |
| isError = false; | |
| // For table view | |
| displayedColumns: string[] = ['name', 'caption', 'versions', 'status', 'lastUpdate', 'actions']; | |
| constructor( | |
| private apiService: ApiService, | |
| private dialog: MatDialog, | |
| private snackBar: MatSnackBar | |
| ) {} | |
| ngOnInit() { | |
| this.loadProjects(); | |
| this.loadEnvironment(); | |
| } | |
| isSparkTabVisible(): boolean { | |
| // Environment bilgisini cache'ten al (eğer varsa) | |
| const env = localStorage.getItem('flare_environment'); | |
| if (env) { | |
| const config = JSON.parse(env); | |
| return !config.work_mode?.startsWith('gpt4o'); | |
| } | |
| return true; // Default olarak göster | |
| } | |
| async loadProjects() { | |
| this.loading = true; | |
| try { | |
| this.projects = await this.apiService.getProjects(this.showDeleted).toPromise() || []; | |
| this.applyFilter(); | |
| } catch (error) { | |
| this.showMessage('Failed to load projects', true); | |
| } finally { | |
| this.loading = false; | |
| } | |
| } | |
| private loadEnvironment() { | |
| this.apiService.getEnvironment().subscribe({ | |
| next: (env) => { | |
| localStorage.setItem('flare_environment', JSON.stringify(env)); | |
| }, | |
| error: (err) => { | |
| console.error('Failed to load environment:', err); | |
| } | |
| }); | |
| } | |
| applyFilter() { | |
| this.filteredProjects = this.projects.filter(project => { | |
| const matchesSearch = !this.searchTerm || | |
| project.name.toLowerCase().includes(this.searchTerm.toLowerCase()) || | |
| (project.caption || '').toLowerCase().includes(this.searchTerm.toLowerCase()); | |
| const matchesDeleted = this.showDeleted || !project.deleted; | |
| return matchesSearch && matchesDeleted; | |
| }); | |
| } | |
| filterProjects() { | |
| this.applyFilter(); | |
| } | |
| onSearchChange() { | |
| this.applyFilter(); | |
| } | |
| onShowDeletedChange() { | |
| this.loadProjects(); | |
| } | |
| createProject() { | |
| const dialogRef = this.dialog.open(ProjectEditDialogComponent, { | |
| width: '500px', | |
| data: { mode: 'create' } | |
| }); | |
| dialogRef.afterClosed().subscribe(result => { | |
| if (result) { | |
| this.loadProjects(); | |
| } | |
| }); | |
| } | |
| editProject(project: Project) { | |
| const dialogRef = this.dialog.open(ProjectEditDialogComponent, { | |
| width: '500px', | |
| data: { mode: 'edit', project: { ...project } } | |
| }); | |
| dialogRef.afterClosed().subscribe(result => { | |
| if (result) { | |
| // Listeyi güncelle | |
| const index = this.projects.findIndex(p => p.id === result.id); | |
| if (index !== -1) { | |
| this.projects[index] = result; | |
| this.applyFilter(); // Filtreyi yeniden uygula | |
| } else { | |
| this.loadProjects(); // Bulunamazsa tüm listeyi yenile | |
| } | |
| } | |
| }); | |
| } | |
| async toggleProject(project: Project) { | |
| try { | |
| const result = await this.apiService.toggleProject(project.id).toPromise(); | |
| project.enabled = result.enabled; | |
| this.showMessage( | |
| `Project "${project.name}" ${project.enabled ? 'enabled' : 'disabled'} successfully`, | |
| false | |
| ); | |
| } catch (error) { | |
| this.showMessage('Failed to toggle project', true); | |
| } | |
| } | |
| manageVersions(project: Project) { | |
| const dialogRef = this.dialog.open(VersionEditDialogComponent, { | |
| width: '90vw', | |
| maxWidth: '1200px', | |
| height: '90vh', | |
| data: { project } | |
| }); | |
| dialogRef.afterClosed().subscribe(result => { | |
| if (result) { | |
| this.loadProjects(); | |
| } | |
| }); | |
| } | |
| async deleteProject(project: Project) { | |
| const hasVersions = project.versions && project.versions.length > 0; | |
| const message = hasVersions ? | |
| `Project "${project.name}" has ${project.versions.length} version(s). Are you sure you want to delete it?` : | |
| `Are you sure you want to delete project "${project.name}"?`; | |
| const dialogRef = this.dialog.open(ConfirmDialogComponent, { | |
| width: '400px', | |
| data: { | |
| title: 'Delete Project', | |
| message: message, | |
| confirmText: 'Delete', | |
| confirmColor: 'warn' | |
| } | |
| }); | |
| dialogRef.afterClosed().subscribe(async confirmed => { | |
| if (confirmed) { | |
| try { | |
| await this.apiService.deleteProject(project.id).toPromise(); | |
| this.showMessage('Project deleted successfully', false); | |
| this.loadProjects(); | |
| } catch (error: any) { | |
| const message = error.error?.detail || 'Failed to delete project'; | |
| this.showMessage(message, true); | |
| } | |
| } | |
| }); | |
| } | |
| async exportProject(project: Project) { | |
| try { | |
| const data = await this.apiService.exportProject(project.id).toPromise(); | |
| // Create and download file | |
| const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); | |
| const url = window.URL.createObjectURL(blob); | |
| const link = document.createElement('a'); | |
| link.href = url; | |
| link.download = `${project.name}_export_${new Date().getTime()}.json`; | |
| link.click(); | |
| window.URL.revokeObjectURL(url); | |
| this.showMessage('Project exported successfully', false); | |
| } catch (error) { | |
| this.showMessage('Failed to export project', true); | |
| } | |
| } | |
| importProject() { | |
| const input = document.createElement('input'); | |
| input.type = 'file'; | |
| input.accept = '.json'; | |
| input.onchange = async (event: any) => { | |
| const file = event.target.files[0]; | |
| if (!file) return; | |
| try { | |
| const text = await file.text(); | |
| const data = JSON.parse(text); | |
| await this.apiService.importProject(data).toPromise(); | |
| this.showMessage('Project imported successfully', false); | |
| this.loadProjects(); | |
| } catch (error: any) { | |
| const message = error.error?.detail || 'Failed to import project'; | |
| this.showMessage(message, true); | |
| } | |
| }; | |
| input.click(); | |
| } | |
| getPublishedCount(project: Project): number { | |
| return project.versions?.filter(v => v.published).length || 0; | |
| } | |
| getRelativeTime(timestamp: string | undefined): string { | |
| if (!timestamp) return 'Never'; | |
| const date = new Date(timestamp); | |
| const now = new Date(); | |
| const diffMs = now.getTime() - date.getTime(); | |
| const diffMins = Math.floor(diffMs / 60000); | |
| const diffHours = Math.floor(diffMs / 3600000); | |
| const diffDays = Math.floor(diffMs / 86400000); | |
| if (diffMins < 60) return `${diffMins} minutes ago`; | |
| if (diffHours < 24) return `${diffHours} hours ago`; | |
| if (diffDays < 7) return `${diffDays} days ago`; | |
| return date.toLocaleDateString(); | |
| } | |
| trackByProjectId(index: number, project: Project): number { | |
| return project.id; | |
| } | |
| private showMessage(message: string, isError: boolean) { | |
| this.message = message; | |
| this.isError = isError; | |
| setTimeout(() => { | |
| this.message = ''; | |
| }, 5000); | |
| } | |
| } |