import { Component, inject, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { ApiService, Project } from '../../services/api.service'; @Component({ selector: 'app-projects', standalone: true, imports: [CommonModule, FormsModule], template: `

Projects

@if (loading) {
Loading projects...
} @else if (filteredProjects.length === 0) {

No projects found.

} @else { @if (viewMode === 'card') {
@for (project of filteredProjects; track project.id) {
đŸ›Šī¸

{{ project.name }}

{{ project.caption || 'No description' }}

Versions: {{ project.versions.length || 0 }} ({{ getPublishedCount(project) }} published) Status: {{ project.enabled ? '✓ Enabled' : '✗ Disabled' }} Last update: {{ getRelativeTime(project.last_update_date) }}
}
} @else { @for (project of filteredProjects; track project.id) { }
Name Caption Versions Enabled Deleted Last Update Actions
{{ project.name }} {{ project.caption || '-' }} {{ project.versions.length || 0 }} ({{ getPublishedCount(project) }} published) @if (project.enabled) { ✓ } @else { ✗ } @if (project.deleted) { ✓ } @else { ✗ } {{ getRelativeTime(project.last_update_date) }} @if (!project.deleted) { }
} } @if (message) {
{{ message }}
}
`, styles: [` .projects-container { .toolbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; h2 { margin: 0; } .toolbar-actions { display: flex; gap: 0.5rem; align-items: center; } } .search-input { padding: 0.375rem 0.75rem; border: 1px solid #ced4da; border-radius: 0.25rem; width: 200px; } .checkbox-label { display: flex; align-items: center; gap: 0.25rem; cursor: pointer; } .view-toggle { display: flex; border: 1px solid #ced4da; border-radius: 0.25rem; overflow: hidden; button { background: white; border: none; padding: 0.375rem 0.75rem; cursor: pointer; &.active { background-color: #007bff; color: white; } } } .loading, .empty-state { text-align: center; padding: 3rem; background-color: white; border-radius: 0.25rem; p { margin-bottom: 1rem; color: #6c757d; } } .project-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1rem; } .project-card { background: white; border: 1px solid #dee2e6; border-radius: 0.5rem; padding: 1.5rem; &:hover { box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); } &.disabled { opacity: 0.7; } &.deleted { background-color: #f8f9fa; } .project-icon { font-size: 2rem; margin-bottom: 0.5rem; } h3 { margin: 0 0 0.5rem 0; font-size: 1.25rem; } p { color: #6c757d; margin-bottom: 1rem; } .project-meta { font-size: 0.875rem; color: #6c757d; margin-bottom: 1rem; span { display: block; margin-bottom: 0.25rem; } } .project-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; button { flex: 1; min-width: 80px; font-size: 0.875rem; padding: 0.375rem 0.5rem; } } } .status-badge { &.enabled { color: #28a745; } &.deleted { color: #dc3545; } } .actions { display: flex; gap: 0.25rem; } .action-btn { background: none; border: none; cursor: pointer; font-size: 1.1rem; padding: 0.25rem; border-radius: 0.25rem; &:hover { background-color: #f8f9fa; } &.danger:hover { background-color: #f8d7da; } } tr.deleted { opacity: 0.6; background-color: #f8f9fa; } } `] }) export class ProjectsComponent implements OnInit { private apiService = inject(ApiService); projects: Project[] = []; filteredProjects: Project[] = []; loading = true; showDeleted = false; searchTerm = ''; viewMode: 'card' | 'list' = 'card'; message = ''; isError = false; ngOnInit() { this.loadProjects(); } loadProjects() { this.loading = true; this.apiService.getProjects(this.showDeleted).subscribe({ next: (projects) => { this.projects = projects; this.filterProjects(); this.loading = false; }, error: (err) => { this.showMessage('Failed to load projects', true); this.loading = false; } }); } filterProjects() { const term = this.searchTerm.toLowerCase(); this.filteredProjects = this.projects.filter(project => project.name.toLowerCase().includes(term) || (project.caption || '').toLowerCase().includes(term) ); } getPublishedCount(project: Project): number { return project.versions.filter(v => v.published).length || 0; } getRelativeTime(timestamp: string): string { const date = new Date(timestamp); const now = new Date(); const diff = now.getTime() - date.getTime(); const minutes = Math.floor(diff / 60000); const hours = Math.floor(diff / 3600000); const days = Math.floor(diff / 86400000); if (minutes < 1) return 'just now'; if (minutes < 60) return `${minutes} min ago`; if (hours < 24) return `${hours} hour${hours > 1 ? 's' : ''} ago`; return `${days} day${days > 1 ? 's' : ''} ago`; } createProject() { // TODO: Open create dialog console.log('Create project - not implemented yet'); } editProject(project: Project) { // TODO: Open edit dialog console.log('Edit project:', project.name); } manageVersions(project: Project) { // TODO: Open versions dialog console.log('Manage versions:', project.name); } exportProject(project: Project) { this.apiService.exportProject(project.id).subscribe({ next: (data) => { const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${project.name}_export.json`; a.click(); window.URL.revokeObjectURL(url); this.showMessage(`Project "${project.name}" exported successfully`, false); }, error: (err) => { this.showMessage('Failed to export project', true); } }); } toggleProject(project: Project) { this.apiService.toggleProject(project.id).subscribe({ next: (result) => { project.enabled = result.enabled; this.showMessage(`Project "${project.name}" ${result.enabled ? 'enabled' : 'disabled'}`, false); }, error: (err) => { this.showMessage('Failed to toggle project', true); } }); } deleteProject(project: Project) { if (confirm(`Are you sure you want to delete "${project.name}"?`)) { this.apiService.deleteProject(project.id).subscribe({ next: () => { this.showMessage(`Project "${project.name}" deleted successfully`, false); this.loadProjects(); }, error: (err) => { this.showMessage(err.error?.detail || 'Failed to delete project', true); } }); } } private showMessage(message: string, isError: boolean) { this.message = message; this.isError = isError; setTimeout(() => { this.message = ''; }, 5000); } }