# Attendance Module — Deep Analysis

**Module path:** `projects/ess/report/attendancecalendar/`, `projects/ess/transaction/attendance/`, `projects/ess/report/monthlyattendance/`
**Date:** 2026-03-04
**Analyst:** Claude Code

---

## Files Analyzed

| File | Lines | Role |
|------|-------|------|
| `projects/ess/report/attendancecalendar/attendancecalendar.component.ts` | ~1247 | Attendance calendar & daily view |
| `projects/ess/service/attendancecalendar.service.ts` | 184 | Calendar service layer |
| `projects/ess/dbservice/attendancecalendar.db.service.ts` | 66 | Calendar HTTP layer |
| `projects/ess/transaction/attendance/essattendanceadjustment/essattendanceadjustment.component.ts` | 366 | Attendance adjustment form |
| `projects/ess/transaction/attendance/permission/permissionrequest/permissionrequest.component.ts` | ~1250 | Permission/on-duty request form |
| `projects/ess/transaction/attendance/permission/permissionrequestlist/permissionrequestlist.component.ts` | 1117 | Permission request list & chart |
| `projects/ess/report/monthlyattendance/monthlyattendance.component.ts` | ~100 | Monthly attendance summary |
| `projects/ess/dbservice/monthlyattendance.db.service.ts` | 41 | Monthly attendance HTTP layer |

---

## Summary Scorecard

| Category | Issues | P0 | P1 |
|----------|--------|----|-----|
| Security | 7 | 5 | 2 |
| Memory Leaks | 14 | 8 | 6 |
| Performance | 9 | 3 | 6 |
| Functional Bugs | 8 | 3 | 5 |
| Code Quality | 18 | 0 | 8 |
| API / Service | 10 | 2 | 8 |

---

## P0 — Critical Issues (Fix Immediately)

### SEC-01: sessionStorage LoginDTO Anti-Pattern — 7 locations

Reading sensitive session data from `sessionStorage` is a P0 security issue (see CLAUDE.md). The `LoginDTO` object contains credentials, user IDs, and period data.

**Locations:**

| File | Line | Context |
|------|------|---------|
| `attendancecalendar.component.ts` | 184 | `ngOnInit` |
| `attendancecalendar.service.ts` | 26 | Constructor |
| `attendancecalendar.service.ts` | 37 | `getDailyAttendanceservice()` |
| `attendancecalendar.service.ts` | 66 | `getAttendanceRegisterservice()` |
| `attendancecalendar.service.ts` | 168 | `getLeaveAllDetailsservice()` |
| `permissionrequestlist.component.ts` | 83 | Constructor |
| `monthlyattendance.component.ts` | 62 | `ngOnInit` |

The service layer is especially problematic: `attendancecalendar.service.ts` reads LoginDTO in the constructor *and* re-reads it in three separate methods. The `refreshLoginDTO()` method exists specifically to re-read sessionStorage on every call — a design that bakes the anti-pattern in permanently.

**Fix:** Replace all `JSON.parse(sessionStorage.getItem('LoginDTO'))` with `inject(GbAppStateService).loginDto()` or the `GbConfigService.getloginDto()` signal already available in the codebase.

---

### MEM-01: `window.addEventListener('storage')` Never Removed

`attendancecalendar.component.ts` `initSelfEmployee()` (line ~271):
```typescript
window.addEventListener('storage', (event) => {
  if (event.key === 'LoginDTO') { ... }
});
```
This listener is added on every `initSelfEmployee()` call and is never removed in `ngOnDestroy`. Each calendar navigation or employee switch accumulates another permanent listener. On a long session, this fires hundreds of handlers per localStorage event.

**Fix:** Store the handler reference in a class field and call `window.removeEventListener` in `ngOnDestroy`, or use `fromEvent(window, 'storage').pipe(takeUntil(this.destroy$))`.

---

### MEM-02: DOM Scroll Listeners Added in ngAfterViewInit Never Removed

