# gbshow Feature — Deep Analysis

**Feature:** `features/gbshow/`
**Role:** Enablement Layer — manages and delivers user engagement content (Banners, Announcements, UI Tours, Feature Tours, Micro Lessons, Checklist Tours, What's New)
**Analyzed:** 2026-02-25
**Components:** 8 components, 2 services, 1 DBService, 1 model file
**Total LOC:** ~2,500 TS + 657 HTML + 1,702 SCSS

---

## Architecture Overview

```
GbShowListComponent          ← Admin list + filter + launch
  └── GbShowComponent        ← Admin creator/editor (5 show types)
        └── GbShowService    ← Business logic + API calls
              └── GbshowlistDbservice  ← HTTP layer

Runtime Player Components (launched via overlay/dialog):
  GbBannerComponent          ← Fixed overlay banner
  GbAnnouncementComponent    ← Modal/inline announcement
  GbFeatureTourComponent     ← Multimedia slideshow with TTS
  GbUiTourComponent          ← Step anchor-based UI tour
  GbMicroLessonComponent     ← Dialog-based lesson player
  GbChecklistTourComponent   ← Draggable checklist widget
  GbWhatsNewComponent        ← Simple "What's New" dialog
```

---

## P0 — Critical Issues (Fix Immediately)

### P0-1: Missing `ChangeDetectionStrategy.OnPush` — 7 of 8 components
Only `GbBannerComponent` has `OnPush`. All others use default CD, meaning Angular re-checks
the entire component tree on every event. For a feature embedded in every page load (shows
fire on login), this is a runtime perf bomb.

**Affected files:**
- `gbshow.component.ts:21` — main editor (585 lines, rich forms, live preview)
- `gbannouncement.component.ts:9` — renders HTML content per step
- `gbfeaturetour.component.ts:27` — 262-line tour player with animations
- `gbuitour.component.ts:16` — calculates DOM positions on every cycle
- `gbchecklisttour.component.ts:11` — draggable widget, always visible
- `gbmicrolesson.component.ts:9` — dialog with TTS state
- `gbwhatsnew.component.ts:8` — simple dialog

---

### P0-2: Dead import from FullCalendar internals — `gbbanner.component.ts:14`
```typescript
import { co } from 'node_modules/@fullcalendar/core/internal-common';
```
`co` is never used. Importing from `node_modules/*/internal-*` paths is a violation of
package contracts — these are private APIs that can break on any minor update. This must
be removed immediately.

---

### P0-3: Untracked `setTimeout` calls — 8 instances across 5 components
None of these handles are stored or cleared in `ngOnDestroy`. If the component is destroyed
while a timer is pending, the callback fires on a dead component, which can cause
`ExpressionChangedAfterItHasBeenChecked` errors, zombie DOM writes, or state corruption.

| Component | Line | Duration | Description |
|-----------|------|----------|-------------|
| `gbshow.component.ts` | 108 | `displayDuration` (default 5000ms) | Preview banner auto-close |
| `gbbanner.component.ts` | 53 | 100ms | Initial animation trigger |
| `gbbanner.component.ts` | 60 | 3000ms | Animation state reset |
| `gbbanner.component.ts` | 86 | 500ms | Banner hide after close |
| `gbmicrolesson.component.ts` | 24 | 1000ms | Initial TTS play |
| `gbmicrolesson.component.ts` | 43 | 1000ms | TTS on nextStep |
| `gbmicrolesson.component.ts` | 52 | 1000ms | TTS on previousStep |
| `gbfeaturetour.component.ts` | 127 | 1500ms | TTS on startTour |
| `gbfeaturetour.component.ts` | 158 | 1500ms | TTS on step change |

Additionally, `GbUiTourComponent` schedules up to **6 recursive retries** at 150ms each
(`gbuitour.component.ts:106`) to locate an anchor element. These are untracked.

---

### P0-4: Raw `.subscribe()` with no cleanup — `gbshow.component.ts:461`
```typescript
this.showservice.saveShow(showData).subscribe({ ... });
```
`GbShowComponent` has no `destroy$`, no `takeUntil`, and no `ngOnDestroy`. If the user
navigates away while a save is in flight, the callback fires on a destroyed component.
Pattern must follow:
```typescript
this.showservice.saveShow(showData)
  .pipe(takeUntil(this.destroy$))
  .subscribe({ ... });
```

---

### P0-5: `setInterval` not cleared on destroy — `gbfeaturetour.component.ts:132`
```typescript
autoSlideInterval: any;
// ...
this.autoSlideInterval = setInterval(() => { ... }, this.intervalDuration / this.selectedSpeed);
```
`GbFeatureTourComponent` has no `ngOnDestroy`. The interval runs indefinitely if the
component is destroyed while slideshow is active. `autoSlide()` calls `clearInterval` at
the start but the final clear on destroy is missing.

---

### P0-6: `alert()` and `confirm()` in production code — 5 instances

| File | Line | Call |
|------|------|------|
| `gbshow.component.ts` | 464 | `alert('Show saved successfully!')` |
| `gbshow.component.ts` | 468 | `alert('Error saving show...')` |
| `gbfeaturetour.component.ts` | 218 | `alert('Video is playing')` |
| `gbshowlist.component.ts` | 302 | `confirm('Are you sure you want to delete...')` |

These block the browser main thread, are not localized, are not styleable, and `alert()`
is blocked in some iframe/sandboxed environments. Replace with `GbDialogBoxComponent`.

---

### P0-7: `console.log` statements in production code — 14+ instances

| File | Lines |
|------|-------|
| `gbshow.component.ts` | 195, 459, 463, 467 |
| `gbbanner.component.ts` | 71, 79 |
| `gbshowlist.component.ts` | 91, 98, 144, 284, 299, 308 |
| `gbshowlist.component.ts` | 421 (console.warn) |

`GbConsoleService` must be used for all logging. The `console.log` in `getCountByStatus()`
(line 284) is called from the template on **every change detection cycle** — extremely expensive.

---

### P0-8: Constructor injection used — must use `inject()`

| File | Injected via constructor |
|------|--------------------------|
| `gbshow.component.ts:48` | `GbShowService` |
| `gbshow.service.ts:20` | `GBHttpService` |
| `gbannouncement.component.ts:26` | `MatDialogRef`, `MAT_DIALOG_DATA` |
| `gbuitour.component.ts:32` | `TourService`, `ElementRef`, `NgZone` |
| `gbchecklisttour.component.ts:26` | `Router` |
| `gbwhatsnew.component.ts:18` | `MatDialogRef`, `MAT_DIALOG_DATA` |
| `gbmicrolesson.component.ts:20` | `MatDialogRef`, `MAT_DIALOG_DATA`, `GbTextToSpeechService` |
| `gbshowlist.component.ts:77` | `GbShowService`, `MatDialog`, `GbShowlistService`, `DomSanitizer` |
| `gbshowlist.dbservice.ts:9` | `GBHttpService` |

---

## P1 — High Priority Issues

### P1-1: Dead / Unused Code

**`gbshow.service.ts:208–280`** — `newcriteria` object (72 lines of hardcoded sample JSON)
is defined inside `saveShow()` but never used. The method correctly passes `criteria` (the
parameter) to the HTTP call. This dead code adds noise and misleads maintainers who might
think it controls what gets sent.

**`gbshow.component.ts:248–316`** — 68-line block of commented-out alternate `showData`
with hardcoded test values. Remove entirely.

**`gbfeaturetour.component.ts:108–116`** — The dropdown tour-switching mechanism is commented
out: `fetchFeatureTours` is never called. `onDropdownChange()` is a functional stub that
does nothing after stopping audio and setting loading=true.

**`gbshowlist.component.ts:312–364`** — 52 lines of 3 different commented-out `openShow()`
implementations. Only the live version at line 365 should remain.

**`gbuitour.component.ts:134`** — Commented `console.debug` left in production file.

---

### P1-2: Pervasive `any` typing — violates TypeScript strict mode

| Location | `any` usage |
|----------|-------------|
| `gbshow.component.ts:206` | `let showData: any = {}` — the entire save payload |
| `gbshow.component.ts:564` | `onBannerContentChanged(event: any)` |
| `gbshow.service.ts:205` | `saveShow(criteria: any): Observable<any>` |
| `gbshow.service.ts:286,291` | `getShows()`, `deleteShow()` return `Observable<any>` |
| `show.models.ts:118` | `SettingsConfig.ShowStepArray: any[]` |
| `gbannouncement.component.ts:19,22,28` | `data: any`, `selectedShow: any`, `dialogData: any` |
| `gbannouncement.component.ts:60,64` | `isContentObject(content: any)`, `onStepButtonClick(btn: any)` |
| `gbfeaturetour.component.ts:65,66,77` | `videoplayer: any`, `dropdown: any`, `autoSlideInterval: any` |
| `gbchecklisttour.component.ts:18` | `steps = signal<any>([])` |
| `gbshowlist.component.ts:51,52,64,68` | `selectedShow: any`, `ShowListData: any[]`, `filteredShows: any[]` |
| `gbmicrolesson.component.ts:16,20` | `lessons: any`, `data: any` |

**Define these interfaces in `show.models.ts`:**
- `ShowSavePayload` — the full save body
- `ShowStep` / `ShowStepDetail` — server response shape
- `ShowListItem` — list view DTO

---

### P1-3: No `ngOnDestroy` where required

`GbShowComponent`, `GbFeatureTourComponent`, `GbBannerComponent`, and `GbMicroLessonComponent`
have timers or subscriptions but **no `ngOnDestroy`**. These resources are never released.

`GbBannerComponent` uses `effect()` in the constructor but does not use `DestroyRef` or
`takeUntilDestroyed`. Effects created without cleanup run even after the component is
destroyed (until GC).

---

### P1-4: Mixed signal/subscribe patterns in `GbChecklistTourComponent`

`currentStepIndex` is initialized as a local `signal(0)` at line 19, then **overwritten**
by assigning the service signal to it in both the `effect()` (line 30) and `ngOnInit`
(line 41):
```typescript
this.currentStepIndex = this.checklistService.currentStep; // replaces local signal ref
```
The original `signal(0)` is discarded. This works coincidentally because both point to the
same service signal after assignment, but the pattern is confusing and the `effect()` block
is doing redundant work that `ngOnInit` already handles. The `effect()` should be removed
in favor of reading `checklistService.isChecklistTourVisible()` directly in the template.

---

### P1-5: `GbShowListComponent.applyFilters()` — O(N×M) search

```typescript
const matchesSearch = !search || Object.values(show).some((value: any) =>
  value?.toString().toLowerCase().includes(search)
);
```
`Object.values(show)` includes `ShowStepArray` which is a deeply nested array. Calling
`.toString()` on it produces `[object Object]` or a long JSON string. For 100 shows × 10
steps each, this runs nested string operations on every keypress. The search should be
restricted to indexed text fields (`ShowName`, `DisplayName`, `Description`, `Tags`).

---

### P1-6: `getCountByStatus()` called from template with console.log — `gbshowlist.component.ts:284`

```typescript
getCountByStatus(statusKey: string): number {
  const count = this.ShowListData.filter(...).length;
  console.log('Status count for', statusKey, ':', count); // fires on every CD cycle
  return count;
}
```
This method scans `ShowListData` and logs to console on **every change detection cycle**.
With 5 status badges and default CD, this runs 5+ times per user interaction. Remove the
log and memoize the counts when data loads.

---

### P1-7: `ShowNature` enum is out of sync with runtime usage

`show.models.ts` defines 5 show types (UITour=1 through Checklist=5).
`GbShowListComponent.showNatureMap` defines 7 types, including `FeatureTour=6` and
`WhatsNew=7` which exist as components but are absent from the enum. Any `switch()` on
`ShowNature` silently ignores these types.

---

### P1-8: `GbWhatsNewComponent` stores preference in `localStorage`

```typescript
localStorage.setItem('hideWhatsNew', 'true');
```
Per project rules: no user data in `localStorage`/`sessionStorage`. Use a user preferences
service or send to the server. This preference is also lost on browser data clear, making
the feature unreliable.

---

### P1-9: Hardcoded TTS voice names

`GbMicroLessonComponent:60`: `'Microsoft Heera - English (India)'`
`GbFeatureTourComponent:240`: `'Microsoft Mark - English (United States)'`

These voice names are Windows-specific and will silently fail on macOS/Linux/Android. The
`GbTextToSpeechService` should resolve available voices and select the best match, or accept
a locale string rather than a platform-specific voice name.

---

### P1-10: MatDialog opened with fixed `width: '600px'` — `gbshowlist.component.ts:373`

```typescript
this.dialog.open(GbAnnouncementComponent, { width: '600px', ... });
```
Per CLAUDE.md: dialog widths must be `min(Xpx, 95vw)` with `maxWidth: '95vw'`. This breaks
on viewports narrower than 600px (mobile, tablet).

---

## P2 — Medium Priority Issues

### P2-1: No i18n — zero Transloco usage across all 8 components

Every user-visible string is hardcoded English: button labels, status text, template names,
tooltip text, sample content. The project targets Arabic users — without RTL CSS and
Transloco integration, this feature is inaccessible to Arabic-locale users.

Hardcoded English examples:
- `gbshow.component.ts:136–169`: `'Happy Birthday!'`, `'Wishing you...'` etc.
- `gbshow.service.ts:122–131`: `'Banner'`, `'Announcement'`, `'UI Tour'`, etc.
- `gbannouncement.component.ts` — all button text
- `gbmicrolesson.component.ts:59` — TTS voice string
- `gbshowlist.component.ts:187–196` — status labels in `getStatusText()`

---

### P2-2: No RTL CSS support in any component

The feature renders in-app overlays and dialogs. Without `[dir="rtl"]` CSS selectors, the
layout breaks entirely for RTL locales. Especially critical for:
- `GbBannerComponent` — fixed positioned overlay with directional positioning
- `GbUiTourComponent` — arrow positioning uses left-biased pixel offsets
- `GbFeatureTourComponent` — slide animations use `translateX(100%)` / `translateX(-100%)`
  which are direction-dependent

---

### P2-3: No `trackBy` on any `*ngFor`

All `*ngFor` loops across the feature create/destroy DOM nodes on every list change.
Affected loops: steps array (announcement, feature tour), show list, checklist items,
template picker, content menu items, speed options.

---

### P2-4: Inconsistent date format in `saveShow()` — `gbshow.component.ts`

- Most dates use `.NET Date` format: `toDotNetDate(now)` → `/Date(1234567890)/`
- Announcement `ExpiryDate` uses ISO 8601: `.toISOString()` → `2025-11-19T06:27:03.507Z`
- `MetaData` for announcement uses `.toISOString()` too

The backend must handle both, or one will fail silently. Pick one format consistently;
ISO 8601 is preferred.

---

### P2-5: `GbFeatureTourComponent` — preloaded images never released

```typescript
preloadedImages: { [key: string]: HTMLImageElement } = {};
```
`HTMLImageElement` objects hold decoded bitmap data. They are accumulated in a plain object
with no eviction. If the tour has many images, or the component is instantiated multiple
times, this grows unboundedly. Clear in `ngOnDestroy`.

---

### P2-6: `GbFeatureTourComponent` — `@HostListener('document:keydown')` fires when invisible

The keyboard handler is active even when the component is minimized (`isMinimized = true`).
The early return prevents action but the event still propagates through Angular's zone.
Consider removing the `@HostListener` when minimized and re-adding on maximize.

---

### P2-7: `GbFeatureTourComponent.pauseVideo()` — debug-only method with `alert()`

```typescript
pauseVideo(vp: any): void {
  vp.nativeElement.pause();
  setTimeout(() => {
    vp.nativeElement.play();
    if (!vp.nativeElement.paused) alert('Video is playing');
  }, 2000);
}
```
This is dead debug code. `pauseVideo` is only called if a `@ViewChild('videoPlayer')`
is passed in, which doesn't happen in the current template. Remove entirely.

---

### P2-8: Naming conventions violated

| Location | Violation |
|----------|-----------|
| `gbshowlist.component.ts:50` | `Newshow: boolean` — PascalCase property (should be `newShow`) |
| `gbshowlist.component.ts:64` | `ShowListData: any[]` — PascalCase (should be `showListData`) |
| `gbchecklisttour.component.ts:55` | `Lightbox()` method — PascalCase (should be `toggleLightbox()`) |

---

### P2-9: `GbChecklistTourComponent` — duplicate import

```typescript
imports: [..., MatIconModule, ..., MatIconModule]  // line 15
```
`MatIconModule` is imported twice. While harmless, it indicates the imports array was
edited without review.

---

### P2-10: `GbAnnouncementComponent` — `OnInit` not declared in `implements`

`ngOnInit()` is implemented (line 39) but `OnInit` is not listed in the `implements` clause.
With strict TS, the interface check provides no compile-time safety without the declaration.

---

## P3 — Low Priority / Improvements

### P3-1: `GbShowService.saveShow()` — hardcoded URL in show template

`getDefaultBannerConfig()` returns `url: 'http://help.goodbookserp.com/?cat=127'` as the
default URL. This is a production URL hardcoded in client-side code. It should be empty
or configurable via environment config.

---

### P3-2: `GbShowComponent.getExpiryDate()` — method is defined but never called

Lines 525–533: `getExpiryDate()` computes a date 30 days in the future as a fallback.
But `saveShow()` uses `toDotNetDate(now)` for `ExpiryDate` and only the Announcement case
overrides it. The method is dead code.

---

### P3-3: `GbUiTourComponent` — global `document.querySelector` for anchor lookup

```typescript
const found = document.querySelector(sel);
```
This searches the entire document. In multi-instance scenarios (e.g., two simultaneous
UI Tours) or pages with nested shadow DOM, this returns the wrong element. Should search
within a scoped container.

---

### P3-4: Magic numbers for `ActionType` in step navigation

`GbAnnouncementComponent:65`: ActionType 1 = Next, 2 = Prev (hardcoded).
`GbShowListComponent:396`: ActionType 1 = Next, 2 = Prev, 6 = Close.
`saveShow()` in `gbshow.component.ts` uses 0 for "Take a Tour" button.

These magic numbers appear in both the save path and the display path with no shared enum.
Define `ActionType` enum in `show.models.ts`.

---

### P3-5: `GbShowComponent` — collapsing state bloat

`showGeneralSettings`, `showPreviewBanner`, `toolsPanelVisible`, `isCollapsed`, `showPreview`
are 5 separate `boolean` properties for UI state. These would be cleaner as signals and
benefit from Angular's signal-based `computed()` for derived states.

---

### P3-6: `MicroLessonConfig` model is incomplete for the player

`GbMicroLessonComponent` reads lessons from `GbMicroLessonService.getAllLessons()` —
which returns static/mock data. The `MicroLessonConfig` saved to the server (title,
content, duration, objectives) is not consumed by the player. The player and creator
use different data shapes with no integration path.

---

## Functional Gaps (Not Yet Implemented)

| Gap | Location | Impact |
|-----|----------|--------|
| **Edit existing show** — `editShow()` is a stub | `gbshowlist.component.ts:298` | Cannot modify any saved show |
| **Delete show** — `deleteShow()` is a stub (no HTTP call, just `console.log`) | `gbshowlist.component.ts:301` | Delete button is non-functional |
| **Toggle status** — stub | `gbshowlist.component.ts:307` | Status cannot be changed from the list |
| **Tour dropdown data fetch** — commented out | `gbfeaturetour.component.ts:108` | Feature tour dropdown does nothing |
| **`saveGeneralSettings()`** — just closes the modal with console.log | `gbshow.component.ts:194` | Settings never saved |
| **Multi-step UI Tour builder** — creates only a single welcome step | `gbshow.component.ts:322` | UI tours can't have multiple anchored steps |
| **Checklist sequential enforcement** — commented out | `gbchecklisttour.component.ts:72` | Steps can be skipped freely |
| **`GbWhatsNew` server-side persistence** — only localStorage | `gbwhatsnew.component.ts:22` | "Don't show again" resets on data clear, not per user account |

---

## Summary Table

| Severity | Count | Key Issues |
|----------|-------|-----------|
| P0 | 8 | Missing OnPush (7 components), dead FullCalendar import, 8+ untracked timers, raw subscribe, interval leak, `alert()`/`confirm()`, console.logs |
| P1 | 10 | Pervasive `any`, constructor injection throughout, dead code blocks (72+ lines), broken signal pattern, O(N×M) search, enum mismatch, localStorage preference, fixed dialog width |
| P2 | 10 | No i18n (zero Transloco), no RTL CSS, no `trackBy`, date format inconsistency, image leak, keyboard listener always active, debug `pauseVideo`, naming violations |
| P3 | 6 | Hardcoded default URL, dead `getExpiryDate()`, global querySelector, magic ActionType numbers, UI state as plain booleans, model/player mismatch |
| Functional | 8 | Edit/delete/status stubs, tour dropdown stub, saveGeneralSettings stub, single-step UI tour only, checklist enforcement disabled |

---

## Recommended Fix Priority

### Immediate (before next deployment)
1. Remove the `@fullcalendar` dead import from `gbbanner.component.ts:14`
2. Remove all `console.log` / `console.error` — replace with `GbConsoleService`
3. Remove all `alert()` / `confirm()` — replace with `GbDialogBoxComponent`
4. Add `ngOnDestroy` + clear all `setTimeout` / `setInterval` handles
5. Add `takeUntil(this.destroy$)` to the `saveShow()` subscribe

### Short-term (next sprint)
6. Add `ChangeDetectionStrategy.OnPush` to all 7 missing components
7. Replace constructor injection with `inject()` across all files
8. Define `ShowSavePayload`, `ShowStep`, `ShowStepDetail`, `ShowListItem` interfaces
9. Remove dead code blocks (~140 lines of commented/unused code)
10. Fix `GbShowListComponent.applyFilters()` search to use indexed fields only
11. Remove `console.log` from `getCountByStatus()`, memoize counts on data load
12. Fix `MatDialog` width to `min(600px, 95vw)` + `maxWidth: '95vw'`
13. Add `ActionType` enum to `show.models.ts`

### Medium-term
14. Integrate Transloco for all display strings
15. Add `[dir="rtl"]` CSS blocks for all layout components
16. Add `trackBy` functions to all `*ngFor` loops
17. Implement `editShow()`, `deleteShow()`, `toggleStatus()` properly
18. Add `ngOnDestroy` to `GbFeatureTourComponent` (clear interval + preloaded images)
19. Standardize date format to ISO 8601 throughout save path
20. Add `ShowNature` enum values for FeatureTour (6) and WhatsNew (7)

### Future
21. Write unit tests for service layer (currently zero test coverage)
22. Make TTS voice selection locale-based, not platform-specific string
23. Implement multi-step UI Tour builder
24. Connect `GbMicroLessonComponent` to server-side lesson data
25. Replace `localStorage` in `GbWhatsNew` with user preferences service
