---
phase: 19-ui-integration
plan: 01
type: execute
wave: 1
depends_on: ["18-01"]
files_modified:
  - resources/views/shipments/prepare.php
  - resources/views/orders/index.php
  - src/Modules/Printing/PrintApiController.php
  - src/Modules/Printing/PrintJobRepository.php
  - resources/views/settings/printing.php
  - routes/web.php
  - resources/scss/_printing.scss
autonomous: false
---

<objective>
## Goal
Dodać przycisk "Drukuj" w widoku przesyłki (prepare.php) oraz zbiorczą akcję "Drukuj etykiety" z listy zamówień — obie wywołujące POST /api/print/jobs. Dodać podgląd kolejki wydruku w ustawieniach drukowania ze statusami zleceń.

## Purpose
Użytkownik może jednym kliknięciem wysłać etykietę do zdalnej drukarki (Windows Client z fazy 20) bez ręcznego pobierania i drukowania. Zbiorcze drukowanie oszczędza czas przy wielu zamówieniach.

## Output
- Przycisk "Drukuj" obok "Pobierz" w prepare.php z AJAX feedback
- Zbiorcza akcja "Drukuj etykiety" na liście zamówień (zaznaczone checkboxy)
- Sekcja "Kolejka wydruku" w ustawieniach drukowania z historią zleceń
</objective>

<context>
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md

## Prior Work
@.paul/phases/18-print-queue-backend/18-01-SUMMARY.md
- Phase 18 created: PrintApiController (createJob, listPending, downloadLabel, markComplete)
- API key auth middleware for Windows client
- print_jobs table (order_id, package_id, label_path, status, created_by)
- createJob() expects package_id, validates label exists

## Source Files
@resources/views/shipments/prepare.php — "Pobierz" button at line ~109
@resources/views/orders/index.php — lista zamówień z checkboxami
@src/Modules/Printing/PrintApiController.php — existing REST endpoints
@src/Modules/Printing/PrintJobRepository.php — DB operations
@resources/views/settings/printing.php — API key management
@routes/web.php — route registration
</context>

<skills>
## Required Skills (from SPECIAL-FLOWS.md)

| Skill | Priority | When to Invoke | Loaded? |
|-------|----------|----------------|---------|
| sonar-scanner | required | Po APPLY, przed UNIFY | ○ |

## Skill Invocation Checklist
- [ ] sonar-scanner loaded (run before UNIFY)
</skills>

<acceptance_criteria>

## AC-1: Przycisk "Drukuj" w widoku przesyłki
```gherkin
Given przesyłka ma wygenerowaną etykietę (label_path istnieje)
When użytkownik klika "Drukuj" obok przycisku "Pobierz"
Then AJAX POST /api/print/jobs z package_id
And przycisk zmienia się na "Wysłano ✓" z krótkim feedbackiem
And w razie błędu wyświetla OrderProAlerts.alert z komunikatem
```

## AC-2: Zbiorcze drukowanie z listy zamówień
```gherkin
Given użytkownik zaznaczył ≥1 zamówienie z etykietą na liście zamówień
When klika akcję "Drukuj etykiety" (bulk action)
Then dla każdego zaznaczonego zamówienia tworzony jest print job
And wyświetla podsumowanie: "Wysłano N zleceń do drukarki"
And zamówienia bez etykiety są pomijane z informacją
```

## AC-3: Kolejka wydruku w ustawieniach
```gherkin
Given użytkownik otwiera Ustawienia > Drukowanie
When strona się ładuje
Then widzi sekcję "Kolejka wydruku" pod sekcją "Klucze API"
And lista pokazuje ostatnie zlecenia: data, zamówienie, status (pending/completed/failed)
And może filtrować po statusie
```

## AC-4: Endpoint bulk create
```gherkin
Given request POST /api/print/jobs/bulk z tablicą package_ids
When co najmniej 1 package_id ma etykietę
Then tworzy print jobs dla wszystkich valid packages
And zwraca JSON z listą created + skipped
```

</acceptance_criteria>

<tasks>