`attendancecalendar.component.ts` `ngAfterViewInit` (line ~195):
```typescript
document.querySelectorAll('.scrollable-container').forEach(el => {
  el.addEventListener('scroll', this.onScroll.bind(this));
});
```
`this.onScroll.bind(this)` creates a new function reference each time, making `removeEventListener` impossible. These listeners are never removed in `ngOnDestroy`.

**Fix:** Store handler references and remove in `ngOnDestroy`. Better: use Angular's `@HostListener('scroll', ['$event'])` on the host element, or use a `ResizeObserver` via `fromEvent`.

---

### MEM-03: `Reportdetailservice` Subscription Without `takeUntil`

`permissionrequestlist.component.ts` line ~149:
```typescript
this.service.Reportdetailservice(-1399997529).subscribe((menudetails: any) => { ... });
```
This subscription is not protected with `takeUntil(this.destroy$)`. If the component is destroyed before the response arrives (e.g., user navigates away) the callback runs against a destroyed component, causing `ExpressionChangedAfterItHasBeenChecked` errors or state corruption.

---

### FUNC-01: `updateAttendance()` 3-Level Nested Subscribe Waterfall

`attendancecalendar.component.ts` contains a deeply nested subscribe chain:
```typescript
getPayPeriodList().subscribe(periods => {          // call 1
  getPayPeriodDetails(periodId).subscribe(details => {  // call 2
    updateDailyAttendance(data).subscribe(result => {   // call 3
      // update UI
    });
  });
});
```
This pattern:
- Leaks if component is destroyed between calls (no `takeUntil`)
- Cannot be cancelled
- Is harder to error-handle
- Adds unnecessary sequential latency (calls 2 and 3 could potentially be combined)

**Fix:** Use `switchMap` / `concatMap` with `pipe(takeUntil(this.destroy$))`:
```typescript
this.service.getPayPeriodList(criteria).pipe(
  switchMap(periods => this.service.getPayPeriodDetails(periods[0].Id)),
  switchMap(details => this.service.updateDailyAttendance(postData)),
  takeUntil(this.destroy$)
).subscribe(result => { ... });
```

---

### FUNC-02: Null Dereference Crash in `AdjustmentEntry`

`essattendanceadjustment.component.ts` line ~155:
```typescript
const data = AdjustmentEntryData.responseValue[0];
this.form.get('DailyAttendanceInTime')?.patchValue(data.InTime); // crashes if responseValue is []
```
No guard on `responseValue[0]`. If the API returns an empty array (no attendance record for selected day), the component throws `Cannot read properties of undefined`.

**Fix:**
```typescript
const data = AdjustmentEntryData.responseValue?.[0];
if (!data) return;
```

---

### PERF-01: Missing `ChangeDetectionStrategy.OnPush` — 3 Components

| Component | Notes |
|-----------|-------|
| `AttendanceCalendarComponent` | 1247-line calendar component, re-renders on every parent CD cycle |
| `PermissionRequestComponent` | 1250-line form, no OnPush |
| `PermissionRequestListComponent` | 1117-line list + D3 chart, 12 manual `cdr.detectChanges()` calls |

The absence of OnPush on the calendar component is especially costly — the calendar renders 30+ day cells with attendance data on every application CD cycle, not just when data changes.

---

## P1 — High Priority Issues

### MEM-04: 15+ Untracked `setTimeout` Calls

Across all attendance components, setTimeout handles are not stored and never cleared in `ngOnDestroy`:

| Component | Lines | Count |
|-----------|-------|-------|
| `attendancecalendar.component.ts` | 249, 276, 369, 432, 731, 737, 743, 886, 1230 | 9 |
| `essattendanceadjustment.component.ts` | 54, 76, 79 | 3 |
| `permissionrequest.component.ts` | 149, 240, 264, 273, 852, 1101, 1214 | 7 |
| `permissionrequestlist.component.ts` | 146, 164, 240, 431, 456, 503, 507, 512, 590, 773 | 10 |

