# Feedback & Change Request System - Implementation Plan

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**Goal:** Build an in-app feedback system with AI-powered report generation that lets users submit bugs, change requests, improvements, and feature requests with element targeting and screenshot annotation.

**Architecture:** Standalone Angular components in `src/app/features/feedback/`, no NgRx (localStorage only), services for AI (DeepSeek/ChatGPT), element inspection, and screenshot capture. FAB button added to main layout.

**Tech Stack:** Angular 21, PrimeNG 21, html2canvas, DeepSeek API, OpenAI API, Canvas 2D for annotations, localStorage for persistence.

---

### Task 1: Install html2canvas and Create Data Models

**Files:**
- Modify: `package.json` (add html2canvas dependency)
- Create: `src/app/features/feedback/models/feedback.model.ts`

**Step 1: Install html2canvas**

Run: `cd /home/moonui/public_html/moon-erp && npm install html2canvas`

**Step 2: Create the feedback data models**

Create `src/app/features/feedback/models/feedback.model.ts`:

```typescript
export type FeedbackType = 'bug' | 'change_request' | 'improvement' | 'feature_request';

export type ChangeType =
  | 'design_change'
  | 'show_element'
  | 'hide_element'
  | 'add_field'
  | 'remove_field'
  | 'reorder_fields'
  | 'change_permissions'
  | 'change_validation'
  | 'change_label'
  | 'change_flow';

export type Priority = 'high' | 'medium' | 'low';
export type AiProvider = 'deepseek' | 'chatgpt';
export type AnnotationTool = 'arrow' | 'rectangle' | 'circle' | 'text' | 'freehand';
export type AnnotationColor = '#ef4444' | '#22c55e' | '#3b82f6' | '#eab308';

export interface TargetElement {
  tag: string;
  id: string;
  classes: string[];
  text: string;
  css_path: string;
  rect: { x: number; y: number; width: number; height: number };
}

export interface FeedbackScreenshot {
  data_url: string;
  annotated_url?: string;
  source: 'auto' | 'upload';
}

export interface FeedbackReport {
  id: string;
  type: FeedbackType;
  status: 'draft' | 'submitted';

  // Auto-captured
  page_url: string;
  screen_name: string;
  module: string;

  // Type-specific
  change_type?: ChangeType;
  description: string;
  as_is?: string;
  to_be?: string;
  why?: string;
  steps_to_reproduce?: string;
  expected_result?: string;
  actual_result?: string;
  priority?: Priority;
  template_type?: string;

  // Targets
  target_elements: TargetElement[];

  // Visual
  screenshots: FeedbackScreenshot[];

  // AI
  ai_report?: string;
  ai_provider?: AiProvider;

  // Meta
  created_at: string;
  user_name?: string;
}

export interface AiReportRequest {
  type: FeedbackType;
  description: string;
  page_url: string;
  screen_name: string;
  module: string;
  change_type?: ChangeType;
  as_is?: string;
  to_be?: string;
  why?: string;
  steps_to_reproduce?: string;
  expected_result?: string;
  actual_result?: string;
  priority?: Priority;
  target_elements: TargetElement[];
  screenshot_base64?: string;
}

export interface Annotation {
  id: string;
  tool: AnnotationTool;
  color: AnnotationColor;
  points: { x: number; y: number }[];
  text?: string;
  width?: number;
  height?: number;
}
```

**Step 3: Commit**

```bash
git add package.json package-lock.json src/app/features/feedback/models/feedback.model.ts
git commit -m "feat(feedback): add html2canvas dependency and data models"
```

---

### Task 2: Create Feedback Storage Service (localStorage)

**Files:**
- Create: `src/app/features/feedback/services/feedback-storage.service.ts`

**Step 1: Create the service**

Create `src/app/features/feedback/services/feedback-storage.service.ts`:

```typescript
import { Injectable, signal, computed } from '@angular/core';
import { FeedbackReport } from '../models/feedback.model';

@Injectable({ providedIn: 'root' })
export class FeedbackStorageService {
  private readonly STORAGE_KEY = 'moon-feedback-reports';
  private readonly MAX_REPORTS = 50;

  reports = signal<FeedbackReport[]>([]);
  count = computed(() => this.reports().length);

  constructor() {
    this.loadFromStorage();
  }

  private loadFromStorage(): void {
    try {
      const stored = localStorage.getItem(this.STORAGE_KEY);
      if (stored) {
        this.reports.set(JSON.parse(stored));
      }
    } catch {
      this.reports.set([]);
    }
  }

  private saveToStorage(): void {
    try {
      localStorage.setItem(this.STORAGE_KEY, JSON.stringify(this.reports()));
    } catch {
      // localStorage full — remove oldest reports
      const trimmed = this.reports().slice(-Math.floor(this.MAX_REPORTS / 2));
      this.reports.set(trimmed);
      localStorage.setItem(this.STORAGE_KEY, JSON.stringify(trimmed));
    }
  }

  save(report: FeedbackReport): void {
    this.reports.update(list => {
      const updated = [report, ...list.filter(r => r.id !== report.id)];
      return updated.slice(0, this.MAX_REPORTS);
    });
    this.saveToStorage();
  }

  getById(id: string): FeedbackReport | undefined {
    return this.reports().find(r => r.id === id);
  }

  delete(id: string): void {
    this.reports.update(list => list.filter(r => r.id !== id));
    this.saveToStorage();
  }

  clearAll(): void {
    this.reports.set([]);
    localStorage.removeItem(this.STORAGE_KEY);
  }

  generateId(): string {
    return crypto.randomUUID();
  }
}
```

**Step 2: Commit**

```bash
git add src/app/features/feedback/services/feedback-storage.service.ts
git commit -m "feat(feedback): add localStorage-based feedback storage service"
```

---

### Task 3: Create AI Report Service (DeepSeek + ChatGPT)

**Files:**
- Create: `src/app/features/feedback/services/ai-report.service.ts`

**Step 1: Create the AI report service**

Create `src/app/features/feedback/services/ai-report.service.ts`:

