Files
market-backOfficce/QUICK-REFERENCE.md
2026-01-22 00:41:13 +04:00

10 KiB

Quick Reference Guide - New Improvements

🚀 How to Use New Features

1. Validation Service

Import and inject:

import { ValidationService } from '../../services/validation.service';

constructor(private validationService: ValidationService) {}

Validate a single field:

onFieldChange(field: string, value: any) {
  const errors = this.validationService.validateItem({ [field]: value });
  
  if (errors[field]) {
    this.snackBar.open(`Error: ${errors[field]}`, 'Close', { duration: 3000 });
    return;
  }
  
  // Proceed with save
}

Validate entire object:

onSubmit() {
  const errors = this.validationService.validateItem(this.item);
  
  if (Object.keys(errors).length > 0) {
    const errorMsg = Object.values(errors).join(', ');
    this.snackBar.open(`Validation failed: ${errorMsg}`, 'Close', { duration: 4000 });
    return;
  }
  
  // Proceed with API call
  this.apiService.createItem(...);
}

Available validators:

  • validateRequired(value) - Checks for empty values
  • validateNumber(value, min?, max?) - Validates numeric input
  • validatePrice(value) - Ensures price >= 0
  • validateQuantity(value) - Validates integer >= 0
  • validateUrl(value) - Checks URL format
  • validateImageUrl(value) - Validates image extensions
  • validateId(value) - Ensures URL-safe ID (2-50 chars)
  • validatePriority(value) - Validates 0-9999
  • validateCurrency(value) - USD, EUR, RUB, GBP, UAH
  • validateArrayNotEmpty(arr) - Ensures non-empty array

2. Toast Notification Service

Import and inject:

import { ToastService } from '../../services/toast.service';

constructor(private toast: ToastService) {}

Show notifications:

// Success (green, 2s)
this.toast.success('Item saved successfully!');

// Error (red, 4s)
this.toast.error('Failed to save item');

// Warning (orange, 3s)
this.toast.warning('This action will delete all items');

// Info (blue, 2s)
this.toast.info('Loading data...');

// Custom duration
this.toast.success('Saved!', 5000); // 5 seconds

Best practices:

  • Use success() for completed operations
  • Use error() for failures with specific message
  • Use warning() before dangerous actions
  • Use info() for status updates

3. Loading Skeleton Component

Import in component:

import { LoadingSkeletonComponent } from '../../components/loading-skeleton/loading-skeleton.component';

@Component({
  imports: [
    // ... other imports
    LoadingSkeletonComponent
  ]
})

Use in template:

<!-- For tree/sidebar -->
@if (loading()) {
  <app-loading-skeleton type="tree"></app-loading-skeleton>
} @else {
  <!-- Your tree content -->
}

<!-- For card layouts -->
@if (loading()) {
  <app-loading-skeleton type="card"></app-loading-skeleton>
} @else {
  <!-- Your card content -->
}

<!-- For list views -->
@if (loading()) {
  <app-loading-skeleton type="list"></app-loading-skeleton>
} @else {
  <!-- Your list content -->
}

<!-- For forms -->
@if (loading()) {
  <app-loading-skeleton type="form"></app-loading-skeleton>
} @else {
  <!-- Your form content -->
}

Available types:

  • tree - Nested hierarchy with indentation (sidebar)
  • card - Grid of rectangular cards
  • list - Vertical list of items
  • form - Form fields with labels

4. Enhanced Error Handling

The API service now provides detailed error messages automatically:

Error codes and messages:

  • 400 - "Invalid request data"
  • 401 - "Unauthorized - please log in"
  • 403 - "You don't have permission to perform this action"
  • 404 - "Resource not found"
  • 409 - "Conflict - resource already exists"
  • 422 - "Validation failed"
  • 500 - "Internal server error"
  • 503 - "Service temporarily unavailable"

Example usage:

this.apiService.createItem(item).subscribe({
  next: (created) => {
    this.toast.success('Item created!');
  },
  error: (err) => {
    // err.message already contains user-friendly message
    this.toast.error(err.message || 'Failed to create item');
  }
});

5. Enhanced Confirmation Dialogs

Example with count information:

deleteCategory(node: CategoryNode) {
  const subCount = node.children?.length || 0;
  const message = subCount > 0 
    ? `Are you sure you want to delete "${node.name}"? This will also delete ${subCount} subcategory(ies) and all their items. This action cannot be undone.`
    : `Are you sure you want to delete "${node.name}"? This action cannot be undone.`;
  
  const dialogRef = this.dialog.open(ConfirmDialogComponent, {
    data: {
      title: 'Delete Category',
      message: message,
      confirmText: 'Delete',
      cancelText: 'Cancel',
      dangerous: true  // Shows red confirm button
    }
  });
  
  dialogRef.afterClosed().subscribe(result => {
    if (result) {
      // User confirmed, proceed with deletion
    }
  });
}

Dialog options:

  • title - Dialog header
  • message - Description text (can include HTML)
  • confirmText - Confirm button label (default: "Confirm")
  • cancelText - Cancel button label (default: "Cancel")
  • dangerous - Makes confirm button red (for destructive actions)