Most are short delays (100–300ms) used to work around change detection ordering. With `OnPush` + signals these delays become unnecessary. Until then, each should be tracked:
```typescript
private timers: ReturnType<typeof setTimeout>[] = [];
// ...
this.timers.push(setTimeout(() => { ... }, 100));
ngOnDestroy() { this.timers.forEach(clearTimeout); }
```

---

### MEM-05: `BiztransactionService` Nested Subscribes Without `takeUntil`

`essattendanceadjustment.component.ts` lines 86, 89:
```typescript
this.BiztransactionService(bizClassId).subscribe(data => {
  this.BizTransactionType(url).subscribe(type => { ... }); // inner subscribe leaks
});
```

`permissionrequest.component.ts` lines 435, 440: same pattern.

These are permanent leaks in both components. The inner observable is never cancelled if the outer resolves after the component is destroyed.

---

### MEM-06: `dialogRef.afterClosed()` Not Protected

`permissionrequest.component.ts` `ToTimeForPrintCalculator()` line ~1113:
```typescript
dialogRef.afterClosed().subscribe(result => { ... });
```
No `takeUntil`. If user navigates away while dialog is open, the callback runs on a destroyed component.

---

### FUNC-03: `DailyAttendanceInTime` Patched Twice — Dead Code

`essattendanceadjustment.component.ts`:
```typescript
// line 156
this.form.get('DailyAttendanceInTime')?.patchValue(data.InTime); // raw value

// ... 13 lines later ...
// line 169
this.form.get('DailyAttendanceInTime')?.patchValue(convertedTime); // formatted value
```
The first patch at line 156 is immediately overwritten. The raw value is never used. This is dead code that creates confusion about which value is intended.

---

### FUNC-04: `PermissionSummary` Default Values Hardcoded Wrong

`permissionrequest.component.ts` lines 423-424:
```typescript
BalanceTimes: 2,   // should be 0
BalanceHours: 4    // should be 0
```
New employees with no permission history will see "2 times / 4 hours" as their balance before data loads. This is a display bug — the defaults should be `0` or `null` with a loading indicator.

---

### FUNC-05: `GetEmployeeThumbnail()` Is Dead Code in PermissionRequestList

`permissionrequestlist.component.ts` lines 333-336:
```typescript
private GetEmployeeThumbnail() {
  this.service.GetBizTransaction(url, params).pipe(takeUntil(this.destroy$)).subscribe((response: any) => {
    if (response.responseValue.length != 0) {
      // empty block — nothing is done with the response
    }
  })
}
```
This fires an HTTP call on init, waits for the response, and does nothing. Pure wasted network request.

---

### FUNC-06: `shiftservice()` TIMESLIP Call Result Never Used

`permissionrequest.component.ts` — the third HTTP call in the shift waterfall fetches TIMESLIP data (lines 618-625) but the result handling is entirely commented out. The HTTP call is still made (wasted network + server load), and the result is discarded.

---

### FUNC-07: `ToTimeForPrintCalculator()` Double-Action UX Bug

`permissionrequest.component.ts`: When a dialog is opened via `ToTimeForPrintCalculator()`, the method simultaneously auto-resets the to-time field without user awareness. The user sees a dialog and doesn't know the form has already changed behind it. This is a UX and correctness bug.

---

### FUNC-08: Monthly Attendance Column Definition Bug

`monthlyattendance.component.ts` lines 53-54:
```typescript
{ field: 'MonthlyAttendancePaidDays', label: 'Paid Days', width: 0 },
{ field: 'MonthlyAttendancePaidDays', label: 'Un PayDays', width: 0 }, // SAME field
```
"Un PayDays" column is bound to the same field as "Paid Days". The unpaid days column always shows paid days. The correct field should be `MonthlyAttendanceUnPaidDays` (or equivalent).

---

### PERF-02: O(N×M) Linear Search in `mapAttendanceToCalendar()`