```typescript
import { Injectable, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { inject } from '@angular/core';
import { Observable, of, catchError, switchMap, map } from 'rxjs';
import { AiProvider, AiReportRequest, FeedbackType } from '../models/feedback.model';

interface AiConfig {
  provider: AiProvider;
  deepseekKey: string;
  chatgptKey: string;
}

@Injectable({ providedIn: 'root' })
export class AiReportService {
  private http = inject(HttpClient);

  private readonly SETTINGS_KEY = 'moon-feedback-ai-config';
  private readonly DEEPSEEK_URL = 'https://api.deepseek.com/v1/chat/completions';
  private readonly OPENAI_URL = 'https://api.openai.com/v1/chat/completions';

  generating = signal(false);

  getConfig(): AiConfig {
    try {
      const stored = localStorage.getItem(this.SETTINGS_KEY);
      if (stored) return JSON.parse(stored);
    } catch {}
    return {
      provider: 'deepseek',
      deepseekKey: '',
      chatgptKey: '',
    };
  }

  saveConfig(config: AiConfig): void {
    localStorage.setItem(this.SETTINGS_KEY, JSON.stringify(config));
  }

  generateReport(request: AiReportRequest): Observable<{ report: string; provider: AiProvider }> {
    this.generating.set(true);
    const config = this.getConfig();
    const prompt = this.buildPrompt(request);
    const messages = this.buildMessages(prompt, request.screenshot_base64);

    // Try primary provider first, fallback to other
    const primary = config.provider;
    const fallback: AiProvider = primary === 'deepseek' ? 'chatgpt' : 'deepseek';

    return this.callProvider(primary, messages, config).pipe(
      catchError(() => this.callProvider(fallback, messages, config)),
      map(result => {
        this.generating.set(false);
        return result;
      }),
      catchError(err => {
        this.generating.set(false);
        throw err;
      })
    );
  }

  private callProvider(
    provider: AiProvider,
    messages: any[],
    config: AiConfig
  ): Observable<{ report: string; provider: AiProvider }> {
    const url = provider === 'deepseek' ? this.DEEPSEEK_URL : this.OPENAI_URL;
    const key = provider === 'deepseek' ? config.deepseekKey : config.chatgptKey;
    const model = provider === 'deepseek' ? 'deepseek-chat' : 'gpt-4o-mini';

    if (!key) {
      return new Observable(subscriber => subscriber.error(new Error(`No API key for ${provider}`)));
    }

    return this.http.post<any>(url, {
      model,
      messages,
      max_tokens: 2000,
      temperature: 0.3,
    }, {
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${key}`,
      }
    }).pipe(
      map(response => ({
        report: response.choices?.[0]?.message?.content || 'No report generated',
        provider,
      }))
    );
  }

  private buildMessages(prompt: string, screenshotBase64?: string): any[] {
    if (screenshotBase64) {
      return [{
        role: 'user',
        content: [
          { type: 'text', text: prompt },
          { type: 'image_url', image_url: { url: screenshotBase64 } }
        ]
      }];
    }
    return [{ role: 'user', content: prompt }];
  }

  private buildPrompt(request: AiReportRequest): string {
    const base = `You are a software analyst helping generate structured reports for a developer team.
The application is an Arabic-first ERP system (Moon ERP).
Generate the report in Arabic with technical terms in English where appropriate.
Be concise and actionable.\n\n`;

    switch (request.type) {
      case 'bug':
        return base + this.buildBugPrompt(request);
      case 'change_request':
        return base + this.buildChangeRequestPrompt(request);
      case 'improvement':
        return base + this.buildImprovementPrompt(request);
      case 'feature_request':
        return base + this.buildFeatureRequestPrompt(request);
    }
  }

  private buildBugPrompt(r: AiReportRequest): string {
    return `Generate a BUG REPORT with these sections:
## عنوان مختصر (Short Title)
## الشدة (Severity): Critical/High/Medium/Low
## المكان (Location): ${r.page_url} - ${r.screen_name}
## خطوات إعادة الإنتاج (Steps to Reproduce):
${r.steps_to_reproduce || 'Not provided'}
## النتيجة المتوقعة (Expected):
${r.expected_result || 'Not provided'}
## النتيجة الفعلية (Actual):
${r.actual_result || 'Not provided'}
## وصف إضافي:
${r.description}
${r.target_elements?.length ? `## العنصر المستهدف: ${r.target_elements.map(e => `${e.tag}#${e.id}.${e.classes.join('.')} "${e.text}"`).join(', ')}` : ''}

Generate:
1. Clear title
2. Severity assessment
3. Detailed reproduction steps
4. Root cause hypothesis
5. Suggested fix approach`;
  }

  private buildChangeRequestPrompt(r: AiReportRequest): string {
    return `Generate a CHANGE REQUEST SPECIFICATION:
## المكان: ${r.module} > ${r.screen_name}
## نوع التعديل: ${r.change_type}
## الوضع الحالي (As-Is):
${r.as_is || 'Not provided'}
## المطلوب (To-Be):
${r.to_be || 'Not provided'}
## السبب (Why):
${r.why || 'Not provided'}
## وصف إضافي:
${r.description}
${r.target_elements?.length ? `## العناصر المستهدفة: ${r.target_elements.map(e => `${e.tag}#${e.id} "${e.text}" at ${e.css_path}`).join('\n')}` : ''}

Generate:
1. عنوان مختصر (Short Title)
2. المواصفة التفصيلية (Detailed Specification)
3. شروط القبول (Acceptance Criteria) - numbered list
4. تحليل التأثير (Impact Analysis)
5. ملاحظات للمبرمج (Developer Notes)`;
  }

  private buildImprovementPrompt(r: AiReportRequest): string {
    return `Generate an IMPROVEMENT PROPOSAL:
## المكان: ${r.module} > ${r.screen_name}
## الأهمية: ${r.priority || 'medium'}
## الوصف:
${r.description}
${r.target_elements?.length ? `## العناصر: ${r.target_elements.map(e => `${e.tag}#${e.id} "${e.text}"`).join(', ')}` : ''}

Generate:
1. عنوان مختصر
2. الوضع الحالي (Current State)
3. التحسين المقترح (Proposed Enhancement)
4. القيمة المضافة (Business Value)
5. شروط القبول (Acceptance Criteria)`;
  }

  private buildFeatureRequestPrompt(r: AiReportRequest): string {
    return `Generate a FEATURE REQUEST SPECIFICATION:
## المكان: ${r.module} > ${r.screen_name}
## الأهمية: ${r.priority || 'medium'}
## نوع القالب: ${r.template_type || 'N/A'}
## الوصف:
${r.description}

Generate:
1. عنوان مختصر
2. مواصفة الميزة (Feature Specification)
3. قصص المستخدم (User Stories) - as "بصفتي [role]، أريد [action]، حتى [benefit]"
4. شروط القبول (Acceptance Criteria) - numbered list
5. اعتبارات التصميم (Design Considerations)`;
  }
}
```

**Step 2: Commit**

```bash
git add src/app/features/feedback/services/ai-report.service.ts
git commit -m "feat(feedback): add AI report service with DeepSeek/ChatGPT support"
```

---

### Task 4: Create Element Inspector Service

**Files:**
- Create: `src/app/features/feedback/services/element-inspector.service.ts`

**Step 1: Create the element inspector service**

Create `src/app/features/feedback/services/element-inspector.service.ts`:

```typescript
import { Injectable, signal, inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { TargetElement } from '../models/feedback.model';

@Injectable({ providedIn: 'root' })
export class ElementInspectorService {
  private document = inject(DOCUMENT);

  active = signal(false);
  selectedElements = signal<TargetElement[]>([]);

  private overlay: HTMLDivElement | null = null;
  private highlight: HTMLDivElement | null = null;
  private currentTarget: HTMLElement | null = null;
  private resolveSelection: ((elements: TargetElement[]) => void) | null = null;

  startInspecting(): Promise<TargetElement[]> {
    return new Promise(resolve => {
      this.resolveSelection = resolve;
      this.active.set(true);
      this.createOverlay();
      this.attachListeners();
    });
  }

  stopInspecting(): void {
    this.active.set(false);
    this.removeOverlay();
    this.detachListeners();
    if (this.resolveSelection) {
      this.resolveSelection(this.selectedElements());
      this.resolveSelection = null;
    }
  }

  clearSelections(): void {
    this.selectedElements.set([]);
  }

  private createOverlay(): void {
    this.overlay = this.document.createElement('div');
    this.overlay.id = 'feedback-inspector-overlay';
    Object.assign(this.overlay.style, {
      position: 'fixed',
      inset: '0',
      zIndex: '99998',
      cursor: 'crosshair',
    });
    this.document.body.appendChild(this.overlay);

    this.highlight = this.document.createElement('div');
    this.highlight.id = 'feedback-inspector-highlight';
    Object.assign(this.highlight.style, {
      position: 'fixed',
      border: '2px solid #3b82f6',
      backgroundColor: 'rgba(59, 130, 246, 0.1)',
      borderRadius: '4px',
      pointerEvents: 'none',
      zIndex: '99999',
      transition: 'all 0.1s ease',
      display: 'none',
    });
    this.document.body.appendChild(this.highlight);

    // Info tooltip
    const tooltip = this.document.createElement('div');
    tooltip.id = 'feedback-inspector-tooltip';
    Object.assign(tooltip.style, {
      position: 'fixed',
      zIndex: '100000',
      background: '#1e293b',
      color: '#fff',
      padding: '4px 8px',
      borderRadius: '4px',
      fontSize: '12px',
      fontFamily: 'monospace',
      pointerEvents: 'none',
      display: 'none',
      maxWidth: '300px',
      whiteSpace: 'nowrap',
      overflow: 'hidden',
      textOverflow: 'ellipsis',
    });
    this.document.body.appendChild(tooltip);
  }

  private removeOverlay(): void {
    this.document.getElementById('feedback-inspector-overlay')?.remove();
    this.document.getElementById('feedback-inspector-highlight')?.remove();
    this.document.getElementById('feedback-inspector-tooltip')?.remove();
    this.overlay = null;
    this.highlight = null;
    this.currentTarget = null;
  }

  private onMouseMove = (e: MouseEvent): void => {
    if (!this.overlay || !this.highlight) return;

    this.overlay.style.pointerEvents = 'none';
    const target = this.document.elementFromPoint(e.clientX, e.clientY) as HTMLElement;
    this.overlay.style.pointerEvents = 'auto';

    if (!target || target === this.overlay || target === this.highlight) return;

    this.currentTarget = target;
    const rect = target.getBoundingClientRect();

    Object.assign(this.highlight.style, {
      display: 'block',
      top: `${rect.top}px`,
      left: `${rect.left}px`,
      width: `${rect.width}px`,
      height: `${rect.height}px`,
    });

    const tooltip = this.document.getElementById('feedback-inspector-tooltip');
    if (tooltip) {
      const label = this.getElementLabel(target);
      tooltip.textContent = label;
      tooltip.style.display = 'block';
      tooltip.style.top = `${Math.max(0, rect.top - 28)}px`;
      tooltip.style.left = `${rect.left}px`;
    }
  };

  private onClick = (e: MouseEvent): void => {
    e.preventDefault();
    e.stopPropagation();

    if (!this.currentTarget) return;

    const element = this.extractElementData(this.currentTarget);
    this.selectedElements.update(list => [...list, element]);

    // Flash green to confirm selection
    if (this.highlight) {
      this.highlight.style.borderColor = '#22c55e';
      this.highlight.style.backgroundColor = 'rgba(34, 197, 94, 0.15)';
      setTimeout(() => {
        if (this.highlight) {
          this.highlight.style.borderColor = '#3b82f6';
          this.highlight.style.backgroundColor = 'rgba(59, 130, 246, 0.1)';
        }
      }, 300);
    }
  };

  private onKeydown = (e: KeyboardEvent): void => {
    if (e.key === 'Escape') {
      this.stopInspecting();
    }
  };

  private attachListeners(): void {
    this.document.addEventListener('mousemove', this.onMouseMove, true);
    this.document.addEventListener('click', this.onClick, true);
    this.document.addEventListener('keydown', this.onKeydown, true);
  }

  private detachListeners(): void {
    this.document.removeEventListener('mousemove', this.onMouseMove, true);
    this.document.removeEventListener('click', this.onClick, true);
    this.document.removeEventListener('keydown', this.onKeydown, true);
  }

  private extractElementData(el: HTMLElement): TargetElement {
    const rect = el.getBoundingClientRect();
    return {
      tag: el.tagName.toLowerCase(),
      id: el.id || '',
      classes: Array.from(el.classList),
      text: (el.textContent || '').trim().substring(0, 100),
      css_path: this.getCssPath(el),
      rect: {
        x: Math.round(rect.x),
        y: Math.round(rect.y),
        width: Math.round(rect.width),
        height: Math.round(rect.height),
      },
    };
  }

  private getCssPath(el: HTMLElement): string {
    const parts: string[] = [];
    let current: HTMLElement | null = el;
    while (current && current !== this.document.body) {
      let selector = current.tagName.toLowerCase();
      if (current.id) {
        selector += `#${current.id}`;
        parts.unshift(selector);
        break;
      }
      const parent = current.parentElement;
      if (parent) {
        const siblings = Array.from(parent.children).filter(c => c.tagName === current!.tagName);
        if (siblings.length > 1) {
          const index = siblings.indexOf(current) + 1;
          selector += `:nth-of-type(${index})`;
        }
      }
      parts.unshift(selector);
      current = parent;
    }
    return parts.join(' > ');
  }

  private getElementLabel(el: HTMLElement): string {
    const tag = el.tagName.toLowerCase();
    const id = el.id ? `#${el.id}` : '';
    const cls = el.classList.length ? `.${Array.from(el.classList).slice(0, 2).join('.')}` : '';
    return `${tag}${id}${cls}`;
  }
}
```

**Step 2: Commit**

```bash
git add src/app/features/feedback/services/element-inspector.service.ts
git commit -m "feat(feedback): add element inspector service with click-to-select"
```

---

### Task 5: Create Screenshot Service

**Files:**
- Create: `src/app/features/feedback/services/screenshot.service.ts`

**Step 1: Create the screenshot capture service**

Create `src/app/features/feedback/services/screenshot.service.ts`:

```typescript
import { Injectable, inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import html2canvas from 'html2canvas';

@Injectable({ providedIn: 'root' })
export class ScreenshotService {
  private document = inject(DOCUMENT);

  async captureScreen(): Promise<string> {
    // Hide feedback UI before capturing
    const feedbackElements = this.document.querySelectorAll(
      '#feedback-fab, #feedback-modal-overlay, .p-dialog-mask'
    );
    feedbackElements.forEach((el: any) => el.style.display = 'none');

    try {
      const canvas = await html2canvas(this.document.body, {
        useCORS: true,
        allowTaint: true,
        scale: 1,
        logging: false,
        ignoreElements: (el) => {
          return el.id === 'feedback-fab' ||
                 el.id === 'feedback-inspector-overlay' ||
                 el.id === 'feedback-inspector-highlight' ||
                 el.id === 'feedback-inspector-tooltip';
        },
      });
      return canvas.toDataURL('image/png', 0.8);
    } finally {
      feedbackElements.forEach((el: any) => el.style.display = '');
    }
  }

  fileToDataUrl(file: File): Promise<string> {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result as string);
      reader.onerror = reject;
      reader.readAsDataURL(file);
    });
  }
}
```

**Step 2: Commit**

```bash
git add src/app/features/feedback/services/screenshot.service.ts
git commit -m "feat(feedback): add screenshot capture service with html2canvas"
```

---

### Task 6: Create Screenshot Annotation Component

**Files:**
- Create: `src/app/features/feedback/screenshot-editor/screenshot-editor.component.ts`

**Step 1: Create the annotation editor component**

Create `src/app/features/feedback/screenshot-editor/screenshot-editor.component.ts`:

```typescript
import {
  Component, input, output, signal, effect, ElementRef,
  viewChild, afterNextRender, OnDestroy
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { TranslateModule } from '@ngx-translate/core';
import { ButtonModule } from 'primeng/button';
import { TooltipModule } from 'primeng/tooltip';
import { Annotation, AnnotationTool, AnnotationColor } from '../models/feedback.model';

@Component({
  selector: 'app-screenshot-editor',
  standalone: true,
  imports: [CommonModule, FormsModule, TranslateModule, ButtonModule, TooltipModule],
  template: `
    <div class="screenshot-editor">
      <!-- Toolbar -->
      <div class="flex items-center gap-2 mb-3 flex-wrap">
        <div class="flex items-center gap-1 bg-surface-100 dark:bg-surface-800 rounded-lg p-1">
          @for (tool of tools; track tool.value) {
            <button
              class="tool-btn"
              [class.active]="activeTool() === tool.value"
              (click)="activeTool.set(tool.value)"
              [pTooltip]="tool.label"
            >
              <i [class]="tool.icon"></i>
            </button>
          }
        </div>

        <div class="flex items-center gap-1 bg-surface-100 dark:bg-surface-800 rounded-lg p-1">
          @for (color of colors; track color) {
            <button
              class="w-6 h-6 rounded-full border-2 transition-transform"
              [style.background]="color"
              [class.scale-125]="activeColor() === color"
              [style.border-color]="activeColor() === color ? '#fff' : 'transparent'"
              (click)="activeColor.set(color)"
            ></button>
          }
        </div>

        <div class="flex items-center gap-1 ms-auto">
          <p-button icon="pi pi-undo" severity="secondary" [text]="true" size="small"
                    (onClick)="undo()" [disabled]="!canUndo()" />
          <p-button icon="pi pi-refresh" severity="secondary" [text]="true" size="small"
                    (onClick)="redo()" [disabled]="!canRedo()" />
          <p-button icon="pi pi-trash" severity="danger" [text]="true" size="small"
                    (onClick)="clearAnnotations()" />
        </div>
      </div>

      <!-- Canvas -->
      <div class="relative border rounded-lg overflow-hidden bg-surface-100 dark:bg-surface-800">
        <canvas
          #annotationCanvas
          class="block max-w-full cursor-crosshair"
          (mousedown)="onMouseDown($event)"
          (mousemove)="onMouseMove($event)"
          (mouseup)="onMouseUp($event)"
        ></canvas>
      </div>
    </div>
  `,
  styles: [`
    .tool-btn {
      width: 32px;
      height: 32px;
      display: flex;
      align-items: center;
      justify-content: center;
      border-radius: 6px;
      border: none;
      background: transparent;
      cursor: pointer;
      color: var(--color-text-secondary);
      transition: all 0.15s;

      &:hover { background: var(--color-surface-muted); }
      &.active {
        background: var(--color-accent);
        color: var(--color-brand);
      }
    }
  `],
})
export class ScreenshotEditorComponent implements OnDestroy {
  imageDataUrl = input.required<string>();
  annotatedImage = output<string>();

  private canvasRef = viewChild.required<ElementRef<HTMLCanvasElement>>('annotationCanvas');
  private ctx: CanvasRenderingContext2D | null = null;
  private bgImage: HTMLImageElement | null = null;
  private isDrawing = false;
  private startX = 0;
  private startY = 0;

  activeTool = signal<AnnotationTool>('arrow');
  activeColor = signal<AnnotationColor>('#ef4444');
  annotations = signal<Annotation[]>([]);
  undoneAnnotations = signal<Annotation[]>([]);
  currentPoints = signal<{ x: number; y: number }[]>([]);

  canUndo = () => this.annotations().length > 0;
  canRedo = () => this.undoneAnnotations().length > 0;

  tools = [
    { value: 'arrow' as AnnotationTool, icon: 'pi pi-arrow-up-right', label: 'Arrow' },
    { value: 'rectangle' as AnnotationTool, icon: 'pi pi-stop', label: 'Rectangle' },
    { value: 'circle' as AnnotationTool, icon: 'pi pi-circle', label: 'Circle' },
    { value: 'text' as AnnotationTool, icon: 'pi pi-pen-to-square', label: 'Text' },
    { value: 'freehand' as AnnotationTool, icon: 'pi pi-pencil', label: 'Freehand' },
  ];

  colors: AnnotationColor[] = ['#ef4444', '#22c55e', '#3b82f6', '#eab308'];

  constructor() {
    effect(() => {
      const dataUrl = this.imageDataUrl();
      if (dataUrl) this.loadImage(dataUrl);
    });
  }

  ngOnDestroy(): void {
    this.bgImage = null;
    this.ctx = null;
  }

  private loadImage(dataUrl: string): void {
    const img = new Image();
    img.onload = () => {
      this.bgImage = img;
      const canvas = this.canvasRef().nativeElement;
      // Scale to fit container (max 800px wide)
      const maxWidth = Math.min(800, canvas.parentElement?.clientWidth || 800);
      const scale = maxWidth / img.width;
      canvas.width = img.width * scale;
      canvas.height = img.height * scale;
      this.ctx = canvas.getContext('2d');
      this.redraw();
    };
    img.src = dataUrl;
  }

  private redraw(): void {
    if (!this.ctx || !this.bgImage) return;
    const canvas = this.canvasRef().nativeElement;

    this.ctx.clearRect(0, 0, canvas.width, canvas.height);
    this.ctx.drawImage(this.bgImage, 0, 0, canvas.width, canvas.height);

    for (const ann of this.annotations()) {
      this.drawAnnotation(ann);
    }
  }

  private drawAnnotation(ann: Annotation): void {
    if (!this.ctx) return;
    this.ctx.strokeStyle = ann.color;
    this.ctx.fillStyle = ann.color;
    this.ctx.lineWidth = 2;

    switch (ann.tool) {
      case 'arrow':
        if (ann.points.length >= 2) {
          const start = ann.points[0];
          const end = ann.points[ann.points.length - 1];
          this.drawArrow(start.x, start.y, end.x, end.y);
        }
        break;
      case 'rectangle':
        if (ann.width !== undefined && ann.height !== undefined) {
          this.ctx.strokeRect(ann.points[0].x, ann.points[0].y, ann.width, ann.height);
        }
        break;
      case 'circle':
        if (ann.width !== undefined && ann.height !== undefined) {
          const cx = ann.points[0].x + ann.width / 2;
          const cy = ann.points[0].y + ann.height / 2;
          const rx = Math.abs(ann.width / 2);
          const ry = Math.abs(ann.height / 2);
          this.ctx.beginPath();
          this.ctx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2);
          this.ctx.stroke();
        }
        break;
      case 'text':
        if (ann.text) {
          this.ctx.font = 'bold 16px Cairo, sans-serif';
          this.ctx.fillText(ann.text, ann.points[0].x, ann.points[0].y);
        }
        break;
      case 'freehand':
        if (ann.points.length > 1) {
          this.ctx.beginPath();
          this.ctx.moveTo(ann.points[0].x, ann.points[0].y);
          for (let i = 1; i < ann.points.length; i++) {
            this.ctx.lineTo(ann.points[i].x, ann.points[i].y);
          }
          this.ctx.stroke();
        }
        break;
    }
  }

  private drawArrow(x1: number, y1: number, x2: number, y2: number): void {
    if (!this.ctx) return;
    const headLen = 12;
    const angle = Math.atan2(y2 - y1, x2 - x1);

    this.ctx.beginPath();
    this.ctx.moveTo(x1, y1);
    this.ctx.lineTo(x2, y2);
    this.ctx.stroke();

    this.ctx.beginPath();
    this.ctx.moveTo(x2, y2);
    this.ctx.lineTo(x2 - headLen * Math.cos(angle - Math.PI / 6), y2 - headLen * Math.sin(angle - Math.PI / 6));
    this.ctx.lineTo(x2 - headLen * Math.cos(angle + Math.PI / 6), y2 - headLen * Math.sin(angle + Math.PI / 6));
    this.ctx.closePath();
    this.ctx.fill();
  }

  private getCanvasCoords(e: MouseEvent): { x: number; y: number } {
    const canvas = this.canvasRef().nativeElement;
    const rect = canvas.getBoundingClientRect();
    return {
      x: e.clientX - rect.left,
      y: e.clientY - rect.top,
    };
  }

  onMouseDown(e: MouseEvent): void {
    const coords = this.getCanvasCoords(e);
    this.isDrawing = true;
    this.startX = coords.x;
    this.startY = coords.y;
    this.currentPoints.set([coords]);
  }

  onMouseMove(e: MouseEvent): void {
    if (!this.isDrawing) return;
    const coords = this.getCanvasCoords(e);
    this.currentPoints.update(p => [...p, coords]);

    // Live preview
    this.redraw();
    const tool = this.activeTool();
    const color = this.activeColor();
    if (this.ctx) {
      this.ctx.strokeStyle = color;
      this.ctx.fillStyle = color;
      this.ctx.lineWidth = 2;
      this.ctx.setLineDash([4, 4]);

      if (tool === 'arrow') {
        this.drawArrow(this.startX, this.startY, coords.x, coords.y);
      } else if (tool === 'rectangle') {
        this.ctx.strokeRect(this.startX, this.startY, coords.x - this.startX, coords.y - this.startY);
      } else if (tool === 'circle') {
        const w = coords.x - this.startX;
        const h = coords.y - this.startY;
        const cx = this.startX + w / 2;
        const cy = this.startY + h / 2;
        this.ctx.beginPath();
        this.ctx.ellipse(cx, cy, Math.abs(w / 2), Math.abs(h / 2), 0, 0, Math.PI * 2);
        this.ctx.stroke();
      } else if (tool === 'freehand') {
        const pts = this.currentPoints();
        if (pts.length > 1) {
          this.ctx.beginPath();
          this.ctx.moveTo(pts[0].x, pts[0].y);
          for (let i = 1; i < pts.length; i++) {
            this.ctx.lineTo(pts[i].x, pts[i].y);
          }
          this.ctx.stroke();
        }
      }
      this.ctx.setLineDash([]);
    }
  }

  onMouseUp(e: MouseEvent): void {
    if (!this.isDrawing) return;
    this.isDrawing = false;
    const coords = this.getCanvasCoords(e);
    const tool = this.activeTool();

    if (tool === 'text') {
      const text = prompt('أدخل النص:');
      if (text) {
        this.addAnnotation({
          id: crypto.randomUUID(),
          tool: 'text',
          color: this.activeColor(),
          points: [{ x: this.startX, y: this.startY }],
          text,
        });
      }
    } else {
      this.addAnnotation({
        id: crypto.randomUUID(),
        tool,
        color: this.activeColor(),
        points: tool === 'freehand' ? this.currentPoints() : [{ x: this.startX, y: this.startY }, coords],
        width: coords.x - this.startX,
        height: coords.y - this.startY,
      });
    }

    this.currentPoints.set([]);
    this.redraw();
    this.emitAnnotated();
  }

  private addAnnotation(ann: Annotation): void {
    this.annotations.update(list => [...list, ann]);
    this.undoneAnnotations.set([]);
  }

  undo(): void {
    this.annotations.update(list => {
      if (list.length === 0) return list;
      const last = list[list.length - 1];
      this.undoneAnnotations.update(u => [...u, last]);
      return list.slice(0, -1);
    });
    this.redraw();
    this.emitAnnotated();
  }

  redo(): void {
    this.undoneAnnotations.update(list => {
      if (list.length === 0) return list;
      const last = list[list.length - 1];
      this.annotations.update(a => [...a, last]);
      return list.slice(0, -1);
    });
    this.redraw();
    this.emitAnnotated();
  }

  clearAnnotations(): void {
    this.annotations.set([]);
    this.undoneAnnotations.set([]);
    this.redraw();
    this.emitAnnotated();
  }

  private emitAnnotated(): void {
    const canvas = this.canvasRef().nativeElement;
    this.annotatedImage.emit(canvas.toDataURL('image/png', 0.8));
  }
}
```

**Step 2: Commit**

```bash
git add src/app/features/feedback/screenshot-editor/screenshot-editor.component.ts
git commit -m "feat(feedback): add screenshot annotation editor with drawing tools"
```

---

### Task 7: Create Feedback FAB Button Component

**Files:**
- Create: `src/app/features/feedback/feedback-button/feedback-button.component.ts`

**Step 1: Create the floating action button**

Create `src/app/features/feedback/feedback-button/feedback-button.component.ts`:

```typescript
import { Component, inject, output } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { TooltipModule } from 'primeng/tooltip';
import { BadgeModule } from 'primeng/badge';
import { FeedbackStorageService } from '../services/feedback-storage.service';