<task type="auto">
  <name>Task 1: Przycisk "Drukuj" w prepare.php + bulk endpoint</name>
  <files>
    resources/views/shipments/prepare.php,
    src/Modules/Printing/PrintApiController.php,
    src/Modules/Printing/PrintJobRepository.php,
    routes/web.php
  </files>
  <action>
    1. W prepare.php obok przycisku "Pobierz" (form POST label) dodaj przycisk "Drukuj":
       - Button z klasą `btn btn-sm btn-outline-primary btn-print-label`
       - data-package-id="{$pkg['id']}" data-order-id="{$orderId}"
       - Ikona drukarki (fa-print lub SVG)
       - Widoczny tylko gdy $pkgLabelPath istnieje (ten sam warunek co "Pobierz")

    2. JavaScript AJAX handler na dole prepare.php (lub w osobnym module):
       - Click `.btn-print-label` → POST /api/print/jobs z {package_id, _token}
       - Success → zmień tekst na "Wysłano ✓", disable button na 3s
       - Error → OrderProAlerts.alert('Błąd: ' + response.error)
       - Duplikat (job already pending) → OrderProAlerts.alert('Zlecenie już w kolejce')

    3. W PrintApiController dodaj metodę bulkCreateJobs():
       - Przyjmuje JSON {package_ids: [1,2,3], _token}
       - Waliduje CSRF
       - Dla każdego package_id: sprawdź label_path, utwórz job lub skip
       - Zwraca {created: [{id, package_id}], skipped: [{package_id, reason}]}

    4. W PrintJobRepository dodaj findPendingByPackageId(int $packageId):
       - Sprawdza czy istnieje pending job dla danego package
       - Używane do ochrony przed duplikatami

    5. W routes/web.php dodaj:
       - POST /api/print/jobs/bulk → PrintApiController::bulkCreateJobs (session auth)

    Avoid: Nie zmieniaj istniejących endpointów API key auth (GET /api/print/jobs/pending itd.)
    Avoid: Nie dodawaj natywnych alert() — używaj OrderProAlerts
  </action>
  <verify>
    - Przycisk "Drukuj" widoczny obok "Pobierz" w prepare.php
    - AJAX POST tworzy rekord w print_jobs
    - Bulk endpoint zwraca poprawny JSON
    - Duplikat nie tworzy drugiego pending job
  </verify>
  <done>AC-1 satisfied (przycisk + AJAX), AC-4 satisfied (bulk endpoint)</done>
</task>

<task type="auto">
  <name>Task 2: Zbiorcze drukowanie z listy zamówień + kolejka w ustawieniach</name>
  <files>
    resources/views/orders/index.php,
    resources/views/settings/printing.php,
    src/Modules/Settings/PrintSettingsController.php,
    src/Modules/Printing/PrintJobRepository.php,
    routes/web.php,
    resources/scss/_printing.scss
  </files>
  <action>
    1. W orders/index.php dodaj bulk action "Drukuj etykiety":
       - Dodaj opcję w dropdown/select akcji zbiorczych (wzoruj się na istniejących bulk actions)
       - JavaScript: zbierz package_ids z zaznaczonych zamówień
       - POST /api/print/jobs/bulk z zebraną tablicą
       - Pokaż wynik: "Wysłano {N} zleceń. Pominięto {M} (brak etykiety)."
       - Użyj OrderProAlerts.alert() dla wyniku

    2. W PrintJobRepository dodaj getRecentJobs(int $limit = 50, ?string $statusFilter = null):
       - SELECT z JOIN na orders (numer zamówienia) i shipment_packages (tracking)
       - Sortowanie: created_at DESC
       - Opcjonalny filtr statusu

    3. W PrintSettingsController dodaj metodę index() rozszerzoną o dane kolejki:
       - Pobierz listę ostatnich print jobs (getRecentJobs)
       - Przekaż do widoku

    4. W settings/printing.php pod sekcją "Klucze API" dodaj sekcję "Kolejka wydruku":
       - Tabela: Data | Zamówienie | Tracking | Status | Akcje
       - Status badges: pending (żółty), completed (zielony), failed (czerwony)
       - Filtr statusu (select/buttons nad tabelą)
       - Paginacja jeśli >20 rekordów (lub scroll)
       - Przycisk "Ponów" przy failed jobs (POST /api/print/jobs z tym samym package_id)

    5. Dodaj _printing.scss z stylami:
       - .print-status-badge (pending/completed/failed kolory)
       - .print-queue-table (kompaktowy layout)
       - Zaimportuj w głównym pliku SCSS

    Avoid: Nie modyfikuj sekcji API keys w printing.php — dodaj nową sekcję pod spodem
    Avoid: Nie dodawaj osobnej strony — kolejka jest częścią ustawień drukowania
  </action>
  <verify>
    - Bulk action widoczna na liście zamówień
    - Zaznaczenie + klik → zlecenia w print_jobs
    - Kolejka wydruku widoczna w ustawieniach
    - Filtrowanie po statusie działa
    - Style badge'ów poprawne
  </verify>
  <done>AC-2 satisfied (bulk z listy), AC-3 satisfied (kolejka w ustawieniach)</done>