`attendancecalendar.component.ts` line ~658:
```typescript
calendarDays.forEach(day => {
  const record = attendanceRecords.find(r => r.Date === day.date); // O(M) per day
});
```
For a month with 30 days and 300 attendance records, this is 9,000 comparisons. Every calendar render repeats this.

**Fix:** Build a Map first, then do O(1) lookup:
```typescript
const recordMap = new Map(attendanceRecords.map(r => [r.Date, r]));
calendarDays.forEach(day => {
  const record = recordMap.get(day.date); // O(1)
});
```

---

### PERF-03: `openScreenInModal()` Uses sessionStorage for Cross-Component State

`attendancecalendar.component.ts` lines 868-884:
```typescript
sessionStorage.setItem('selectedCalendarDate', date);
sessionStorage.setItem('hideListButton', 'true');
sessionStorage.setItem('selectedEmployeeId', String(empId));
sessionStorage.setItem('selectedEmployeeCode', empCode);
sessionStorage.setItem('selectedEmployeeName', empName);
```
Then the child component reads all 5 keys back from sessionStorage. This is:
1. A P0 security concern (state in sessionStorage)
2. Fragile (stale keys from previous sessions cause wrong state)
3. Leaks keys across tabs

**Fix:** Use `DataPassingService` (already signals-based) or Angular Router `state`:
```typescript
this.router.navigate(['permission-request'], { state: { date, employeeId, ... } });
```

---

### PERF-04: D3 Chart SVG Dimensions Hardcoded

| Component | Hardcoded size |
|-----------|----------------|
| `permissionrequestlist.component.ts` `drawPermissionChart()` | SVG width 500px |
| (leave list counterpart) | SVG width 600px |

The chart does not resize when the browser window changes. On small screens or split-pane layouts the chart overflows. Use `ResizeObserver` + `d3.select(element).node().getBoundingClientRect()` to read actual container dimensions.

---

### API-01: Attendance Calendar Init — 4+ Sequential HTTP Calls That Should Be 1

On component init, `AttendanceCalendarComponent` fires these in sequence:
1. `GET Framework.User.GetUser?UserId=X` — user details + thumbnail
2. `POST /prs/Leave.svc/All/Leave/Details/` — leave summary
3. `POST Ess.Calendar.Post?EmployeeId=X&FromDate=...&ToDate=...` — calendar records
4. `POST /prs/DailyAttendance.svc/AttendanceRegister/...` — detailed daily data

Each waits for the previous to complete. Total wall-clock latency = sum of all 4 RTTs.

**Proposed combined endpoint:**
```
POST /prs/Attendance.svc/AttendanceCalendarContext/
{
  "EmployeeId": 123,
  "FromDate": 1700000000,
  "ToDate": 1702600000,
  "OUId": 456
}
```
Response includes: `{ UserDetail, LeaveBalance, CalendarRecords, DailyAttendance }`

This eliminates 3 sequential round trips, reducing calendar load time significantly.

This same pattern repeats on every employee picklist change — the 4-call waterfall restarts. With a combined endpoint, employee switching becomes a single call.

---

### API-02: Permission Request List Init — 3-Call Sequential Waterfall

On init and on every employee change, `PermissionRequestListComponent` fires:
1. `GET Framework.User.GetUser?UserId=X` — thumbnail + user type
2. `GET /cs/Criteria.svc/List/?ObjectCode=EMPLOYEE` — department/designation
3. `POST /prs/TimeSlip.svc/TimeSlip/` — permission history

Calls 1 and 2 are independent and could be parallelized with `forkJoin`. Call 3 depends on the employee ID from call 1 but can start at the same time if the ID is already known.

**Minimum fix (no backend change):**
```typescript
forkJoin({
  user: this.service.GetBizTransaction('Framework.User.GetUser', '/?UserId=' + id),
  employee: this.service.GetPermissionRequest('/cs/Criteria.svc/...', criteria)
}).pipe(
  switchMap(({ user, employee }) => {
    this.patchUserFields(user);
    this.patchEmployeeFields(employee);
    return this.service.GetPermissionRequest(permissionUrl, permissionCriteria);
  }),
  takeUntil(this.destroy$)
).subscribe(permissions => { this.PermissionReport = permissions.responseValue; });
```

