feat(28-shipment-tracking-ui): badge'e statusow dostawy, linki sledzenia, ustawienia interwalu trackingu

- Kolorowe badge'e statusow dostawy w tabelach paczek (show.php + prepare.php)
- Link sledzenia z carrier detection (InPost, Apaczka, Orlen, Allegro, Google fallback)
- Sekcja Status dostawy w boksie Platnosc i wysylka
- Ustawienie interwalu trackingu crona (5-120 min) w zakladce Ustawienia
- Tekstowe mapowania statusow Apaczka API (NEW, CONFIRMED, etc.)
- Fix: use-statements ApaczkaShipmentService (pre-existing bug)
- Fix: pickup date normalization (next day po 16:00)
- Fix: przycisk Pobierz etykiete (POST zamiast link do prepare)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-23 23:04:05 +01:00
parent 228c0e96cf
commit 98a0077204
17 changed files with 1108 additions and 174 deletions

View File

@@ -0,0 +1,294 @@
---
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>