front-end-dev-standards 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,234 +0,0 @@
1
- # Angular Standards
2
-
3
- > Company-wide Angular conventions for AI-assisted and human-written code.
4
- > Target: Angular 20+ (standalone-first, signals-first).
5
-
6
- ---
7
-
8
- ## Core Principles
9
-
10
- 1. **Standalone only** — no `NgModule` for new features.
11
- 2. **Signals first** — prefer `signal()`, `computed()`, `effect()` over RxJS for local state.
12
- 3. **Functional DI** — use `inject()` instead of constructor injection.
13
- 4. **OnPush everywhere** — default change detection strategy for all components.
14
- 5. **Typed everything** — strict TypeScript, typed reactive forms, typed HTTP responses.
15
-
16
- ---
17
-
18
- ## Components
19
-
20
- ### Required Pattern
21
-
22
- ```typescript
23
- import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
24
- import { EmployeeService } from '../services/employee.service';
25
- import { Employee } from '../models/employee.model';
26
-
27
- @Component({
28
- selector: 'app-employee-list',
29
- imports: [/* standalone imports only */],
30
- templateUrl: './employee-list.component.html',
31
- styleUrl: './employee-list.component.scss',
32
- changeDetection: ChangeDetectionStrategy.OnPush,
33
- })
34
- export class EmployeeListComponent {
35
- private readonly employeeService = inject(EmployeeService);
36
-
37
- readonly employees = signal<Employee[]>([]);
38
- readonly loading = signal(false);
39
- readonly error = signal<string | null>(null);
40
- }
41
- ```
42
-
43
- ### Rules
44
-
45
- | Do | Don't |
46
- |---|---|
47
- | Standalone components | `NgModule`-based components |
48
- | `inject()` for DI | Constructor injection |
49
- | `signal()` / `computed()` for state | `BehaviorSubject` for simple UI state |
50
- | `ChangeDetectionStrategy.OnPush` | Default change detection |
51
- | `input()` / `output()` signal APIs | `@Input()` / `@Output()` decorators (legacy) |
52
- | `host: { class: '...' }` or `:host` in SCSS | Inline styles unless dynamic |
53
- | Separate `.html`, `.scss`, `.ts` files | Inline templates for complex UI |
54
-
55
- ### File Naming
56
-
57
- ```
58
- employee-list.component.ts
59
- employee-list.component.html
60
- employee-list.component.scss
61
- employee-list.component.spec.ts
62
- ```
63
-
64
- Selector prefix: `app-` (application) or feature prefix e.g. `hr-` for HRMS.
65
-
66
- ---
67
-
68
- ## Services
69
-
70
- ```typescript
71
- import { Injectable, inject } from '@angular/core';
72
- import { HttpClient } from '@angular/common/http';
73
- import { Observable } from 'rxjs';
74
- import { Employee } from '../models/employee.model';
75
-
76
- @Injectable({ providedIn: 'root' })
77
- export class EmployeeService {
78
- private readonly http = inject(HttpClient);
79
- private readonly baseUrl = '/api/employees';
80
-
81
- getAll(): Observable<Employee[]> {
82
- return this.http.get<Employee[]>(this.baseUrl);
83
- }
84
- }
85
- ```
86
-
87
- ### Rules
88
-
89
- - Use `providedIn: 'root'` unless scoped to a route or component.
90
- - Return typed `Observable<T>` or use `httpResource()` for signal-based HTTP (Angular 19+).
91
- - Keep services thin — business logic belongs in dedicated use-case/domain services when complex.
92
- - No `HttpClient` calls directly from components.
93
-
94
- ---
95
-
96
- ## Routing
97
-
98
- ```typescript
99
- import { Routes } from '@angular/router';
100
-
101
- export const EMPLOYEE_ROUTES: Routes = [
102
- {
103
- path: '',
104
- loadComponent: () =>
105
- import('./pages/employee-list/employee-list.page').then(m => m.EmployeeListPage),
106
- },
107
- {
108
- path: ':id',
109
- loadComponent: () =>
110
- import('./pages/employee-detail/employee-detail.page').then(m => m.EmployeeDetailPage),
111
- },
112
- ];
113
- ```
114
-
115
- ### Rules
116
-
117
- - Lazy-load feature routes with `loadComponent`.
118
- - Use functional guards: `canActivate: [authGuard]`.
119
- - Route data and params via `input()` bindings where supported.
120
- - One `*.routes.ts` file per feature.
121
-
122
- ---
123
-
124
- ## Forms
125
-
126
- Use **Typed Reactive Forms** exclusively for non-trivial forms.
127
-
128
- ```typescript
129
- import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
130
-
131
- interface EmployeeForm {
132
- firstName: FormControl<string>;
133
- lastName: FormControl<string>;
134
- email: FormControl<string>;
135
- }
136
-
137
- @Component({
138
- imports: [ReactiveFormsModule],
139
- changeDetection: ChangeDetectionStrategy.OnPush,
140
- })
141
- export class EmployeeFormComponent {
142
- readonly form = new FormGroup<EmployeeForm>({
143
- firstName: new FormControl('', { nonNullable: true, validators: [Validators.required] }),
144
- lastName: new FormControl('', { nonNullable: true, validators: [Validators.required] }),
145
- email: new FormControl('', { nonNullable: true, validators: [Validators.required, Validators.email] }),
146
- });
147
- }
148
- ```
149
-
150
- ### Rules
151
-
152
- | Do | Don't |
153
- |---|---|
154
- | Typed `FormGroup<T>` | Untyped forms |
155
- | `nonNullable: true` on controls | Nullable controls without reason |
156
- | Reactive forms for complex UI | Template-driven forms for multi-field forms |
157
- | Angular Material form controls | Custom unstyled inputs in enterprise apps |
158
-
159
- ---
160
-
161
- ## State Management
162
-
163
- **Default:** component-level signals + services.
164
-
165
- **Use NgRx / SignalStore when:**
166
-
167
- - State is shared across multiple features.
168
- - Complex async orchestration is required.
169
- - Time-travel debugging or audit trails are needed.
170
-
171
- ```typescript
172
- import { signalStore, withState, withMethods } from '@ngrx/signals';
173
-
174
- export const EmployeeStore = signalStore(
175
- { providedIn: 'root' },
176
- withState({ employees: [] as Employee[], loading: false }),
177
- withMethods(/* ... */),
178
- );
179
- ```
180
-
181
- ---
182
-
183
- ## HTTP & Error Handling
184
-
185
- ```typescript
186
- // Interceptor pattern — centralized error handling
187
- export const errorInterceptor: HttpInterceptorFn = (req, next) => {
188
- return next(req).pipe(
189
- catchError((error: HttpErrorResponse) => {
190
- // log, toast, rethrow
191
- return throwError(() => error);
192
- }),
193
- );
194
- };
195
- ```
196
-
197
- - Always type HTTP responses: `http.get<Employee[]>()`.
198
- - Use functional interceptors (`HttpInterceptorFn`).
199
- - Display user-friendly errors via a shared `NotificationService`.
200
-
201
- ---
202
-
203
- ## Angular Material
204
-
205
- - Use Material 3 theming (`@angular/material`).
206
- - Prefer CDK utilities (Overlay, A11y, DragDrop) over third-party equivalents.
207
- - Wrap Material components in feature-specific wrappers when repeated patterns emerge.
208
-
209
- ```typescript
210
- import { MatButtonModule } from '@angular/material/button';
211
- import { MatTableModule } from '@angular/material/table';
212
- ```
213
-
214
- ---
215
-
216
- ## Performance
217
-
218
- - `track` in `@for` loops: `@for (item of items(); track item.id)`.
219
- - Avoid unnecessary subscriptions — use `async` pipe or signals.
220
- - Lazy load routes and heavy components.
221
- - Use `NgOptimizedImage` for static images.
222
-
223
- ---
224
-
225
- ## Anti-Patterns (Never Generate)
226
-
227
- - ❌ `NgModule` declarations for new code
228
- - ❌ Constructor-based dependency injection
229
- - ❌ `BehaviorSubject` for simple component state
230
- - ❌ `any` type without explicit justification
231
- - ❌ Direct DOM manipulation (`document.querySelector`)
232
- - ❌ Business logic inside components (beyond UI orchestration)
233
- - ❌ Unsubscribed manual subscriptions
234
- - ❌ Default change detection on presentational components
@@ -1,263 +0,0 @@
1
- # Architecture Standards
2
-
3
- > Feature-based, domain-driven frontend architecture for enterprise Angular applications.
4
-
5
- ---
6
-
7
- ## High-Level Structure
8
-
9
- ```
10
- src/
11
- ├── app/
12
- │ ├── core/ # Singleton services, guards, interceptors (app-wide)
13
- │ │ ├── auth/
14
- │ │ ├── interceptors/
15
- │ │ ├── guards/
16
- │ │ └── services/
17
- │ ├── shared/ # Reusable UI components, pipes, directives
18
- │ │ ├── components/
19
- │ │ ├── directives/
20
- │ │ ├── pipes/
21
- │ │ └── models/
22
- │ ├── features/ # Business feature modules (lazy-loaded)
23
- │ │ ├── employees/
24
- │ │ │ ├── components/
25
- │ │ │ ├── pages/
26
- │ │ │ ├── services/
27
- │ │ │ ├── models/
28
- │ │ │ ├── employees.routes.ts
29
- │ │ │ └── index.ts
30
- │ │ └── payroll/
31
- │ ├── layout/ # Shell, header, sidebar, footer
32
- │ ├── app.config.ts
33
- │ ├── app.routes.ts
34
- │ └── app.ts
35
- ├── environments/
36
- └── styles/
37
- ├── _variables.scss
38
- ├── _mixins.scss
39
- └── styles.scss
40
- ```
41
-
42
- ---
43
-
44
- ## Layer Responsibilities
45
-
46
- ### Core Layer
47
-
48
- **Purpose:** Application-wide infrastructure used once.
49
-
50
- | Contains | Does NOT contain |
51
- |---|---|
52
- | Auth service & guards | Feature-specific business logic |
53
- | HTTP interceptors | UI components |
54
- | Global error handler | Feature models |
55
- | App initializer | |
56
-
57
- ```
58
- core/
59
- auth/
60
- auth.service.ts
61
- auth.guard.ts
62
- interceptors/
63
- auth.interceptor.ts
64
- error.interceptor.ts
65
- services/
66
- notification.service.ts
67
- ```
68
-
69
- ### Shared Layer
70
-
71
- **Purpose:** Reusable, presentation-focused building blocks with no feature coupling.
72
-
73
- ```
74
- shared/
75
- components/
76
- confirm-dialog/
77
- data-table/
78
- page-header/
79
- models/
80
- api-response.model.ts
81
- pagination.model.ts
82
- ```
83
-
84
- **Rules:**
85
-
86
- - Shared components must not import from `features/`.
87
- - Inputs/outputs only — no direct service calls to feature services.
88
- - Document inputs with JSDoc when non-obvious.
89
-
90
- ### Features Layer
91
-
92
- **Purpose:** Self-contained business capabilities.
93
-
94
- Each feature is a vertical slice:
95
-
96
- ```
97
- features/employees/
98
- components/ # Feature-specific presentational components
99
- employee-card/
100
- employee-form/
101
- pages/ # Routable smart components (containers)
102
- employee-list/
103
- employee-detail/
104
- services/ # Feature API & state services
105
- employee.service.ts
106
- models/ # Feature domain types
107
- employee.model.ts
108
- employee-filter.model.ts
109
- employees.routes.ts # Feature routing
110
- index.ts # Public API barrel (optional)
111
- ```
112
-
113
- ---
114
-
115
- ## Smart vs Presentational Components
116
-
117
- ### Presentational (Dumb)
118
-
119
- - Receives data via `input()`.
120
- - Emits events via `output()`.
121
- - No injected feature services.
122
- - OnPush change detection.
123
-
124
- ```typescript
125
- @Component({
126
- selector: 'app-employee-card',
127
- changeDetection: ChangeDetectionStrategy.OnPush,
128
- })
129
- export class EmployeeCardComponent {
130
- readonly employee = input.required<Employee>();
131
- readonly selected = output<Employee>();
132
- }
133
- ```
134
-
135
- ### Smart (Container / Page)
136
-
137
- - Injects services.
138
- - Manages state with signals.
139
- - Passes data down to presentational components.
140
-
141
- ```typescript
142
- @Component({
143
- selector: 'app-employee-list-page',
144
- changeDetection: ChangeDetectionStrategy.OnPush,
145
- })
146
- export class EmployeeListPage {
147
- private readonly employeeService = inject(EmployeeService);
148
- readonly employees = signal<Employee[]>([]);
149
- }
150
- ```
151
-
152
- **Naming:** routable components use `.page.ts` suffix or live in `pages/` folder.
153
-
154
- ---
155
-
156
- ## Data Flow
157
-
158
- ```
159
- ┌─────────────┐ HTTP/API ┌─────────────┐
160
- │ Service │ ◄──────────────► │ Backend │
161
- └──────┬──────┘ └─────────────┘
162
- │ signal / observable
163
-
164
- ┌─────────────┐ inputs/outputs ┌──────────────────┐
165
- │ Page/Smart │ ────────────────► │ Presentational │
166
- │ Component │ │ Component │
167
- └─────────────┘ └──────────────────┘
168
- ```
169
-
170
- - Unidirectional data flow.
171
- - Pages orchestrate; components render.
172
- - No two sibling components communicating directly — go through parent or a store.
173
-
174
- ---
175
-
176
- ## API Integration
177
-
178
- ### Model Conventions
179
-
180
- ```typescript
181
- /** Domain entity returned by GET /api/employees */
182
- export interface Employee {
183
- id: string;
184
- firstName: string;
185
- lastName: string;
186
- email: string;
187
- departmentId: string;
188
- createdAt: string; // ISO 8601 from API; map to Date in mappers if needed
189
- }
190
-
191
- /** Payload for POST /api/employees */
192
- export interface CreateEmployeeRequest {
193
- firstName: string;
194
- lastName: string;
195
- email: string;
196
- departmentId: string;
197
- }
198
- ```
199
-
200
- - Separate **entity**, **request DTO**, and **response wrapper** types.
201
- - Map API shapes to view models in services when UI needs differ from API.
202
-
203
- ### Environment Configuration
204
-
205
- ```typescript
206
- // environments/environment.ts
207
- export const environment = {
208
- production: false,
209
- apiBaseUrl: 'http://localhost:8080/api',
210
- };
211
- ```
212
-
213
- Never hardcode API URLs in services.
214
-
215
- ---
216
-
217
- ## Cross-Cutting Concerns
218
-
219
- | Concern | Location |
220
- |---|---|
221
- | Authentication | `core/auth/` |
222
- | Authorization guards | `core/guards/` |
223
- | HTTP headers / tokens | `core/interceptors/auth.interceptor.ts` |
224
- | Global errors | `core/interceptors/error.interceptor.ts` + `NotificationService` |
225
- | Logging | `core/services/logger.service.ts` |
226
- | Feature flags | `core/services/feature-flag.service.ts` |
227
-
228
- ---
229
-
230
- ## Barrel Exports
231
-
232
- Use `index.ts` sparingly at feature boundaries only:
233
-
234
- ```typescript
235
- // features/employees/index.ts
236
- export { EMPLOYEE_ROUTES } from './employees.routes';
237
- export type { Employee } from './models/employee.model';
238
- ```
239
-
240
- Do not create deep barrel files that cause circular dependencies.
241
-
242
- ---
243
-
244
- ## Dependency Rules
245
-
246
- ```
247
- features/ ──► shared/ ──► (no upward imports)
248
- │ │
249
- └──────► core/ ◄──────┘
250
-
251
- features/ ──X──► other features/ (use shared events, routing, or stores instead)
252
- ```
253
-
254
- Enforce with ESLint boundary rules when available.
255
-
256
- ---
257
-
258
- ## Scalability Guidelines
259
-
260
- 1. **New feature = new folder** under `features/` with its own routes.
261
- 2. **Promote to shared** only after reuse in 2+ features.
262
- 3. **Split features** when a folder exceeds ~15 components or multiple unrelated domains.
263
- 4. **Co-locate tests** next to source files (`*.spec.ts`).
@@ -1,223 +0,0 @@
1
- # Coding Style Standards
2
-
3
- > TypeScript and Angular style conventions for consistent, review-friendly code.
4
-
5
- ---
6
-
7
- ## TypeScript
8
-
9
- ### Strict Mode
10
-
11
- Always enable strict compiler options:
12
-
13
- ```json
14
- {
15
- "compilerOptions": {
16
- "strict": true,
17
- "noImplicitOverride": true,
18
- "noPropertyAccessFromIndexSignature": true,
19
- "noImplicitReturns": true,
20
- "noFallthroughCasesInSwitch": true
21
- }
22
- }
23
- ```
24
-
25
- ### Type Rules
26
-
27
- | Rule | Example |
28
- |---|---|
29
- | Prefer `interface` for object shapes | `interface Employee { id: string }` |
30
- | Use `type` for unions/intersections | `type Status = 'active' \| 'inactive'` |
31
- | Never use `any` | Use `unknown` + type guards |
32
- | Explicit return types on public methods | `getEmployee(id: string): Observable<Employee>` |
33
- | Use `readonly` for immutable properties | `readonly id = input.required<string>()` |
34
- | Use `const` by default | `const items = signal<T[]>([])` |
35
-
36
- ### Naming Conventions
37
-
38
- | Element | Convention | Example |
39
- |---|---|---|
40
- | Classes | PascalCase | `EmployeeService` |
41
- | Interfaces | PascalCase (no `I` prefix) | `Employee`, not `IEmployee` |
42
- | Files | kebab-case | `employee-list.component.ts` |
43
- | Variables / functions | camelCase | `getEmployeeById` |
44
- | Constants | SCREAMING_SNAKE_CASE | `MAX_RETRY_COUNT` |
45
- | Signals | noun or adjective | `loading`, `employees`, `isValid` |
46
- | Boolean signals | `is/has/can` prefix | `isLoading`, `hasError` |
47
- | Observables | `$` suffix (when used) | `employees$` |
48
- | Private fields | `private readonly` | `private readonly http = inject(HttpClient)` |
49
-
50
- ---
51
-
52
- ## Angular Style
53
-
54
- ### Import Order
55
-
56
- 1. Angular core/common
57
- 2. Angular feature modules (router, forms, material)
58
- 3. RxJS
59
- 4. Third-party libraries
60
- 5. Application core
61
- 6. Application shared
62
- 7. Feature-relative imports
63
-
64
- ```typescript
65
- import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
66
- import { RouterLink } from '@angular/router';
67
- import { MatButtonModule } from '@angular/material/button';
68
-
69
- import { EmployeeService } from '../../services/employee.service';
70
- import { Employee } from '../../models/employee.model';
71
- ```
72
-
73
- ### Member Order (inside classes)
74
-
75
- 1. `inject()` / DI fields
76
- 2. `input()` / `output()` declarations
77
- 3. Public signals / computed
78
- 4. Protected template-facing members
79
- 5. Constructor (avoid — use inject)
80
- 6. Lifecycle hooks (alphabetical)
81
- 7. Public methods
82
- 8. Private methods
83
-
84
- ### Template Style
85
-
86
- ```html
87
- <!-- Use native control flow -->
88
- @if (loading()) {
89
- <app-loading-spinner />
90
- } @else if (error(); as message) {
91
- <app-error-banner [message]="message" />
92
- } @else {
93
- <ul>
94
- @for (employee of employees(); track employee.id) {
95
- <li>{{ employee.firstName }} {{ employee.lastName }}</li>
96
- } @empty {
97
- <li>No employees found.</li>
98
- }
99
- </ul>
100
- }
101
- ```
102
-
103
- | Do | Don't |
104
- |---|---|
105
- | `@if`, `@for`, `@switch` | `*ngIf`, `*ngFor`, `*ngSwitch` |
106
- | `track` in `@for` | `@for` without track |
107
- | Async pipe or signals in template | Manual subscribe + field assignment |
108
- | `[class.active]="isActive()"` | `[ngClass]` for simple cases |
109
-
110
- ### SCSS Style
111
-
112
- - Use component-scoped styles (`styleUrl`).
113
- - Use design tokens / variables from `styles/_variables.scss`.
114
- - BEM-like naming for complex components: `.employee-card__title`.
115
- - No `!important` unless overriding third-party styles.
116
-
117
- ```scss
118
- :host {
119
- display: block;
120
- }
121
-
122
- .employee-card {
123
- &__title {
124
- font-weight: 600;
125
- }
126
- }
127
- ```
128
-
129
- ---
130
-
131
- ## Comments & Documentation
132
-
133
- ### When to Comment
134
-
135
- - **Do** explain *why* for non-obvious business rules.
136
- - **Do** use JSDoc on public service methods and shared component inputs.
137
- - **Don't** comment obvious code.
138
-
139
- ```typescript
140
- /**
141
- * Fetches employees filtered by department.
142
- * API returns inactive employees unless `includeInactive` is true.
143
- */
144
- getByDepartment(departmentId: string, includeInactive = false): Observable<Employee[]> {
145
- // ...
146
- }
147
- ```
148
-
149
- ### TODO Format
150
-
151
- ```typescript
152
- // TODO(HRMS-1234): Replace mock data with API call after backend deploy
153
- ```
154
-
155
- ---
156
-
157
- ## Error Handling
158
-
159
- ```typescript
160
- async loadEmployees(): Promise<void> {
161
- this.loading.set(true);
162
- this.error.set(null);
163
-
164
- try {
165
- const data = await firstValueFrom(this.employeeService.getAll());
166
- this.employees.set(data);
167
- } catch (err) {
168
- this.error.set('Failed to load employees. Please try again.');
169
- console.error('[EmployeeListPage]', err);
170
- } finally {
171
- this.loading.set(false);
172
- }
173
- }
174
- ```
175
-
176
- - Always handle errors at the page/smart component level.
177
- - Log with context prefix: `[ClassName]`.
178
- - Never swallow errors silently.
179
-
180
- ---
181
-
182
- ## Formatting
183
-
184
- Use Prettier with project defaults:
185
-
186
- ```json
187
- {
188
- "printWidth": 100,
189
- "singleQuote": true,
190
- "trailingComma": "all",
191
- "tabWidth": 2
192
- }
193
- ```
194
-
195
- Run `prettier --write .` before committing.
196
-
197
- ---
198
-
199
- ## Git Commit Messages
200
-
201
- Follow Conventional Commits:
202
-
203
- ```
204
- feat(employees): add employee list page with pagination
205
- fix(auth): redirect to login on 401 response
206
- refactor(shared): extract confirm dialog to shared components
207
- test(employees): add unit tests for EmployeeService
208
- docs: update architecture standards
209
- ```
210
-
211
- ---
212
-
213
- ## Code Review Checklist
214
-
215
- - [ ] Standalone component with explicit `imports`
216
- - [ ] OnPush change detection
217
- - [ ] `inject()` instead of constructor DI
218
- - [ ] Signals for local state
219
- - [ ] Typed forms / typed HTTP
220
- - [ ] No `any` types
221
- - [ ] Tests for services and complex components
222
- - [ ] No hardcoded URLs or secrets
223
- - [ ] Accessibility: labels, ARIA, keyboard navigation