**Proposed combined endpoint (same as API-01 pattern):**
```
POST /prs/Permission.svc/PermissionRequestContext/
{
  "EmployeeId": 123,
  "OUId": 456,
  "FromDate": 1700000000,
  "ToDate": 1702600000
}
```
Returns: `{ UserDetail, EmployeeDetail, PermissionHistory }`

---

### API-03: `Reportdetailservice` Called Twice in PermissionRequestList

`permissionrequestlist.component.ts` calls `this.service.Reportdetailservice(-1399997529)` in:
- `ngOnInit` (line 149)
- `onNavigateToList()` (line 881)

This is the same menu metadata and does not change. Cache the result after first load:
```typescript
private menuDetailCache: any[] | null = null;

private loadMenuDetail(): Observable<any[]> {
  if (this.menuDetailCache) return of(this.menuDetailCache);
  return this.service.Reportdetailservice(-1399997529).pipe(
    tap(result => this.menuDetailCache = result.responseValue)
  );
}
```

---

### API-04: Attendance Adjustment Does Not Reload After Save

`essattendanceadjustment.component.ts` `handleFormResult()`:
- Resets the form on successful save
- Does NOT call the load service again to refresh the displayed data
- User sees empty form but doesn't know if the adjustment persisted correctly

After save, the component should reload the attendance record to confirm persistence.

---

### API-05: Monthly Attendance — Current Period Only, No Navigation

`monthlyattendance.component.ts` always calls `callMonthlyAttendanceService()` with the current login period. There is no way to view historical periods. The table shows "Period" as a column but only ever shows one row.

The `MonthlyAttendanceDbService.MonthlyAttendanceService()` accepts `EmployeeperiodId` but the service hardcodes the criteria — changing the period parameter has no effect because the criteria always comes from `loginDTO.WorkPeriodId`.

---

### CODE-01: `console.log` in Production — 15+ Instances

| File | Lines | Content logged |
|------|-------|----------------|
| `attendancecalendar.db.service.ts` | 12, 18, 19, 24, 25 | URLs, full criteria objects |
| `monthlyattendance.db.service.ts` | 12, 13 | EmployeeId, periodId |
| `monthlyattendance.component.ts` | 63 | Full LoginDTO (sensitive) |
| `permissionrequestlist.component.ts` | 150, 520, 521 | Menu details, date values |

LoginDTO logged at `monthlyattendance.component.ts:63` includes user credentials context — P0 security. Replace all `console.log` with `GbConsoleService`.

---

### CODE-02: Constructor Injection Throughout

All DB services and several components use constructor injection instead of `inject()`:

| File | Pattern |
|------|---------|
| `attendancecalendar.db.service.ts` | `constructor(public http: GBHttpService)` |
| `monthlyattendance.db.service.ts` | `constructor(private http: GBHttpService)` |
| `monthlyattendance.component.ts` | `constructor(..., public MonthlyAttendance: MonthlyAttendanceService)` |
| `permissionrequestlist.component.ts` | `constructor(private configService, private reportviewerdbservice, ...)` |

**Fix:** Migrate to `inject()` pattern per CLAUDE.md.

---

### CODE-03: Magic Module IDs with No Named Constants

Throughout the attendance components, module IDs appear as raw numbers with no documentation:

| Magic Number | Apparent Meaning |
|-------------|-----------------|
| `-1399999915` | ESS module ID |
| `-1399986715` | Permission module (ESS) |
| `-1399977149` | On-duty module |
| `-1399985712` | Unknown variant |
| `-1499999788` | ERP module ID |
| `-1399999728` | Specific client ID |
| `-1399999905` | Entity type (User?) |
| `-1399999975` | BIZ transaction class (Permission) |
| `-1399997529` | Menu/report config ID |

