Files
market-backOfficce/src/app/pages/project-view/project-view.component.ts
2026-01-19 23:17:07 +04:00

239 lines
7.0 KiB
TypeScript

import { Component, OnInit, signal, computed } from '@angular/core';
import { ActivatedRoute, Router, RouterOutlet } from '@angular/router';
import { CommonModule } from '@angular/common';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatTreeModule } from '@angular/material/tree';
import { MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
import { ApiService } from '../../services';
import { Category, Subcategory } from '../../models';
import { CreateDialogComponent } from '../../components/create-dialog/create-dialog.component';
import { ConfirmDialogComponent } from '../../components/confirm-dialog/confirm-dialog.component';
interface CategoryNode {
id: string;
name: string;
type: 'category' | 'subcategory';
visible: boolean;
expanded?: boolean;
children?: CategoryNode[];
categoryId?: string;
}
@Component({
selector: 'app-project-view',
standalone: true,
imports: [
CommonModule,
RouterOutlet,
MatSidenavModule,
MatTreeModule,
MatIconModule,
MatButtonModule,
MatSlideToggleModule,
MatToolbarModule,
MatProgressSpinnerModule,
MatDialogModule,
MatSnackBarModule
],
templateUrl: './project-view.component.html',
styleUrls: ['./project-view.component.scss']
})
export class ProjectViewComponent implements OnInit {
projectId = signal<string>('');
project = signal<any>(null);
categories = signal<Category[]>([]);
loading = signal(true);
treeData = signal<CategoryNode[]>([]);
constructor(
private route: ActivatedRoute,
private router: Router,
private apiService: ApiService,
private dialog: MatDialog,
private snackBar: MatSnackBar
) {}
ngOnInit() {
this.route.params.subscribe(params => {
this.projectId.set(params['projectId']);
this.loadProject();
this.loadCategories();
});
}
hasActiveRoute(): boolean {
return this.route.children.length > 0;
}
loadProject() {
// Load project details
this.apiService.getProjects().subscribe({
next: (projects) => {
const project = projects.find(p => p.id === this.projectId());
this.project.set(project);
},
error: (err) => {
console.error('Failed to load project', err);
}
});
}
loadCategories() {
this.loading.set(true);
this.apiService.getCategories(this.projectId()).subscribe({
next: (categories) => {
this.categories.set(categories);
this.buildTree();
this.loading.set(false);
},
error: (err) => {
console.error('Failed to load categories', err);
this.loading.set(false);
}
});
}
buildTree() {
const tree: CategoryNode[] = this.categories().map(cat => ({
id: cat.id,
name: cat.name,
type: 'category' as const,
visible: cat.visible,
expanded: false,
children: (cat.subcategories || []).map(sub => ({
id: sub.id,
name: sub.name,
type: 'subcategory' as const,
visible: sub.visible,
categoryId: cat.id
}))
}));
this.treeData.set(tree);
}
toggleNode(node: CategoryNode) {
node.expanded = !node.expanded;
}
editNode(node: CategoryNode, event: Event) {
event.stopPropagation();
if (node.type === 'category') {
this.router.navigate(['category', node.id], { relativeTo: this.route });
} else {
this.router.navigate(['subcategory', node.id], { relativeTo: this.route });
}
}
toggleVisibility(node: CategoryNode, event: any) {
event.stopPropagation();
node.visible = !node.visible;
if (node.type === 'category') {
this.apiService.updateCategory(node.id, { visible: node.visible }).subscribe();
} else {
this.apiService.updateSubcategory(node.id, { visible: node.visible }).subscribe();
}
}
viewItems(node: CategoryNode, event: Event) {
event.stopPropagation();
if (node.type === 'subcategory') {
console.log('Navigating to items for subcategory:', node.id);
this.router.navigate(['/project', this.projectId(), 'items', node.id]);
}
}
goBack() {
this.router.navigate(['/']);
}
addCategory() {
const dialogRef = this.dialog.open(CreateDialogComponent, {
data: {
title: 'Create New Category',
type: 'category',
fields: [
{ name: 'name', label: 'Name', type: 'text', required: true },
{ name: 'priority', label: 'Priority', type: 'number', value: 99 },
{ name: 'visible', label: 'Visible', type: 'toggle', value: true }
]
}
});
dialogRef.afterClosed().subscribe(result => {
if (result) {
this.apiService.createCategory(this.projectId(), result).subscribe({
next: () => {
this.snackBar.open('Category created!', 'Close', { duration: 2000 });
this.loadCategories();
},
error: (err) => {
this.snackBar.open('Failed to create category', 'Close', { duration: 3000 });
}
});
}
});
}
deleteCategory(node: CategoryNode, event: Event) {
event.stopPropagation();
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
data: {
title: 'Delete Category',
message: `Are you sure you want to delete "${node.name}"? This will also delete all subcategories and items.`,
confirmText: 'Delete',
warning: true
}
});
dialogRef.afterClosed().subscribe(confirmed => {
if (confirmed) {
this.apiService.deleteCategory(node.id).subscribe({
next: () => {
this.snackBar.open('Category deleted', 'Close', { duration: 2000 });
this.loadCategories();
},
error: (err) => {
this.snackBar.open('Failed to delete category', 'Close', { duration: 3000 });
}
});
}
});
}
deleteSubcategory(node: CategoryNode, event: Event) {
event.stopPropagation();
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
data: {
title: 'Delete Subcategory',
message: `Are you sure you want to delete "${node.name}"? This will also delete all items.`,
confirmText: 'Delete',
cancelText: 'Cancel',
dangerous: true
}
});
dialogRef.afterClosed().subscribe(confirmed => {
if (confirmed) {
this.apiService.deleteSubcategory(node.id).subscribe({
next: () => {
this.snackBar.open('Subcategory deleted', 'Close', { duration: 2000 });
this.loadCategories();
},
error: (err) => {
this.snackBar.open('Failed to delete subcategory', 'Close', { duration: 3000 });
}
});
}
});
}
}