flare / flare-ui /src /app /components /projects /projects.component.ts
ciyidogan's picture
Update flare-ui/src/app/components/projects/projects.component.ts
6d42cab verified
raw
history blame
7.99 kB
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';
@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();
}
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;
}
}
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) {
this.loadProjects();
}
});
}
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);
}
}