Appearance
Quy trình Frontend Development (SOP)
Quick Reference
- Who: Frontend Developer
- Stack: Angular 19 (Signals, Standalone), TailwindCSS 4, Vitest
- Package Manager: Bun (dev) / npm (CI)
- Rule: Conventional commit messages
1. Thiết lập Dự án
bash
git clone <repo>
cd boxme-insight
bun install # hoặc npm installbash
npm start
# → http://localhost:4200 (demoMode=true mặc định)bash
npm test # Watch mode
npm test -- --watch=false # CI mode (Vitest)bash
npm run build -- --configuration=production
# Output: dist/boxme-insight/browser/2. Tạo Feature mới
2a. Tạo Component
bash
npx ng g c features/ten-tinh-nang --standaloneĐặt tại: src/app/features/ten-tinh-nang/
2b. Tạo Routes
Tạo file ten-tinh-nang.routes.ts:
typescript
import { Routes } from '@angular/router';
export const FEATURE_ROUTES: Routes = [
{
path: '',
loadComponent: () =>
import('./ten-tinh-nang.component').then(m => m.TenTinhNangComponent),
},
];2c. Đăng ký trong App Routes
Thêm vào src/app/app.routes.ts:
typescript
{
path: 'ten-tinh-nang',
loadChildren: () =>
import('./features/ten-tinh-nang/ten-tinh-nang.routes').then(m => m.FEATURE_ROUTES),
canActivate: [authGuard],
},TIP
Luôn dùng loadComponent / loadChildren cho lazy-loading. Không import component trực tiếp trong routes.
3. Signal State Management
Tạo service quản lý state cho feature:
typescript
@Injectable({ providedIn: 'root' })
export class FeatureService {
// Writable signals
private readonly _items = signal<Item[]>([]);
private readonly _isLoading = signal(false);
private readonly _error = signal<string | null>(null);
// Public read-only
readonly items = this._items.asReadonly();
readonly isLoading = this._isLoading.asReadonly();
readonly error = this._error.asReadonly();
// Computed
readonly hasItems = computed(() => this._items().length > 0);
readonly itemCount = computed(() => this._items().length);
async loadItems(): Promise<void> {
this._isLoading.set(true);
this._error.set(null);
try {
const items = await firstValueFrom(this.api.listItems());
this._items.set(items);
} catch (e) {
this._error.set('Failed to load');
} finally {
this._isLoading.set(false);
}
}
}Pattern: Private writable signals + public readonly + computed derivations.
4. Sử dụng Chart Renderer
html
<app-chart-renderer
[data]="chartData()"
[chartHint]="chartHint()"
[height]="300"
/>- Dùng
<app-chart-renderer>thay vì ngx-echarts trực tiếp - Component tự build ECharts config từ
ChartHint - Hỗ trợ: line, area, bar, grouped_bar, stacked_bar, pie, heatmap
- Export PNG:
chartRenderer.getChartImage()
Source: (src/app/shared/components/chart-renderer.component.ts)
5. Sử dụng Data Table
html
<app-data-table
[data]="tableData()"
[pageSize]="10"
/>- Auto-detect columns từ data keys
- Phân trang: 10/25/50 rows
- Export CSV/Excel built-in
Source: (src/app/shared/components/data-table.component.ts)
6. Thêm Widget cho Dashboard
Khi tạo tính năng cho phép pin dữ liệu vào dashboard:
typescript
// Tạo widget từ artifact
const widget: DashboardWidgetCreateRequest = {
title: 'Widget title',
widget_type: 'chart', // 'chart' | 'table' | 'kpi'
display_size: 'medium', // 'small' | 'medium' | 'large' | 'full'
data_source: 'snapshot', // 'live_insight' | 'snapshot'
snapshot_data: currentData,
snapshot_chart_hint: currentChartHint,
};
await this.dashboardService.createWidget(dashboardId, widget);7. Định nghĩa Types
Luôn định nghĩa interfaces trong src/app/shared/models/api.models.ts hoặc src/app/core/models/:
typescript
// DO: Interface với types rõ ràng
interface MyResponse {
data: DataRow[];
total: number;
}
// DON'T: any
const response: any = await fetch(...); // ❌8. Tailwind CSS Guidelines
- Viết class inline trên template (utility-first)
- Custom colors: xem
src/styles.csscho brand palette - Dark mode: dùng
dark:prefix - Responsive:
sm:,md:,lg:,xl:
9. Demo Mode
Khi thêm API endpoint mới, thêm mock handler trong demo.interceptor.ts:
typescript
// Trong handleRequest()
if (url.includes('/api/v1/your-endpoint') && method === 'GET') {
return of(new HttpResponse({
status: 200,
body: MOCK_YOUR_DATA,
})).pipe(delay(demoDelay('list')));
}10. Commit Convention
feat: add dashboard sharing feature
fix: resolve chart rendering on mobile
chore: update dependencies
docs: update API referenceLiên kết
- Architecture — Kiến trúc hệ thống
- Data Flow — Luồng dữ liệu
- API Reference — API endpoints
- Deployment — Build & deploy