Define these as named constants in a shared module constants file:
```typescript
// attendance.constants.ts
export const ESS_MODULE_ID = -1399999915;
export const PERMISSION_MODULE_ID = -1399986715;
// ...
```

---

### CODE-04: All Types Are `any`

Across all attendance components and services, properties are untyped:
- `LoginDTO: any`
- `PermissionReport: any[]`
- `chartData: any[]`
- `UserDetail: any`
- Service method parameters: `criteria: any`, `params: any`

Define interfaces for all shared data structures. The `ILoginDTO` interface already exists at `projects/gbhost/public/interface/ilogindto.model.ts` — use it consistently. Add:
```typescript
interface IPermissionRecord {
  TimeSlipDate: string;         // '/Date(...)/' format
  TimeSlipDuration: number;     // minutes
  TimeSlipType: 0 | 1;          // 0=OnDuty, 1=Permission
  TimeSlipAttendanceDate: string;
  StatusName: 'Approved' | 'Pending' | 'Rejected';
  Remarks: string;
}

interface IAttendanceCalendarDay {
  date: string;
  status: 'Present' | 'Absent' | 'WeeklyOff' | 'Holiday' | 'Leave';
  inTime?: string;
  outTime?: string;
}
```

---

### CODE-05: ~150 Lines of Commented-Out Code in PermissionRequest

`permissionrequest.component.ts` contains large blocks of commented-out code including:
- `validateShiftTimeRange()` — entire method body commented
- `EmployeeNamFunction()` — entire method commented
- Multiple debug blocks

Commented code should be deleted — version control (git) preserves history.

---

### CODE-06: Validation Message Bug

`essattendanceadjustment.component.ts` line ~217:
```typescript
alertdata.push("In Time* is not 00:00")
```
This message reads as "In Time is not 00:00" — it sounds like a factual statement, not a validation error. Should read: **"In Time should not be 00:00"** or **"Please enter a valid In Time"**.

---

### CODE-07: `LeaveName` Hardcoded String

`essattendanceadjustment.component.ts` line ~182:
```typescript
this.form.get('LeaveName')?.patchValue("NONE");
```
Hardcoded English string, not a Transloco key. If the application is used in other languages or if the business wants to change this label, this requires a code change.

---

### CODE-08: Hardcoded Financial Year 2025/2026

`permissionrequestlist.component.ts` lines 119-122:
```typescript
const financialYearStart = new Date(Date.UTC(2025, 3, 1, 0, 0, 0));
const financialYearEnd = new Date(Date.UTC(2026, 2, 31, 0, 0, 0));
```
This will break in FY 2026/2027. The financial year should be derived from `LoginDTO.WorkPeriodFromDate` / `WorkPeriodToDate` or fetched from the period API.

---

### CODE-09: No RTL (Right-to-Left) CSS

None of the attendance component SCSS files include `[dir="rtl"]` selectors. The application supports Arabic (see CLAUDE.md) but flex direction, margins, and text alignment will be mirrored incorrectly in RTL mode.

---

### CODE-10: Zero Unit Tests

All spec files are empty across the entire attendance module. No unit tests exist for:
- Permission summary calculations
- Date formatting functions (`formatJsonDate`, `convertMinutesToTime`)
- Chart data aggregation logic
- Attendance status mapping

---

## Service Architecture Issues

### `attendancecalendar.service.ts` — Structural Problems

1. **LoginDTO read in constructor AND 3 methods** — constructor stores it in `this.LoginDTODetail`, then individual methods call `JSON.parse(sessionStorage.getItem('LoginDTO'))` again. The `refreshLoginDTO()` method exists to re-read on every call. This was designed to handle user account switching but bakes sessionStorage dependency permanently into the service.

2. **Duplicate HttpClient injection** — the service injects both `private http = inject(HttpClient)` and `private localhttp: HttpClient` (via constructor). Only one is used. Remove the duplicate.