</task>

<task type="checkpoint:human-verify" gate="blocking">
  <what-built>
    Przycisk "Drukuj" w widoku przesyłki, zbiorcze drukowanie z listy zamówień,
    kolejka wydruku w ustawieniach drukowania.
  </what-built>
  <how-to-verify>
    1. Otwórz zamówienie z wygenerowaną etykietą → widok przesyłki
    2. Sprawdź: obok "Pobierz" jest przycisk "Drukuj"
    3. Kliknij "Drukuj" → powinno pojawić się "Wysłano ✓"
    4. Kliknij ponownie → powinien pojawić się alert "Zlecenie już w kolejce"
    5. Wróć do listy zamówień → zaznacz kilka z etykietami
    6. Wybierz akcję "Drukuj etykiety" → podsumowanie ile wysłano/pominięto
    7. Otwórz Ustawienia > Drukowanie → sprawdź sekcję "Kolejka wydruku"
    8. Sprawdź: widać zlecenia z kroków 3 i 6 ze statusem "pending"
    9. Sprawdź filtrowanie po statusie
  </how-to-verify>
  <resume-signal>Type "approved" to continue, or describe issues to fix</resume-signal>
</task>

</tasks>

<boundaries>

## DO NOT CHANGE
- database/migrations/* (schemat z fazy 18 jest stabilny)
- src/Modules/Printing/ApiKeyMiddleware.php (auth middleware gotowe)
- Endpointy API key auth (GET /api/print/jobs/pending, download, complete) — te są dla Windows Client

## SCOPE LIMITS
- Nie buduj Windows Client (to faza 20)
- Nie dodawaj WebSocket/polling do auto-odświeżania statusów — wystarczy ręczne odświeżenie
- Nie zmieniaj flow pobierania etykiet ("Pobierz" pozostaje bez zmian)
- Nie dodawaj nowych tabel DB — używaj istniejącej print_jobs

</boundaries>

<verification>
Before declaring plan complete:
- [ ] Przycisk "Drukuj" widoczny w prepare.php obok "Pobierz"
- [ ] AJAX feedback działa (success + error + duplikat)
- [ ] Bulk endpoint /api/print/jobs/bulk tworzy zlecenia
- [ ] Bulk action w index.php działa z checkboxami
- [ ] Kolejka wydruku widoczna w ustawieniach drukowania
- [ ] Filtrowanie statusu w kolejce
- [ ] Style SCSS skompilowane (brak inline CSS)
- [ ] Brak natywnych alert()/confirm() — tylko OrderProAlerts
- [ ] Wszystkie acceptance criteria spełnione
</verification>

<success_criteria>
- Wszystkie 4 AC spełnione
- Brak regresji w istniejącym flow pobierania etykiet
- Kod zgodny z konwencjami projektu (camelCase, Medoo, XSS escape)
- Brak nowych issues SonarQube na zmienionych plikach
</success_criteria>

<output>
After completion, create `.paul/phases/19-ui-integration/19-01-SUMMARY.md`
</output>
