---
phase: 28-shipment-tracking-ui
plan: 01
type: execute
wave: 1
depends_on: ["27-01"]
files_modified:
  - resources/views/orders/show.php
  - resources/views/shipments/prepare.php
  - resources/views/settings/cron.php
  - resources/scss/modules/_shipments.scss
  - src/Modules/Settings/CronSettingsController.php
  - src/Modules/Cron/CronRepository.php
autonomous: false
---

<objective>
## Goal
Wyświetlić status dostawy przesyłek w UI (szczegóły zamówienia + strona przygotowania przesyłki), dodać ustawienie interwału trackingu w zakładce crona, oraz obsłużyć link śledzenia dla przesyłek ręcznych.

## Purpose
Sprzedawca widzi aktualny status dostawy bez opuszczania aplikacji — oszczędza czas i eliminuje ręczne sprawdzanie na stronach przewoźników.

## Output
- Kolumna "Status dostawy" w tabelach paczek (show.php + prepare.php)
- Kolorowe badge'e statusów z polskimi labelami
- Link śledzenia dla przesyłek z tracking number (URL budowany z providera)
- Ustawienie interwału trackingu crona w settings/cron.php
- Sekcja tracking info w boksie "Płatność i wysyłka"
</objective>

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

## Prior Work
@.paul/phases/27-shipment-tracking-backend/27-01-SUMMARY.md

## Source Files
@resources/views/orders/show.php
@resources/views/shipments/prepare.php
@resources/views/settings/cron.php
@src/Modules/Settings/CronSettingsController.php
@src/Modules/Cron/CronRepository.php
@src/Modules/Shipments/DeliveryStatus.php
@src/Modules/Shipments/ShipmentPackageRepository.php
</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 uruchomiony po zakończeniu APPLY
</skills>

<acceptance_criteria>

## AC-1: Status dostawy w tabeli paczek na stronie zamówienia
```gherkin
Given zamówienie ma przesyłkę ze statusem delivery_status = 'in_transit' i delivery_status_raw = 'adopted_at_sorting_center'
When użytkownik otwiera stronę szczegółów zamówienia
Then w tabeli paczek (zakładka Przesyłki) widoczna jest kolumna "Status dostawy"
And status wyświetla się jako kolorowy badge "W tranzycie"
And po najechaniu (title) widać surowy status: "adopted_at_sorting_center — Przyjęta w centrum sortowania"
```

## AC-2: Status dostawy w tabeli paczek na stronie przygotowania przesyłki
```gherkin
Given zamówienie ma istniejące paczki z różnymi delivery_status
When użytkownik otwiera stronę przygotowania przesyłki
Then w sekcji "Wygenerowane przesyłki" widoczna jest kolumna "Status dostawy"
And każda paczka pokazuje badge ze statusem dostawy
```

## AC-3: Link śledzenia przesyłki
```gherkin
Given paczka ma tracking_number i znany provider (inpost/apaczka/allegro_wza)
When użytkownik widzi tabelę paczek
Then obok numeru śledzenia wyświetla się ikonka linku (🔗) otwierająca stronę śledzenia przewoźnika
And dla InPost link to https://inpost.pl/sledzenie-przesylek?number={tracking_number}
And dla Apaczka link to https://www.apaczka.pl/sledz-paczke/?numer={tracking_number}
And dla Allegro link budowany z allegro tracking URL
And dla manual bez linku (chyba że user poda URL w przyszłości)
```

## AC-4: Ustawienie interwału trackingu w cronie
```gherkin
Given użytkownik jest na stronie Ustawienia > Cron
When widzi tabelę harmonogramów crona
Then przy rekordzie shipment_tracking_sync widoczny jest edytowalny interwał (w minutach)
And po zmianie wartości i zapisaniu formularza interwał jest aktualizowany w cron_schedules
And dozwolone wartości: 5-120 minut
```

## AC-5: Info o śledzeniu w boksie "Płatność i wysyłka"
```gherkin
Given zamówienie ma przesyłkę z delivery_status != 'unknown'
When użytkownik otwiera szczegóły zamówienia
Then w boksie "Płatność i wysyłka" widoczna jest sekcja "Status dostawy"
And wyświetla znormalizowany status z kolorowym badge + datę ostatniej aktualizacji
And dla wielu paczek pokazuje status najnowszej (ostatnio zaktualizowanej)
```

</acceptance_criteria>

<tasks>