3. **`getAttendanceRegisterservice()` hardcodes EmployeeId from LoginDTO** — managers cannot use this endpoint to view other employees' attendance. The method should accept `employeeId` as a parameter.

### `attendancecalendar.db.service.ts` — Diagnostic `console.log` in Production

Every DB service method logs its URL and criteria:
```typescript
console.log('Attendance Calendar DB URL:', url);       // line 12
console.log('Daily Attendance URL:', URL);              // line 18
console.log('Daily Attendance Criteria:', criteria);    // line 19
console.log("Attendance Register URL:", URL);           // line 24
console.log("Attendance Register Criteria:", criteria); // line 25
```
These are diagnostic logs that expose internal API endpoint structure in browser console. Remove all and use `GbConsoleService` if debug logging is needed.

### `monthlyattendance.db.service.ts` — Constructor Logging

```typescript
console.log("EmployeeId", EmployeeId);      // line 12
console.log("EmployeeperiodId", EmployeeperiodId); // line 13
```
These log parameter values on every monthly attendance request. Remove.

---

## Prioritized Fix List

### Immediate (P0 — Block Release)

1. **[SEC-01]** Remove all `sessionStorage.getItem('LoginDTO')` — use `GbAppStateService` or `GbConfigService` signal
2. **[SEC-01b]** Remove `console.log` of LoginDTO in `monthlyattendance.component.ts:63` — logs sensitive data
3. **[MEM-01]** Fix `window.addEventListener('storage')` leak in `attendancecalendar.component.ts`
4. **[MEM-02]** Fix DOM scroll listeners added in ngAfterViewInit never removed
5. **[FUNC-01]** Refactor 3-level nested subscribe waterfall in `updateAttendance()` to use `switchMap`
6. **[FUNC-02]** Add null guard on `AdjustmentEntryData.responseValue[0]` in adjustment component
7. **[PERF-01]** Add `ChangeDetectionStrategy.OnPush` to all 3 missing components
8. **[CODE-01b]** Remove all production `console.log` — especially in DB services that log URLs and criteria

### High (P1 — Fix Within Sprint)

9. **[MEM-04]** Track all setTimeout handles and clear in ngOnDestroy
10. **[MEM-05]** Add `takeUntil` to all nested subscribes in attendance adjustment and permission request
11. **[FUNC-03]** Remove dead double-patch of `DailyAttendanceInTime`
12. **[FUNC-04]** Fix `PermissionSummary` default values (should be 0, not 2/4)
13. **[FUNC-05]** Remove dead `GetEmployeeThumbnail()` HTTP call in permission list
14. **[FUNC-06]** Remove dead TIMESLIP HTTP call in `shiftservice()` or implement the missing feature
15. **[FUNC-08]** Fix duplicate field binding in monthly attendance `TableColumn` (UnPayDays → correct field)
16. **[PERF-02]** Replace `calendarDays.find()` per record with Map-based O(1) lookup
17. **[PERF-03]** Replace sessionStorage cross-component state with `DataPassingService` or Router state
18. **[API-01]** Propose/implement combined calendar context endpoint to reduce 4 sequential calls to 1
19. **[API-02]** Parallelize user + employee calls with `forkJoin` in permission request list
20. **[API-03]** Cache `Reportdetailservice` result — not re-fetchable data
21. **[CODE-08]** Fix hardcoded 2025/2026 financial year in permission list

### Standard (P2 — Backlog)

22. **[API-04]** Reload attendance data after successful adjustment save
23. **[API-05]** Add period navigation to monthly attendance (currently shows only current period)
24. **[CODE-02]** Migrate constructor injection to `inject()` across all DB services
25. **[CODE-03]** Extract magic numbers to named constants file (`attendance.constants.ts`)
26. **[CODE-04]** Define TypeScript interfaces for all attendance data structures
27. **[CODE-05]** Delete ~150 lines of commented-out code in permission request
28. **[CODE-06]** Fix validation message wording in attendance adjustment
29. **[CODE-07]** Replace hardcoded "NONE" with Transloco key
30. **[CODE-09]** Add RTL CSS selectors to all attendance component SCSS files
31. **[CODE-10]** Write unit tests for date formatting, chart aggregation, and status mapping
32. **[PERF-04]** Make D3 chart dimensions responsive (container-relative, not hardcoded 500px)