🎨 Styling Tips

Toast colors are automatic:

// Green background
this.toast.success('...');

// Red background
this.toast.error('...');

// Orange background
this.toast.warning('...');

// Blue background
this.toast.info('...');

Loading skeletons animate automatically:

  • Shimmer effect from left to right
  • Gray placeholders that match content structure
  • No additional CSS needed

📋 Component Integration Checklist

When adding these features to a new component:

For validation:

  • Import ValidationService
  • Inject in constructor
  • Call validation before API calls
  • Display errors via toast or inline

For loading states:

  • Import LoadingSkeletonComponent
  • Add to imports array
  • Add loading signal
  • Wrap content with @if (loading()) { skeleton } @else { content }

For toasts:

  • Import ToastService
  • Inject in constructor
  • Replace MatSnackBar calls with toast methods
  • Use appropriate type (success/error/warning/info)

For error handling:

  • Handle error callback in subscribe
  • Use err.message for user-friendly message
  • Display via toast service
  • Consider retry logic for critical operations

🐛 Common Patterns

Pattern 1: Form submission with validation

onSubmit() {
  // Validate
  const errors = this.validationService.validateItem(this.item());
  if (Object.keys(errors).length > 0) {
    const errorMsg = Object.values(errors).join(', ');
    this.toast.error(`Validation failed: ${errorMsg}`);
    return;
  }
  
  // Save
  this.saving.set(true);
  this.apiService.updateItem(this.itemId(), this.item()).subscribe({
    next: (updated) => {
      this.item.set(updated);
      this.toast.success('Item saved successfully!');
      this.saving.set(false);
    },
    error: (err) => {
      this.toast.error(err.message || 'Failed to save item');
      this.saving.set(false);
    }
  });
}

Pattern 2: Loading data with skeleton

// Component
loading = signal(true);

ngOnInit() {
  this.loadData();
}

loadData() {
  this.loading.set(true);
  this.apiService.getData().subscribe({
    next: (data) => {
      this.data.set(data);
      this.loading.set(false);
    },
    error: (err) => {
      this.toast.error(err.message || 'Failed to load data');
      this.loading.set(false);
    }
  });
}

// Template
@if (loading()) {
  <app-loading-skeleton type="list"></app-loading-skeleton>
} @else {
  @for (item of data(); track item.id) {
    <div>{{ item.name }}</div>
  }
}

Pattern 3: Destructive action with confirmation

deleteItem() {
  const dialogRef = this.dialog.open(ConfirmDialogComponent, {
    data: {
      title: 'Delete Item',
      message: `Are you sure you want to delete "${this.item().name}"? This action cannot be undone.`,
      confirmText: 'Delete',
      cancelText: 'Cancel',
      dangerous: true
    }
  });
  
  dialogRef.afterClosed().subscribe(result => {
    if (result) {
      this.apiService.deleteItem(this.itemId()).subscribe({
        next: () => {
          this.toast.success('Item deleted successfully');
          this.router.navigate(['/items']);
        },
        error: (err) => {
          this.toast.error(err.message || 'Failed to delete item');
        }
      });
    }
  });
}

Testing Your Implementation

Test validation:

  1. Try submitting empty form → Should show "Name is required"
  2. Try negative price → Should show "Price must be at least 0"
  3. Try invalid quantity → Should show "Quantity must be a non-negative integer"

Test toasts:

  1. Success action → Should show green toast
  2. Failed action → Should show red toast
  3. Multiple toasts → Should queue properly

Test loading skeletons:

  1. Slow network → Should show skeleton first
  2. Fast network → Should briefly show skeleton
  3. Skeleton should match content structure

Test error handling:

  1. Network error → Should show user-friendly message
  2. 404 error → Should show "Resource not found"
  3. 500 error → Should show "Internal server error"

🎯 Migration Guide

Replace old MatSnackBar:

// OLD
this.snackBar.open('Saved!', 'Close', { duration: 2000 });
this.snackBar.open('Error!', 'Close', { duration: 3000 });

// NEW
this.toast.success('Saved!');
this.toast.error('Error!');

Replace old spinners:

<!-- OLD -->
@if (loading()) {
  <mat-spinner></mat-spinner>
}

<!-- NEW -->
@if (loading()) {
  <app-loading-skeleton type="list"></app-loading-skeleton>
}

Add validation to existing forms:

// Before API call, add:
const errors = this.validationService.validateItem(this.item);
if (Object.keys(errors).length > 0) {
  this.toast.error(`Validation errors: ${Object.values(errors).join(', ')}`);
  return;
}

📚 Additional Resources

  • IMPROVEMENTS.md - Full improvement roadmap
  • URGENT-IMPROVEMENTS-COMPLETED.md - Implementation details
  • API.md - API documentation

For questions or issues, check the implementation in:

  • src/app/pages/project-view/ - Reference implementation
  • src/app/pages/item-editor/ - Validation examples
  • src/app/services/ - Service documentation