431 lines
10 KiB
Markdown
431 lines
10 KiB
Markdown
# Quick Reference Guide - New Improvements
|
|
|
|
## 🚀 How to Use New Features
|
|
|
|
### 1. Validation Service
|
|
|
|
#### Import and inject:
|
|
```typescript
|
|
import { ValidationService } from '../../services/validation.service';
|
|
|
|
constructor(private validationService: ValidationService) {}
|
|
```
|
|
|
|
#### Validate a single field:
|
|
```typescript
|
|
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:
|
|
```typescript
|
|
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:
|
|
```typescript
|
|
import { ToastService } from '../../services/toast.service';
|
|
|
|
constructor(private toast: ToastService) {}
|
|
```
|
|
|
|
#### Show notifications:
|
|
```typescript
|
|
// 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:
|
|
```typescript
|
|
import { LoadingSkeletonComponent } from '../../components/loading-skeleton/loading-skeleton.component';
|
|
|
|
@Component({
|
|
imports: [
|
|
// ... other imports
|
|
LoadingSkeletonComponent
|
|
]
|
|
})
|
|
```
|
|
|
|
#### Use in template:
|
|
```html
|
|
<!-- 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:
|
|
```typescript
|
|
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:
|
|
```typescript
|
|
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:
|
|
```typescript
|
|
// 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
|
|
```typescript
|
|
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
|
|
```typescript
|
|
// 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
|
|
```typescript
|
|
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:
|
|
```typescript
|
|
// 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:
|
|
```html
|
|
<!-- OLD -->
|
|
@if (loading()) {
|
|
<mat-spinner></mat-spinner>
|
|
}
|
|
|
|
<!-- NEW -->
|
|
@if (loading()) {
|
|
<app-loading-skeleton type="list"></app-loading-skeleton>
|
|
}
|
|
```
|
|
|
|
### Add validation to existing forms:
|
|
```typescript
|
|
// 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
|