---

## Proposed API Optimizations

### New: `POST /prs/Attendance.svc/AttendanceCalendarContext/`

Combines 4 sequential calls into 1. Called on calendar init and employee change.

**Request:**
```json
{
  "EmployeeId": 123,
  "OUId": 456,
  "FromDate": 1700000000,
  "ToDate": 1702600000
}
```

**Response:**
```json
{
  "UserDetail": { "UserThumbNail": "...", "UserPunchType": 0 },
  "LeaveBalance": [ { "LeaveName": "CL", "LeaveBalance": 5, "Opening": 8, "AllotedLeave": 8, "TakenLeave": 3 } ],
  "CalendarRecords": [ { "Date": 1700000000, "Status": "Present", "InTime": "09:00", "OutTime": "18:00" } ],
  "DailyAttendance": [ { "AttendanceDate": "...", "ShiftName": "...", "WorkedHours": "08:00" } ]
}
```

**Impact:** Reduces calendar load from ~4 sequential RTTs to 1.

---

### New: `POST /prs/Permission.svc/PermissionRequestContext/`

Combines 3 sequential calls into 1 for permission request list init and employee change.

**Request:**
```json
{
  "EmployeeId": 123,
  "OUId": 456,
  "FromDate": 1700000000,
  "ToDate": 1702600000
}
```

**Response:**
```json
{
  "UserDetail": { "UserCode": "EMP001", "UserName": "John Doe", "UserThumbNail": "...", "UserPunchType": 0 },
  "EmployeeDetail": { "DepartmentName": "IT", "DesignationName": "Engineer" },
  "PermissionHistory": [ { "TimeSlipDate": "...", "TimeSlipDuration": 120, "TimeSlipType": 1, "StatusName": "Approved" } ]
}
```

**Impact:** Reduces permission list load from 3 sequential RTTs to 1. The employee-change flow (which fires the same 3 calls again) benefits equally.

---

### Existing: Parallelize Independent Calls Immediately (No Backend Change)

Until combined endpoints are available, these pairs of independent calls can be parallelized with `forkJoin`:

```typescript
// In permission request list — user thumbnail + employee details are independent
forkJoin({
  user: this.service.GetBizTransaction('Framework.User.GetUser', '/?UserId=' + id),
  emp: this.service.GetPermissionRequest('/cs/Criteria.svc/...', criteria)
}).pipe(takeUntil(this.destroy$)).subscribe(({ user, emp }) => {
  this.patchUserFields(user.responseValue);
  this.patchEmployeeFields(emp.responseValue);
  this.loadPermissionHistory(id); // can start this in parallel too
});
```

---

## Comparison With Leave Module

The leave module and attendance module share many structural patterns (same codebase era):

| Issue | Leave | Attendance |
|-------|-------|-----------|
| sessionStorage anti-pattern | Yes | Yes (worse — also in service layer) |
| Missing OnPush | Yes | Yes (3 components) |
| Untracked setTimeout | 2 | 15+ |
| Nested subscribes | Yes | Yes |
| Sequential HTTP waterfall | 5-call | 3-4 call |
| console.log in production | Yes | Yes (also in DB services) |
| Magic numbers | Yes | Yes (more) |
| D3 chart hardcoded size | Yes | Yes |
| Commented code | No | Yes (~150 lines) |
| Functional column bug | No | Yes (MonthlyAttendance UnPayDays) |
| sessionStorage cross-component state | No | **Yes — unique issue** |

The attendance module has additional concerns the leave module does not: the sessionStorage-based cross-component state passing (5 keys set/read for navigation), and production `console.log` in DB services (logging API URLs and full request criteria).