<task type="auto">
  <name>Task 1: Style SCSS + DeliveryStatus badge helper + tracking URL builder</name>
  <files>resources/scss/modules/_shipments.scss, src/Modules/Shipments/DeliveryStatus.php</files>
  <action>
    **SCSS — badge'e statusów dostawy** (w _shipments.scss, lub nowy plik jeśli nie istnieje):
    - `.delivery-badge` — bazowy styl: inline-block, padding 2px 8px, border-radius 3px, font-size 0.8em
    - `.delivery-badge--unknown` — szary (#999 bg, #fff text)
    - `.delivery-badge--created` — jasnoniebieski (#e3f2fd bg, #1565c0 text)
    - `.delivery-badge--confirmed` — niebieski (#bbdefb bg, #0d47a1 text)
    - `.delivery-badge--in_transit` — pomarańczowy (#fff3e0 bg, #e65100 text)
    - `.delivery-badge--out_for_delivery` — ciemnopomarańczowy (#ffe0b2 bg, #bf360c text)
    - `.delivery-badge--ready_for_pickup` — fioletowy (#f3e5f5 bg, #6a1b9a text)
    - `.delivery-badge--delivered` — zielony (#e8f5e9 bg, #2e7d32 text)
    - `.delivery-badge--returned` — czerwony (#ffebee bg, #c62828 text)
    - `.delivery-badge--cancelled` — ciemnoszary (#e0e0e0 bg, #616161 text)
    - `.delivery-badge--problem` — żółto-czerwony (#fff8e1 bg, #f57f17 text)

    **DeliveryStatus — metoda trackingUrl()**:
    - Dodaj static method `trackingUrl(string $provider, string $trackingNumber): ?string`
    - InPost: `https://inpost.pl/sledzenie-przesylek?number={trackingNumber}`
    - Apaczka: `https://www.apaczka.pl/sledz-paczke/?numer={trackingNumber}`
    - Allegro WZA: `https://allegro.pl/przesylka/{trackingNumber}` (generyczny tracking Allegro)
    - Manual / unknown: return null
    - Zwracaj null jeśli trackingNumber jest pusty

    Zbuilduj SCSS: sprawdź jak inne pliki SCSS są buildowane w projekcie (npx sass lub ręczny build).
    Avoid: nie dodawaj nowych zależności npm/composer
  </action>
  <verify>
    php -l src/Modules/Shipments/DeliveryStatus.php
    Sprawdź że DeliveryStatus::trackingUrl('inpost', 'ABC123') zwraca poprawny URL
    Sprawdź że SCSS kompiluje się bez błędów
  </verify>
  <done>AC-3 (częściowo) satisfied: tracking URL builder gotowy</done>
</task>

<task type="auto">
  <name>Task 2: UI — status dostawy w show.php i prepare.php + boks Płatność i wysyłka</name>
  <files>resources/views/orders/show.php, resources/views/shipments/prepare.php</files>
  <action>
    **show.php — tabela paczek (zakładka Przesyłki):**
    - Dodaj kolumnę "Status dostawy" po kolumnie "Status" w nagłówku tabeli
    - Dla każdej paczki wyświetl:
      ```php
      $deliveryStatus = $pkg['delivery_status'] ?? 'unknown';
      $deliveryRaw = $pkg['delivery_status_raw'] ?? '';
      $label = \App\Modules\Shipments\DeliveryStatus::label($deliveryStatus);
      $description = $deliveryRaw !== '' ? \App\Modules\Shipments\DeliveryStatus::description($pkg['provider'] ?? '', $deliveryRaw) : '';
      $title = $deliveryRaw !== '' ? e($deliveryRaw) . ' — ' . e($description) : '';
      ```
    - Badge: `<span class="delivery-badge delivery-badge--{$deliveryStatus}" title="{$title}">{$label}</span>`
    - Kolumna "Nr śledzenia": dodaj link śledzenia obok numeru tracking
      ```php
      $trackingUrl = \App\Modules\Shipments\DeliveryStatus::trackingUrl($pkg['provider'] ?? '', $pkg['tracking_number'] ?? '');
      ```
      Jeśli trackingUrl !== null: `<a href="{$trackingUrl}" target="_blank" title="Śledź przesyłkę">🔗</a>`

    **show.php — boks "Płatność i wysyłka":**
    - Po istniejących wierszach (carrier, send_date_max, shipments count) dodaj wiersz "Status dostawy"
    - Pokaż status najnowszej paczki (ostatnia z $packagesList posortowana by delivery_status_updated_at DESC)
    - Badge + data ostatniej aktualizacji: `<span class="delivery-badge ...">{label}</span> <small>{date}</small>`
    - Wyświetlaj TYLKO jeśli $packagesList nie jest pusta i najnowszy delivery_status != 'unknown'

    **prepare.php — sekcja "Wygenerowane przesyłki":**
    - Dodaj kolumnę "Status dostawy" analogicznie jak w show.php
    - Badge z tooltip surowego statusu
    - Link śledzenia obok tracking number

    Avoid:
    - NIE dodawaj inline CSS — używaj klas z _shipments.scss
    - NIE zmieniaj logiki kontrolerów — dane delivery_status są już w $packagesList z bazy
    - Helper e() do escape HTML
  </action>
  <verify>
    Ręczna weryfikacja wizualna (checkpoint)
  </verify>
  <done>AC-1, AC-2, AC-3, AC-5 satisfied</done>
</task>

<task type="checkpoint:human-verify" gate="blocking">
  <what-built>Status dostawy w tabelach paczek (show.php + prepare.php) oraz w boksie Płatność i wysyłka. Kolorowe badge'e, tooltip z surowym statusem, linki śledzenia.</what-built>
  <how-to-verify>
    1. Otwórz stronę szczegółów zamówienia które ma przesyłkę
    2. Sprawdź zakładkę "Przesyłki" — czy jest kolumna "Status dostawy" z badge
    3. Najedź na badge — czy tooltip pokazuje surowy status
    4. Sprawdź link 🔗 obok numeru śledzenia — czy otwiera stronę przewoźnika
    5. Sprawdź boks "Płatność i wysyłka" — czy jest wiersz "Status dostawy"
    6. Otwórz stronę przygotowania przesyłki — czy sekcja istniejących paczek ma kolumnę statusu
  </how-to-verify>
  <resume-signal>Type "approved" to continue, or describe issues to fix</resume-signal>
</task>

<task type="auto">
  <name>Task 3: Ustawienie interwału trackingu w cronie</name>
  <files>resources/views/settings/cron.php, src/Modules/Settings/CronSettingsController.php, src/Modules/Cron/CronRepository.php</files>
  <action>
    **CronRepository — nowa metoda:**
    - `updateScheduleInterval(string $jobType, int $intervalSeconds): void`
    - UPDATE cron_schedules SET interval_seconds = :interval WHERE job_type = :job_type

    **CronSettingsController::save() — dodaj obsługę interwału trackingu:**
    - Odczytaj POST param `tracking_interval_minutes` (int, default 15)
    - Waliduj: min 5, max 120
    - Przelicz na sekundy: $intervalSeconds = $minutes * 60
    - Wywołaj: $cronRepository->updateScheduleInterval('shipment_tracking_sync', $intervalSeconds)

    **cron.php — dodaj sekcję ustawień trackingu:**
    - Pod istniejącym formularzem "Uruchamianie z poziomu web" dodaj nową sekcję
    - Nagłówek: "Śledzenie przesyłek"
    - Input number: `tracking_interval_minutes`, min=5, max=120
    - Wartość domyślna: aktualny interval_seconds z cron_schedules / 60
    - Label: "Interwał sprawdzania statusu (minuty)"
    - Opis: "Jak często system automatycznie sprawdza status dostawy przesyłek"
    - Umieść w tym samym formularzu POST (żeby jedno "Zapisz" zapisywało wszystko)

    **CronSettingsController::index() — przekaż interwał do widoku:**
    - Pobierz z $schedulesList rekord job_type='shipment_tracking_sync'
    - Oblicz: $trackingIntervalMinutes = (int)(interval_seconds / 60)
    - Przekaż do widoku

    Avoid:
    - NIE twórz osobnego formularza — rozszerz istniejący
    - NIE usuwaj istniejących ustawień crona
  </action>
  <verify>
    php -l na zmienionych plikach PHP
    Sprawdź że zmiana interwału na 10 minut zapisuje interval_seconds=600 w cron_schedules
  </verify>
  <done>AC-4 satisfied: interwał trackingu konfigurowalny w UI</done>
</task>

</tasks>

<boundaries>

## DO NOT CHANGE
- src/Modules/Shipments/ShipmentProviderInterface.php
- src/Modules/Shipments/InpostShipmentService.php
- src/Modules/Shipments/ApaczkaShipmentService.php
- src/Modules/Shipments/AllegroShipmentService.php
- src/Modules/Shipments/ShipmentTrackingInterface.php
- src/Modules/Shipments/InpostTrackingService.php (Phase 27)
- src/Modules/Shipments/ApaczkaTrackingService.php (Phase 27)
- src/Modules/Shipments/AllegroTrackingService.php (Phase 27)
- src/Modules/Cron/ShipmentTrackingHandler.php (Phase 27)
- src/Modules/Cron/CronHandlerFactory.php (Phase 27)
- database/migrations/* (brak nowych migracji w tej fazie)

## SCOPE LIMITS
- Tylko UI i ustawienia — bez zmian w logice backendu trackingu
- Brak nowych migracji DB
- Brak nowych routów API
- Brak JavaScript — badge'e i linki to czysty HTML/PHP
- Tracking URL budowany statycznie z providera + tracking_number (nie z API)

</boundaries>

<verification>
Before declaring plan complete:
- [ ] php -l przechodzi na wszystkich zmienionych plikach
- [ ] SCSS kompiluje się bez błędów
- [ ] Badge'e statusów wyświetlają się poprawnie w show.php
- [ ] Badge'e statusów wyświetlają się poprawnie w prepare.php
- [ ] Link śledzenia otwiera poprawny URL dla InPost/Apaczka/Allegro
- [ ] Boks "Płatność i wysyłka" pokazuje status najnowszej paczki
- [ ] Interwał trackingu zapisuje się poprawnie w cron_schedules
- [ ] Istniejące ustawienia crona działają bez zmian
- [ ] All acceptance criteria met
</verification>

<success_criteria>
- Wszystkie taski ukończone (+ checkpoint approved)
- Badge'e widoczne z poprawnymi kolorami
- Linki śledzenia działają dla 3 providerów
- Interwał trackingu konfigurowalny 5-120 min
- Brak nowych zależności
</success_criteria>

<output>
After completion, create `.paul/phases/28-shipment-tracking-ui/28-01-SUMMARY.md`
</output>