@Component({
  selector: 'app-feedback-button',
  standalone: true,
  imports: [CommonModule, TranslateModule, TooltipModule, BadgeModule],
  template: `
    <button
      id="feedback-fab"
      class="feedback-fab"
      (click)="openFeedback.emit()"
      [pTooltip]="'FEEDBACK.TITLE' | translate"
      tooltipPosition="right"
    >
      <i class="pi pi-comments text-xl"></i>
      @if (storageService.count() > 0) {
        <span class="feedback-badge">{{ storageService.count() }}</span>
      }
    </button>
  `,
  styles: [`
    .feedback-fab {
      position: fixed;
      bottom: 24px;
      inset-inline-start: 24px;
      z-index: 9990;
      width: 52px;
      height: 52px;
      border-radius: 50%;
      border: none;
      background: var(--color-accent);
      color: var(--color-brand);
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
      transition: all 0.2s ease;

      &:hover {
        transform: scale(1.1);
        box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3);
      }

      &:active {
        transform: scale(0.95);
      }
    }

    .feedback-badge {
      position: absolute;
      top: -4px;
      inset-inline-end: -4px;
      min-width: 20px;
      height: 20px;
      border-radius: 10px;
      background: var(--color-danger);
      color: #fff;
      font-size: 11px;
      font-weight: 700;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 0 4px;
    }
  `],
})
export class FeedbackButtonComponent {
  storageService = inject(FeedbackStorageService);
  openFeedback = output<void>();
}
```

**Step 2: Commit**

```bash
git add src/app/features/feedback/feedback-button/feedback-button.component.ts
git commit -m "feat(feedback): add floating action button component"
```

---

### Task 8: Create Feedback Modal Component

**Files:**
- Create: `src/app/features/feedback/feedback-modal/feedback-modal.component.ts`
- Create: `src/app/features/feedback/feedback-modal/feedback-modal.component.html`
- Create: `src/app/features/feedback/feedback-modal/feedback-modal.component.scss`

This is the largest task — the main modal with stepper flow, all form fields, and AI integration.

**Step 1: Create the component TypeScript**

Create `src/app/features/feedback/feedback-modal/feedback-modal.component.ts`:

```typescript
import { Component, inject, signal, model, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { Store } from '@ngrx/store';
import { ButtonModule } from 'primeng/button';
import { DialogModule } from 'primeng/dialog';
import { SelectModule } from 'primeng/select';
import { TextareaModule } from 'primeng/textarea';
import { InputTextModule } from 'primeng/inputtext';
import { StepperModule } from 'primeng/stepper';
import { RadioButtonModule } from 'primeng/radiobutton';
import { FileUploadModule } from 'primeng/fileupload';
import { TagModule } from 'primeng/tag';
import { TooltipModule } from 'primeng/tooltip';
import { ProgressSpinnerModule } from 'primeng/progressspinner';
import { MessageModule } from 'primeng/message';

import { selectUser } from '../../../core/store';
import {
  FeedbackType, FeedbackReport, ChangeType, Priority,
  TargetElement, FeedbackScreenshot, AiProvider
} from '../models/feedback.model';
import { FeedbackStorageService } from '../services/feedback-storage.service';
import { AiReportService } from '../services/ai-report.service';
import { ElementInspectorService } from '../services/element-inspector.service';
import { ScreenshotService } from '../services/screenshot.service';
import { ScreenshotEditorComponent } from '../screenshot-editor/screenshot-editor.component';

@Component({
  selector: 'app-feedback-modal',
  standalone: true,
  imports: [
    CommonModule, FormsModule, TranslateModule,
    ButtonModule, DialogModule, SelectModule, TextareaModule,
    InputTextModule, StepperModule, RadioButtonModule, FileUploadModule,
    TagModule, TooltipModule, ProgressSpinnerModule, MessageModule,
    ScreenshotEditorComponent,
  ],
  templateUrl: './feedback-modal.component.html',
  styleUrl: './feedback-modal.component.scss',
})
export class FeedbackModalComponent implements OnInit {
  private router = inject(Router);
  private store = inject(Store);
  private translate = inject(TranslateService);
  storageService = inject(FeedbackStorageService);
  aiService = inject(AiReportService);
  inspectorService = inject(ElementInspectorService);
  screenshotService = inject(ScreenshotService);

  visible = model(false);
  user$ = this.store.select(selectUser);

  // Stepper
  currentStep = signal(0);

  // Step 1: Type
  feedbackType = signal<FeedbackType | null>(null);

  // Step 2: Details
  description = signal('');
  changeType = signal<ChangeType | null>(null);
  asIs = signal('');
  toBe = signal('');
  why = signal('');
  stepsToReproduce = signal('');
  expectedResult = signal('');
  actualResult = signal('');
  priority = signal<Priority>('medium');
  templateType = signal<string | null>(null);

  // Step 3: Element + Screenshot
  targetElements = signal<TargetElement[]>([]);
  screenshots = signal<FeedbackScreenshot[]>([]);
  activeScreenshotIndex = signal(0);

  // Step 4: AI Report
  aiReport = signal('');
  aiProvider = signal<AiProvider | null>(null);
  aiError = signal('');

  // Step 5: History view
  showHistory = signal(false);

  // Auto-captured context
  pageUrl = signal('');
  screenName = signal('');
  moduleName = signal('');
  userName = signal('');

  // Options
  feedbackTypes: { value: FeedbackType; label: string; icon: string; desc: string }[] = [
    { value: 'bug', label: 'FEEDBACK.TYPE_BUG', icon: 'pi pi-bug', desc: 'FEEDBACK.TYPE_BUG_DESC' },
    { value: 'change_request', label: 'FEEDBACK.TYPE_CHANGE', icon: 'pi pi-pencil', desc: 'FEEDBACK.TYPE_CHANGE_DESC' },
    { value: 'improvement', label: 'FEEDBACK.TYPE_IMPROVEMENT', icon: 'pi pi-chart-line', desc: 'FEEDBACK.TYPE_IMPROVEMENT_DESC' },
    { value: 'feature_request', label: 'FEEDBACK.TYPE_FEATURE', icon: 'pi pi-plus-circle', desc: 'FEEDBACK.TYPE_FEATURE_DESC' },
  ];

  changeTypes: { value: ChangeType; label: string }[] = [
    { value: 'design_change', label: 'FEEDBACK.CHANGE_DESIGN' },
    { value: 'show_element', label: 'FEEDBACK.CHANGE_SHOW' },
    { value: 'hide_element', label: 'FEEDBACK.CHANGE_HIDE' },
    { value: 'add_field', label: 'FEEDBACK.CHANGE_ADD_FIELD' },
    { value: 'remove_field', label: 'FEEDBACK.CHANGE_REMOVE_FIELD' },
    { value: 'reorder_fields', label: 'FEEDBACK.CHANGE_REORDER' },
    { value: 'change_permissions', label: 'FEEDBACK.CHANGE_PERMISSIONS' },
    { value: 'change_validation', label: 'FEEDBACK.CHANGE_VALIDATION' },
    { value: 'change_label', label: 'FEEDBACK.CHANGE_LABEL' },
    { value: 'change_flow', label: 'FEEDBACK.CHANGE_FLOW' },
  ];

  templateTypes: { value: string; label: string; icon: string }[] = [
    { value: 'button', label: 'FEEDBACK.TPL_BUTTON', icon: 'pi pi-bolt' },
    { value: 'table', label: 'FEEDBACK.TPL_TABLE', icon: 'pi pi-table' },
    { value: 'form', label: 'FEEDBACK.TPL_FORM', icon: 'pi pi-list' },
    { value: 'report', label: 'FEEDBACK.TPL_REPORT', icon: 'pi pi-file' },
  ];

  priorities: { value: Priority; label: string; severity: string }[] = [
    { value: 'high', label: 'FEEDBACK.PRIORITY_HIGH', severity: 'danger' },
    { value: 'medium', label: 'FEEDBACK.PRIORITY_MEDIUM', severity: 'warn' },
    { value: 'low', label: 'FEEDBACK.PRIORITY_LOW', severity: 'info' },
  ];

  ngOnInit(): void {
    this.captureContext();
    this.user$.subscribe(user => {
      if (user) this.userName.set(user.name);
    });
  }

  private captureContext(): void {
    this.pageUrl.set(window.location.href);
    const url = this.router.url;
    const segments = url.split('/').filter(Boolean);
    this.moduleName.set(segments[0] || 'unknown');
    this.screenName.set(segments.slice(1).join(' > ') || 'unknown');
  }

  onShow(): void {
    this.captureContext();
  }

  resetForm(): void {
    this.currentStep.set(0);
    this.feedbackType.set(null);
    this.description.set('');
    this.changeType.set(null);
    this.asIs.set('');
    this.toBe.set('');
    this.why.set('');
    this.stepsToReproduce.set('');
    this.expectedResult.set('');
    this.actualResult.set('');
    this.priority.set('medium');
    this.templateType.set(null);
    this.targetElements.set([]);
    this.screenshots.set([]);
    this.aiReport.set('');
    this.aiProvider.set(null);
    this.aiError.set('');
    this.inspectorService.clearSelections();
  }

  selectType(type: FeedbackType): void {
    this.feedbackType.set(type);
    this.currentStep.set(1);
  }

  // Element inspector
  async startInspector(): Promise<void> {
    this.visible.set(false);
    const elements = await this.inspectorService.startInspecting();
    this.targetElements.set(elements);
    this.visible.set(true);
  }

  removeElement(index: number): void {
    this.targetElements.update(list => list.filter((_, i) => i !== index));
  }

  // Screenshots
  async captureScreenshot(): Promise<void> {
    this.visible.set(false);
    // Small delay to let dialog close
    await new Promise(r => setTimeout(r, 300));
    try {
      const dataUrl = await this.screenshotService.captureScreen();
      this.screenshots.update(list => [...list, { data_url: dataUrl, source: 'auto' }]);
      this.activeScreenshotIndex.set(this.screenshots().length - 1);
    } catch (err) {
      console.error('Screenshot failed:', err);
    }
    this.visible.set(true);
  }

  async onFileUpload(event: any): Promise<void> {
    const file = event.files?.[0] || event.target?.files?.[0];
    if (!file) return;
    const dataUrl = await this.screenshotService.fileToDataUrl(file);
    this.screenshots.update(list => [...list, { data_url: dataUrl, source: 'upload' }]);
    this.activeScreenshotIndex.set(this.screenshots().length - 1);
  }

  onAnnotated(dataUrl: string): void {
    const idx = this.activeScreenshotIndex();
    this.screenshots.update(list =>
      list.map((s, i) => i === idx ? { ...s, annotated_url: dataUrl } : s)
    );
  }

  removeScreenshot(index: number): void {
    this.screenshots.update(list => list.filter((_, i) => i !== index));
    if (this.activeScreenshotIndex() >= this.screenshots().length) {
      this.activeScreenshotIndex.set(Math.max(0, this.screenshots().length - 1));
    }
  }

  // AI Report
  generateReport(): void {
    this.aiError.set('');
    const screenshotData = this.screenshots().length > 0
      ? (this.screenshots()[0].annotated_url || this.screenshots()[0].data_url)
      : undefined;

    this.aiService.generateReport({
      type: this.feedbackType()!,
      description: this.description(),
      page_url: this.pageUrl(),
      screen_name: this.screenName(),
      module: this.moduleName(),
      change_type: this.changeType() || undefined,
      as_is: this.asIs() || undefined,
      to_be: this.toBe() || undefined,
      why: this.why() || undefined,
      steps_to_reproduce: this.stepsToReproduce() || undefined,
      expected_result: this.expectedResult() || undefined,
      actual_result: this.actualResult() || undefined,
      priority: this.priority(),
      target_elements: this.targetElements(),
      screenshot_base64: screenshotData,
    }).subscribe({
      next: (result) => {
        this.aiReport.set(result.report);
        this.aiProvider.set(result.provider);
        this.currentStep.set(3);
      },
      error: (err) => {
        this.aiError.set(err.message || 'Failed to generate AI report');
      },
    });
  }

  // Save & Submit
  saveReport(): void {
    const report: FeedbackReport = {
      id: this.storageService.generateId(),
      type: this.feedbackType()!,
      status: 'submitted',
      page_url: this.pageUrl(),
      screen_name: this.screenName(),
      module: this.moduleName(),
      change_type: this.changeType() || undefined,
      description: this.description(),
      as_is: this.asIs() || undefined,
      to_be: this.toBe() || undefined,
      why: this.why() || undefined,
      steps_to_reproduce: this.stepsToReproduce() || undefined,
      expected_result: this.expectedResult() || undefined,
      actual_result: this.actualResult() || undefined,
      priority: this.priority(),
      template_type: this.templateType() || undefined,
      target_elements: this.targetElements(),
      screenshots: this.screenshots(),
      ai_report: this.aiReport() || undefined,
      ai_provider: this.aiProvider() || undefined,
      created_at: new Date().toISOString(),
      user_name: this.userName(),
    };

    this.storageService.save(report);
    this.resetForm();
    this.visible.set(false);
  }

  copyReport(): void {
    navigator.clipboard.writeText(this.aiReport());
  }

  canProceedToStep2(): boolean {
    return this.feedbackType() !== null;
  }

  canProceedToStep3(): boolean {
    if (!this.description()) return false;
    if (this.feedbackType() === 'change_request') {
      return !!this.changeType() && !!this.asIs() && !!this.toBe();
    }
    if (this.feedbackType() === 'bug') {
      return !!this.stepsToReproduce();
    }
    return true;
  }

  // AI Settings
  showAiSettings = signal(false);
  aiConfig = signal(this.aiService.getConfig());

  saveAiConfig(): void {
    this.aiService.saveConfig(this.aiConfig());
    this.showAiSettings.set(false);
  }
}
```

**Step 2: Create the modal template**

Create `src/app/features/feedback/feedback-modal/feedback-modal.component.html`:

```html
<p-dialog
  [header]="(showHistory() ? 'FEEDBACK.HISTORY' : 'FEEDBACK.TITLE') | translate"
  [(visible)]="visible"
  [modal]="true"
  [style]="{ width: '720px', maxHeight: '90vh' }"
  [maximizable]="true"
  [closable]="true"
  (onShow)="onShow()"
  (onHide)="showHistory.set(false)"
>
  <!-- History Toggle -->
  <div class="flex items-center justify-between mb-4">
    <div class="flex items-center gap-2 text-sm text-text-secondary">
      <i class="pi pi-map-marker"></i>
      <span>{{ moduleName() }} > {{ screenName() }}</span>
    </div>
    <div class="flex items-center gap-2">
      <p-button
        icon="pi pi-cog"
        severity="secondary"
        [text]="true"
        size="small"
        (onClick)="showAiSettings.set(!showAiSettings())"
        pTooltip="AI Settings"
      />
      <p-button
        [icon]="showHistory() ? 'pi pi-plus' : 'pi pi-history'"
        severity="secondary"
        [text]="true"
        size="small"
        (onClick)="showHistory.set(!showHistory())"
        [pTooltip]="(showHistory() ? 'FEEDBACK.NEW' : 'FEEDBACK.HISTORY') | translate"
      />
    </div>
  </div>

  <!-- AI Settings Panel -->
  @if (showAiSettings()) {
    <div class="mb-4 p-4 border rounded-lg bg-surface-50 dark:bg-surface-900">
      <h4 class="text-sm font-bold mb-3">{{ 'FEEDBACK.AI_SETTINGS' | translate }}</h4>
      <div class="grid grid-cols-1 gap-3">
        <div>
          <label class="block text-sm mb-1">{{ 'FEEDBACK.AI_PROVIDER' | translate }}</label>
          <p-select
            [options]="[{label: 'DeepSeek', value: 'deepseek'}, {label: 'ChatGPT', value: 'chatgpt'}]"
            [(ngModel)]="aiConfig().provider"
            [style]="{ width: '100%' }"
            optionLabel="label"
            optionValue="value"
          />
        </div>
        <div>
          <label class="block text-sm mb-1">DeepSeek API Key</label>
          <input pInputText type="password" [(ngModel)]="aiConfig().deepseekKey" class="w-full" />
        </div>
        <div>
          <label class="block text-sm mb-1">ChatGPT API Key</label>
          <input pInputText type="password" [(ngModel)]="aiConfig().chatgptKey" class="w-full" />
        </div>
        <p-button
          [label]="'COMMON.SAVE' | translate"
          icon="pi pi-check"
          size="small"
          (onClick)="saveAiConfig()"
        />
      </div>
    </div>
  }

  <!-- History View -->
  @if (showHistory()) {
    <div class="space-y-3">
      @if (storageService.reports().length === 0) {
        <p class="text-center text-text-secondary py-8">{{ 'FEEDBACK.NO_REPORTS' | translate }}</p>
      }
      @for (report of storageService.reports(); track report.id) {
        <div class="border rounded-lg p-4 hover:bg-surface-50 dark:hover:bg-surface-900 transition-colors">
          <div class="flex items-center justify-between mb-2">
            <div class="flex items-center gap-2">
              @switch (report.type) {
                @case ('bug') { <p-tag severity="danger" value="Bug" /> }
                @case ('change_request') { <p-tag severity="warn" value="Change" /> }
                @case ('improvement') { <p-tag severity="info" value="Improve" /> }
                @case ('feature_request') { <p-tag severity="success" value="Feature" /> }
              }
              <span class="text-sm font-medium">{{ report.description | slice:0:60 }}{{ report.description.length > 60 ? '...' : '' }}</span>
            </div>
            <div class="flex items-center gap-1">
              <span class="text-xs text-text-secondary">{{ report.created_at | date:'short' }}</span>
              <p-button
                icon="pi pi-trash"
                severity="danger"
                [text]="true"
                size="small"
                (onClick)="storageService.delete(report.id)"
              />
            </div>
          </div>
          @if (report.ai_report) {
            <div class="text-xs bg-surface-100 dark:bg-surface-800 rounded p-3 mt-2 max-h-32 overflow-y-auto whitespace-pre-wrap font-mono">
              {{ report.ai_report }}
            </div>
          }
          <div class="text-xs text-text-secondary mt-1">
            {{ report.module }} > {{ report.screen_name }}
            @if (report.ai_provider) { <span class="ms-2">({{ report.ai_provider }})</span> }
          </div>
        </div>
      }
    </div>
  } @else {
    <!-- New Feedback Form -->

    <!-- Step 0: Type Selection -->
    @if (currentStep() === 0) {
      <div class="grid grid-cols-2 gap-3">
        @for (type of feedbackTypes; track type.value) {
          <button
            class="type-card"
            (click)="selectType(type.value)"
            [class.selected]="feedbackType() === type.value"
          >
            <i [class]="type.icon + ' text-2xl mb-2'"></i>
            <span class="font-bold text-sm">{{ type.label | translate }}</span>
            <span class="text-xs text-text-secondary mt-1">{{ type.desc | translate }}</span>
          </button>
        }
      </div>
    }

    <!-- Step 1: Details -->
    @if (currentStep() === 1) {
      <div class="space-y-4">
        <!-- Bug fields -->
        @if (feedbackType() === 'bug') {
          <div>
            <label class="block text-sm font-medium mb-1">{{ 'FEEDBACK.STEPS' | translate }} *</label>
            <textarea pTextarea [ngModel]="stepsToReproduce()" (ngModelChange)="stepsToReproduce.set($event)"
                      rows="3" class="w-full" [placeholder]="'FEEDBACK.STEPS_HINT' | translate"></textarea>
          </div>
          <div class="grid grid-cols-2 gap-3">
            <div>
              <label class="block text-sm font-medium mb-1">{{ 'FEEDBACK.EXPECTED' | translate }}</label>
              <textarea pTextarea [ngModel]="expectedResult()" (ngModelChange)="expectedResult.set($event)"
                        rows="2" class="w-full"></textarea>
            </div>
            <div>
              <label class="block text-sm font-medium mb-1">{{ 'FEEDBACK.ACTUAL' | translate }}</label>
              <textarea pTextarea [ngModel]="actualResult()" (ngModelChange)="actualResult.set($event)"
                        rows="2" class="w-full"></textarea>
            </div>
          </div>
        }

        <!-- Change Request fields -->
        @if (feedbackType() === 'change_request') {
          <div>
            <label class="block text-sm font-medium mb-1">{{ 'FEEDBACK.CHANGE_TYPE' | translate }} *</label>
            <p-select
              [options]="changeTypes"
              [ngModel]="changeType()"
              (ngModelChange)="changeType.set($event)"
              optionLabel="label"
              optionValue="value"
              [placeholder]="'FEEDBACK.SELECT_CHANGE_TYPE' | translate"
              [style]="{ width: '100%' }"
            >
              <ng-template let-item pTemplate="item">
                {{ item.label | translate }}
              </ng-template>
              <ng-template let-item pTemplate="selectedItem">
                {{ item.label | translate }}
              </ng-template>
            </p-select>
          </div>
          <div>
            <label class="block text-sm font-medium mb-1">{{ 'FEEDBACK.AS_IS' | translate }} *</label>
            <textarea pTextarea [ngModel]="asIs()" (ngModelChange)="asIs.set($event)"
                      rows="2" class="w-full" [placeholder]="'FEEDBACK.AS_IS_HINT' | translate"></textarea>
          </div>
          <div>
            <label class="block text-sm font-medium mb-1">{{ 'FEEDBACK.TO_BE' | translate }} *</label>
            <textarea pTextarea [ngModel]="toBe()" (ngModelChange)="toBe.set($event)"
                      rows="2" class="w-full" [placeholder]="'FEEDBACK.TO_BE_HINT' | translate"></textarea>
          </div>
          <div>
            <label class="block text-sm font-medium mb-1">{{ 'FEEDBACK.WHY' | translate }}</label>
            <textarea pTextarea [ngModel]="why()" (ngModelChange)="why.set($event)"
                      rows="2" class="w-full" [placeholder]="'FEEDBACK.WHY_HINT' | translate"></textarea>
          </div>
        }

        <!-- Improvement / Feature Request fields -->
        @if (feedbackType() === 'improvement' || feedbackType() === 'feature_request') {
          <div>
            <label class="block text-sm font-medium mb-1">{{ 'FEEDBACK.PRIORITY' | translate }}</label>
            <div class="flex gap-3">
              @for (p of priorities; track p.value) {
                <label class="flex items-center gap-2 cursor-pointer">
                  <p-radioButton
                    [value]="p.value"
                    [ngModel]="priority()"
                    (ngModelChange)="priority.set($event)"
                    name="priority"
                  />
                  <p-tag [severity]="p.severity" [value]="p.label | translate" />
                </label>
              }
            </div>
          </div>
        }

        @if (feedbackType() === 'feature_request') {
          <div>
            <label class="block text-sm font-medium mb-1">{{ 'FEEDBACK.TEMPLATE' | translate }}</label>
            <div class="flex gap-2 flex-wrap">
              @for (tpl of templateTypes; track tpl.value) {
                <button
                  class="px-3 py-2 rounded-lg border text-sm flex items-center gap-2 transition-colors"
                  [class.bg-accent]="templateType() === tpl.value"
                  [class.text-brand]="templateType() === tpl.value"
                  [class.border-accent]="templateType() === tpl.value"
                  (click)="templateType.set(templateType() === tpl.value ? null : tpl.value)"
                >
                  <i [class]="tpl.icon"></i>
                  {{ tpl.label | translate }}
                </button>
              }
            </div>
          </div>
        }

        <!-- Common description -->
        <div>
          <label class="block text-sm font-medium mb-1">{{ 'FEEDBACK.DESCRIPTION' | translate }} *</label>
          <textarea pTextarea [ngModel]="description()" (ngModelChange)="description.set($event)"
                    rows="3" class="w-full" [placeholder]="'FEEDBACK.DESCRIPTION_HINT' | translate"></textarea>
        </div>

        <!-- Navigation -->
        <div class="flex justify-between pt-3">
          <p-button
            [label]="'COMMON.BACK' | translate"
            icon="pi pi-arrow-right"
            severity="secondary"
            (onClick)="currentStep.set(0)"
          />
          <p-button
            [label]="'COMMON.NEXT' | translate"
            icon="pi pi-arrow-left"
            iconPos="right"
            (onClick)="currentStep.set(2)"
            [disabled]="!canProceedToStep3()"
          />
        </div>
      </div>
    }

    <!-- Step 2: Element + Screenshot -->
    @if (currentStep() === 2) {
      <div class="space-y-4">
        <!-- Target Elements -->
        <div>
          <div class="flex items-center justify-between mb-2">
            <label class="text-sm font-medium">{{ 'FEEDBACK.TARGET_ELEMENTS' | translate }}</label>
            <p-button
              [label]="'FEEDBACK.SELECT_ELEMENT' | translate"
              icon="pi pi-search"
              severity="secondary"
              size="small"
              (onClick)="startInspector()"
            />
          </div>
          @if (targetElements().length > 0) {
            <div class="space-y-2">
              @for (el of targetElements(); track el.css_path; let i = $index) {
                <div class="flex items-center gap-2 bg-surface-100 dark:bg-surface-800 rounded-lg px-3 py-2">
                  <i class="pi pi-code text-xs text-text-secondary"></i>
                  <span class="text-xs font-mono flex-1 truncate">
                    &lt;{{ el.tag }}&gt; {{ el.id ? '#' + el.id : '' }} "{{ el.text | slice:0:40 }}"
                  </span>
                  <p-button icon="pi pi-times" severity="danger" [text]="true" size="small"
                            (onClick)="removeElement(i)" />
                </div>
              }
            </div>
          } @else {
            <p class="text-xs text-text-secondary">{{ 'FEEDBACK.NO_ELEMENTS' | translate }}</p>
          }
        </div>

        <!-- Screenshots -->
        <div>
          <div class="flex items-center justify-between mb-2">
            <label class="text-sm font-medium">{{ 'FEEDBACK.SCREENSHOTS' | translate }}</label>
            <div class="flex gap-2">
              <p-button
                [label]="'FEEDBACK.CAPTURE' | translate"
                icon="pi pi-camera"
                severity="secondary"
                size="small"
                (onClick)="captureScreenshot()"
              />
              <p-button
                [label]="'FEEDBACK.UPLOAD' | translate"
                icon="pi pi-upload"
                severity="secondary"
                size="small"
                (onClick)="fileInput.click()"
              />
              <input #fileInput type="file" accept="image/*" (change)="onFileUpload($event)" class="hidden" />
            </div>
          </div>

          @if (screenshots().length > 0) {
            <!-- Screenshot tabs -->
            <div class="flex gap-2 mb-2">
              @for (ss of screenshots(); track $index; let i = $index) {
                <button
                  class="px-3 py-1 rounded text-xs border transition-colors"
                  [class.bg-accent]="activeScreenshotIndex() === i"
                  [class.text-brand]="activeScreenshotIndex() === i"
                  (click)="activeScreenshotIndex.set(i)"
                >
                  {{ ss.source === 'auto' ? ('FEEDBACK.AUTO' | translate) : ('FEEDBACK.UPLOAD' | translate) }} {{ i + 1 }}
                  <i class="pi pi-times ms-1 cursor-pointer" (click)="removeScreenshot(i); $event.stopPropagation()"></i>
                </button>
              }
            </div>

            <!-- Annotation editor -->
            <app-screenshot-editor
              [imageDataUrl]="screenshots()[activeScreenshotIndex()].data_url"
              (annotatedImage)="onAnnotated($event)"
            />
          } @else {
            <p class="text-xs text-text-secondary">{{ 'FEEDBACK.NO_SCREENSHOTS' | translate }}</p>
          }
        </div>

        <!-- Navigation -->
        <div class="flex justify-between pt-3">
          <p-button
            [label]="'COMMON.BACK' | translate"
            icon="pi pi-arrow-right"
            severity="secondary"
            (onClick)="currentStep.set(1)"
          />
          <div class="flex gap-2">
            <p-button
              [label]="'FEEDBACK.SKIP_AI' | translate"
              severity="secondary"
              (onClick)="currentStep.set(3)"
            />
            <p-button
              [label]="'FEEDBACK.GENERATE_AI' | translate"
              icon="pi pi-sparkles"
              (onClick)="generateReport()"
              [loading]="aiService.generating()"
            />
          </div>
        </div>

        @if (aiError()) {
          <p-message severity="error" [text]="aiError()" />
        }
      </div>
    }

    <!-- Step 3: AI Report + Save -->
    @if (currentStep() === 3) {
      <div class="space-y-4">
        @if (aiReport()) {
          <div class="bg-surface-100 dark:bg-surface-800 rounded-lg p-4">
            <div class="flex items-center justify-between mb-3">
              <div class="flex items-center gap-2">
                <i class="pi pi-sparkles text-accent"></i>
                <span class="text-sm font-bold">{{ 'FEEDBACK.AI_REPORT' | translate }}</span>
                @if (aiProvider()) {
                  <p-tag [value]="aiProvider()!" size="small" />
                }
              </div>
              <p-button icon="pi pi-copy" severity="secondary" [text]="true" size="small"
                        (onClick)="copyReport()" pTooltip="Copy" />
            </div>
            <div class="whitespace-pre-wrap text-sm leading-relaxed max-h-96 overflow-y-auto">
              {{ aiReport() }}
            </div>
          </div>
        } @else {
          <p class="text-center text-text-secondary py-4">{{ 'FEEDBACK.NO_AI_REPORT' | translate }}</p>
        }

        <!-- Summary -->
        <div class="bg-surface-50 dark:bg-surface-900 rounded-lg p-4">
          <h4 class="text-sm font-bold mb-2">{{ 'FEEDBACK.SUMMARY' | translate }}</h4>
          <div class="grid grid-cols-2 gap-2 text-xs">
            <div><strong>{{ 'FEEDBACK.TYPE' | translate }}:</strong> {{ feedbackType() }}</div>
            <div><strong>{{ 'FEEDBACK.MODULE' | translate }}:</strong> {{ moduleName() }}</div>
            <div><strong>{{ 'FEEDBACK.SCREEN' | translate }}:</strong> {{ screenName() }}</div>
            <div><strong>{{ 'FEEDBACK.ELEMENTS' | translate }}:</strong> {{ targetElements().length }}</div>
            <div><strong>{{ 'FEEDBACK.SCREENSHOTS_COUNT' | translate }}:</strong> {{ screenshots().length }}</div>
            @if (priority()) {
              <div><strong>{{ 'FEEDBACK.PRIORITY' | translate }}:</strong> {{ priority() }}</div>
            }
          </div>
        </div>

        <!-- Navigation -->
        <div class="flex justify-between pt-3">
          <p-button
            [label]="'COMMON.BACK' | translate"
            icon="pi pi-arrow-right"
            severity="secondary"
            (onClick)="currentStep.set(2)"
          />
          <p-button
            [label]="'FEEDBACK.SAVE_REPORT' | translate"
            icon="pi pi-check"
            (onClick)="saveReport()"
          />
        </div>
      </div>
    }
  }
</p-dialog>
```

**Step 3: Create the modal styles**

Create `src/app/features/feedback/feedback-modal/feedback-modal.component.scss`:

```scss
.type-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 1.25rem;
  border: 2px solid var(--color-glass-border);
  border-radius: 12px;
  background: transparent;
  cursor: pointer;
  transition: all 0.2s ease;
  text-align: center;

  &:hover {
    border-color: var(--color-accent);
    background: var(--color-accent-glow);
  }

  &.selected {
    border-color: var(--color-accent);
    background: var(--color-accent-glow);
  }
}

:host ::ng-deep {
  .p-dialog-content {
    padding-bottom: 0 !important;
  }
}
```

**Step 4: Commit**

```bash
git add src/app/features/feedback/feedback-modal/
git commit -m "feat(feedback): add main feedback modal with stepper form and AI integration"
```

---

### Task 9: Create Feedback History Component

**Files:**
- Create: `src/app/features/feedback/feedback-history/feedback-history.component.ts`

This is already integrated into the modal (showHistory toggle). No separate component needed — history view is built into feedback-modal.component.html. **Skip this task.**

---

### Task 10: Add Translation Keys

**Files:**
- Modify: `src/assets/i18n/ar.json`
- Modify: `src/assets/i18n/en.json`

**Step 1: Add Arabic translations**

Add to `src/assets/i18n/ar.json` inside the root object (after last existing key):

```json
"FEEDBACK": {
  "TITLE": "ملاحظات واقتراحات",
  "NEW": "طلب جديد",
  "HISTORY": "السجل",
  "NO_REPORTS": "لا توجد تقارير محفوظة",
  "TYPE": "النوع",
  "TYPE_BUG": "مشكلة",
  "TYPE_BUG_DESC": "بلاغ عن خطأ أو مشكلة في النظام",
  "TYPE_CHANGE": "طلب تعديل",
  "TYPE_CHANGE_DESC": "تعديل على شيء موجود حالياً",
  "TYPE_IMPROVEMENT": "تحسين",
  "TYPE_IMPROVEMENT_DESC": "تحسين أداء أو تجربة المستخدم",
  "TYPE_FEATURE": "ميزة جديدة",
  "TYPE_FEATURE_DESC": "إضافة وظيفة أو ميزة جديدة",
  "CHANGE_TYPE": "نوع التعديل",
  "SELECT_CHANGE_TYPE": "اختر نوع التعديل",
  "CHANGE_DESIGN": "تغيير شكل/تصميم",
  "CHANGE_SHOW": "إظهار عنصر",
  "CHANGE_HIDE": "إخفاء عنصر",
  "CHANGE_ADD_FIELD": "إضافة حقل",
  "CHANGE_REMOVE_FIELD": "حذف حقل",
  "CHANGE_REORDER": "تغيير ترتيب",
  "CHANGE_PERMISSIONS": "تغيير صلاحيات",
  "CHANGE_VALIDATION": "تغيير Validation",
  "CHANGE_LABEL": "تغيير نص/Label",
  "CHANGE_FLOW": "تغيير Flow",
  "AS_IS": "الوضع الحالي",
  "AS_IS_HINT": "إيه اللي بيحصل دلوقتي؟",
  "TO_BE": "المطلوب",
  "TO_BE_HINT": "عايزه يبقى عامل إزاي؟",
  "WHY": "السبب / القيمة",
  "WHY_HINT": "ليه؟ (يوفر وقت، يقلل خطأ...)",
  "DESCRIPTION": "الوصف",
  "DESCRIPTION_HINT": "اوصف الطلب بالتفصيل...",
  "STEPS": "خطوات إعادة الإنتاج",
  "STEPS_HINT": "1. افتح الصفحة\n2. اضغط على...\n3. ...",
  "EXPECTED": "النتيجة المتوقعة",
  "ACTUAL": "النتيجة الفعلية",
  "PRIORITY": "الأهمية",
  "PRIORITY_HIGH": "عالية",
  "PRIORITY_MEDIUM": "متوسطة",
  "PRIORITY_LOW": "منخفضة",
  "TEMPLATE": "نوع القالب",
  "TPL_BUTTON": "زر",
  "TPL_TABLE": "جدول",
  "TPL_FORM": "نموذج",
  "TPL_REPORT": "تقرير",
  "TARGET_ELEMENTS": "العناصر المستهدفة",
  "SELECT_ELEMENT": "حدد عنصر",
  "NO_ELEMENTS": "لم يتم تحديد عناصر. اضغط 'حدد عنصر' لاختيار عنصر من الصفحة.",
  "SCREENSHOTS": "لقطات الشاشة",
  "CAPTURE": "التقاط",
  "UPLOAD": "رفع صورة",
  "AUTO": "تلقائي",
  "NO_SCREENSHOTS": "لا توجد لقطات شاشة.",
  "GENERATE_AI": "توليد تقرير AI",
  "SKIP_AI": "تخطي AI",
  "AI_REPORT": "تقرير الذكاء الاصطناعي",
  "AI_SETTINGS": "إعدادات AI",
  "AI_PROVIDER": "مزود الخدمة",
  "NO_AI_REPORT": "لم يتم توليد تقرير AI. يمكنك الحفظ بدونه.",
  "SUMMARY": "ملخص الطلب",
  "MODULE": "الموديول",
  "SCREEN": "الشاشة",
  "ELEMENTS": "العناصر",
  "SCREENSHOTS_COUNT": "لقطات الشاشة",
  "SAVE_REPORT": "حفظ التقرير"
}
```

**Step 2: Add English translations**

Add to `src/assets/i18n/en.json`:

```json
"FEEDBACK": {
  "TITLE": "Feedback & Suggestions",
  "NEW": "New Request",
  "HISTORY": "History",
  "NO_REPORTS": "No saved reports",
  "TYPE": "Type",
  "TYPE_BUG": "Bug Report",
  "TYPE_BUG_DESC": "Report an error or problem",
  "TYPE_CHANGE": "Change Request",
  "TYPE_CHANGE_DESC": "Modify existing functionality",
  "TYPE_IMPROVEMENT": "Improvement",
  "TYPE_IMPROVEMENT_DESC": "Enhance performance or UX",
  "TYPE_FEATURE": "Feature Request",
  "TYPE_FEATURE_DESC": "Add new functionality",
  "CHANGE_TYPE": "Change Type",
  "SELECT_CHANGE_TYPE": "Select change type",
  "CHANGE_DESIGN": "Design/Layout Change",
  "CHANGE_SHOW": "Show Element",
  "CHANGE_HIDE": "Hide Element",
  "CHANGE_ADD_FIELD": "Add Field",
  "CHANGE_REMOVE_FIELD": "Remove Field",
  "CHANGE_REORDER": "Reorder Fields",
  "CHANGE_PERMISSIONS": "Change Permissions",
  "CHANGE_VALIDATION": "Change Validation",
  "CHANGE_LABEL": "Change Label/Text",
  "CHANGE_FLOW": "Change Flow",
  "AS_IS": "Current State (As-Is)",
  "AS_IS_HINT": "What happens currently?",
  "TO_BE": "Desired State (To-Be)",
  "TO_BE_HINT": "How should it work?",
  "WHY": "Reason / Value",
  "WHY_HINT": "Why? (saves time, reduces errors...)",
  "DESCRIPTION": "Description",
  "DESCRIPTION_HINT": "Describe your request in detail...",
  "STEPS": "Steps to Reproduce",
  "STEPS_HINT": "1. Open the page\n2. Click on...\n3. ...",
  "EXPECTED": "Expected Result",
  "ACTUAL": "Actual Result",
  "PRIORITY": "Priority",
  "PRIORITY_HIGH": "High",
  "PRIORITY_MEDIUM": "Medium",
  "PRIORITY_LOW": "Low",
  "TEMPLATE": "Template Type",
  "TPL_BUTTON": "Button",
  "TPL_TABLE": "Table",
  "TPL_FORM": "Form",
  "TPL_REPORT": "Report",
  "TARGET_ELEMENTS": "Target Elements",
  "SELECT_ELEMENT": "Select Element",
  "NO_ELEMENTS": "No elements selected. Click 'Select Element' to pick an element from the page.",
  "SCREENSHOTS": "Screenshots",
  "CAPTURE": "Capture",
  "UPLOAD": "Upload",
  "AUTO": "Auto",
  "NO_SCREENSHOTS": "No screenshots.",
  "GENERATE_AI": "Generate AI Report",
  "SKIP_AI": "Skip AI",
  "AI_REPORT": "AI Report",
  "AI_SETTINGS": "AI Settings",
  "AI_PROVIDER": "Provider",
  "NO_AI_REPORT": "No AI report generated. You can save without it.",
  "SUMMARY": "Request Summary",
  "MODULE": "Module",
  "SCREEN": "Screen",
  "ELEMENTS": "Elements",
  "SCREENSHOTS_COUNT": "Screenshots",
  "SAVE_REPORT": "Save Report"
}
```

**Step 3: Commit**

```bash
git add src/assets/i18n/ar.json src/assets/i18n/en.json
git commit -m "feat(feedback): add Arabic and English translation keys"
```

---

### Task 11: Integrate FAB + Modal into Main Layout

**Files:**
- Modify: `src/app/layout/main-layout/main-layout.component.ts`
- Modify: `src/app/layout/main-layout/main-layout.component.html`

**Step 1: Update main-layout.component.ts imports**

Add imports for the feedback components:

```typescript
import { Component, inject, computed, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { Store } from '@ngrx/store';
import { SidebarComponent } from '../sidebar/sidebar.component';
import { TopbarComponent } from '../topbar/topbar.component';
import { ModuleNavComponent } from '../../shared/components/module-nav/module-nav.component';
import { FeedbackButtonComponent } from '../../features/feedback/feedback-button/feedback-button.component';
import { FeedbackModalComponent } from '../../features/feedback/feedback-modal/feedback-modal.component';
import { selectUser } from '../../core/store';
import { ThemeService } from '../../core/services/theme.service';

@Component({
  selector: 'app-main-layout',
  standalone: true,
  imports: [
    CommonModule, RouterModule,
    SidebarComponent, TopbarComponent, ModuleNavComponent,
    FeedbackButtonComponent, FeedbackModalComponent,
  ],
  templateUrl: './main-layout.component.html',
  styleUrl: './main-layout.component.scss',
})
export class MainLayoutComponent {
  private store = inject(Store);
  themeService = inject(ThemeService);
  user$ = this.store.select(selectUser);

  sidebarWidth = computed(() => this.themeService.getSidebarCollapsedWidth());
  feedbackVisible = signal(false);
}
```

**Step 2: Update main-layout.component.html**

Replace with:

```html
<app-sidebar />
<app-topbar [sidebarWidth]="sidebarWidth()" />
<div class="mt-16 transition-[margin-inline-start] duration-250 ease-[cubic-bezier(0.4,0,0.2,1)]"
     [style.margin-inline-start.px]="sidebarWidth()">
  @if (themeService.showModuleNav()) {
    <app-module-nav />
  }
  <main class="main-content p-6 min-h-[calc(100vh-4rem)] bg-surface-muted">
    <router-outlet />
  </main>
</div>

<!-- Feedback System -->
<app-feedback-button (openFeedback)="feedbackVisible.set(true)" />
<app-feedback-modal [(visible)]="feedbackVisible" />
```

**Step 3: Commit**

```bash
git add src/app/layout/main-layout/
git commit -m "feat(feedback): integrate FAB button and modal into main layout"
```

---

### Task 12: Update Auth Interceptor to Skip AI API Calls

**Files:**
- Modify: `src/app/core/interceptors/auth.interceptor.ts`

**Step 1: Update interceptor to skip external API calls**

The auth interceptor currently adds `X-Authorization` to all requests except `/assets/`. We need to also skip DeepSeek and OpenAI API calls:

Add to the skip condition at the top of the interceptor function:

```typescript
// Skip interceptor for static assets and external AI APIs
if (
  req.url.includes('/assets/') ||
  req.url.endsWith('.json') ||
  req.url.includes('api.deepseek.com') ||
  req.url.includes('api.openai.com')
) {
  return next(req);
}
```

**Step 2: Commit**

```bash
git add src/app/core/interceptors/auth.interceptor.ts
git commit -m "feat(feedback): skip auth interceptor for AI API calls"
```

---

### Task 13: Build and Deploy

**Step 1: Build the project**

Run: `cd /home/moonui/public_html/moon-erp && npx ng build --base-href /app/`

Expected: Successful build with no errors.

**Step 2: Deploy**

```bash
rm -f /home/moonui/public_html/app/*.js /home/moonui/public_html/app/*.css /home/moonui/public_html/app/*.html /home/moonui/public_html/app/*.ico
\cp -rf /home/moonui/public_html/moon-erp/dist/moon-erp/browser/* /home/moonui/public_html/app/
```

**Step 3: Verify**

Open `https://moonui.elbaset.com/app/` and verify:
1. Gold floating button appears bottom-left
2. Click opens the feedback modal
3. Type selection works
4. Element inspector works (click-to-select)
5. Screenshot capture works
6. AI report generation works (need API keys configured)
7. History shows saved reports

**Step 4: Final commit**

```bash
git add -A
git commit -m "feat(feedback): complete feedback & change request system v1"
```
