Oś czasu statusu zamówienia, która wyjaśnia, co się dzieje, co będzie dalej i kiedy trzeba się martwić, używając prostego modelu zdarzeń, który utrzymuje spójność aktualizacji.

status = shipped), tracisz historię. Gdy klient pyta „Kiedy to wysłano?” albo „Dlaczego cofnęło się do poprzedniego statusu?”, nie masz czystej odpowiedzi. Dzięki zdarzeniom masz historię i audyt, którym można zaufać.\n\n### Najmniejszy użyteczny rekord zdarzenia\n\nTrzymaj rekord mały i nudny. Zawsze możesz dodać więcej później.\n\n- order_id: do którego zamówienia należy zdarzenie\n- event_type: co się wydarzyło (picked_up, out_for_delivery, delivered)\n- happened_at: kiedy to się wydarzyło (czas rzeczywistego działania)\n- actor: kto to spowodował (system, magazyn, przewoźnik, support)\n- details: niewielkie dodatkowe dane (numer przesyłki, lokalizacja, notatka)\n\nGdy UI renderuje oś czasu, sortuje zdarzenia po happened_at i je wyświetla. Jeśli później zmienisz, jak nazywasz stany widoczne dla klienta, możesz odwzorować typy zdarzeń bez przepisywania historii.\n\n### Idempotentność (brak duplikatów)\n\nSystemy dostawcze często wysyłają te same aktualizacje ponownie. Idempotentność oznacza: jeśli to samo zdarzenie przyjdzie dwa razy, nie powinno tworzyć dwóch wpisów na osi czasu.\n\nNajprostsze podejście to nadanie każdemu przychodzącemu zdarzeniu stabilnego unikalnego klucza (np. ID zdarzenia przewoźnika lub hash order_id + event_type + happened_at + tracking_number) i zapisanie go. Jeśli pojawi się ponownie, ignorujesz je.\n\n## Wybór właściwych zdarzeń i mapowanie na oś czasu\n\nOś czasu najlepiej działa, gdy odzwierciedla prawdziwe momenty, które klient rozpoznaje, a nie twoje wewnętrzne zadania. Zacznij od listy punktów, w których coś naprawdę się zmienia dla kupującego: zostały pobrane pieniądze, istnieje paczka, przewoźnik ją ma, dotarła.\n\n### Wybieraj zdarzenia z rzeczywistych momentów\n\nMały zestaw zwykle wystarcza, by odpowiedzieć na większość pytań „Gdzie jest moje zamówienie?”:\n\n- Potwierdzenie płatności\n- Utworzono etykietę\n- Przekazano przewoźnikowi (pierwszy skan przewoźnika, jeśli masz)\n- W drodze do dostawy\n- Dostarczone\n\nTrzymaj nazwy spójne i konkretne. „Spakowane” i „Gotowe” brzmią podobnie, ale znaczą dla klienta różne rzeczy. Wybierz jedno znaczenie dla każdego zdarzenia i nie używaj tej samej etykiety dla innego momentu.\n\n### Zdecyduj, co klienci zobaczą\n\nNie każde zdarzenie backendowe powinno trafiać do UI. Niektóre są tylko dla twojego zespołu (kontrola antyfraudowa, rozpoczęcie kompletacji, walidacja adresu). Dobra zasada: jeśli pokazanie tego stworzyłoby więcej pytań niż odpowiedzi, trzymaj to wewnętrznie.\n\nMapuj wewnętrzne kroki na mniej stanów dla klienta. Możesz mieć pięć kroków magazynowych, ale oś czasu pokazuje tylko „Przygotowywane” aż do „Przekazano przewoźnikowi”. To utrzymuje UI spokojne i przewidywalne.\n\nCzas ma taką samą wagę jak etykieta. Gdy możesz, zapisz dwa znaczniki czasu: kiedy zdarzenie się wydarzyło i kiedy je zarejestrowano. Pokaż czas wystąpienia w UI (czas skanu przewoźnika, czas potwierdzenia doręczenia). Jeśli pokazujesz tylko czas przetwarzania, późny import może sprawić, że będzie wyglądać, jakby paczka cofnęła się w czasie.\n\nDane od przewoźnika będą czasem brakować. Zaplanuj to. Jeśli nigdy nie otrzymasz skanu „Przekazano przewoźnikowi”, odwołaj się do „Utworzono etykietę” z jasnym komunikatem: „Oczekiwanie na skan przewoźnika.” Unikaj wymyślania postępu.\n\nKonkretne: magazyn drukuje etykietę o 10:05, ale przewoźnik nie skanuje do 18:40. Twój model zdarzeń backend powinien zapisać oba zdarzenia, ale twoja oś czasu nie powinna sugerować ruchu w przerwie. Sama ta decyzja zapobiega wielu zgłoszeniom typu „Mówi, że wysłane, ale nie ruszyło”.\n\n## Krok po kroku: zbuduj UI osi czasu i trzymaj go w synchronizacji\n\nKrok 1: napisz najpierw oś czasu widoczną dla klienta. Wypisz 5–8 kroków, które kupujący zrozumie (np.: Zamówienie złożone, Opłacone, Spakowane, Wysłane, W drodze do dostawy, Dostarczone). Napisz dokładne zdanie, które pokażesz przy każdym kroku. Trzymaj je spokojne i konkretne.\n\nKrok 2: zdefiniuj typy zdarzeń i mapowanie. Twoje systemy mogą mieć dziesiątki wewnętrznych stanów, lecz klienci powinni widzieć mniejszy zestaw. Stwórz prostą tabelę mapowania jak warehouse.picked -> Packed i carrier.in_transit -> Shipped.\n\nKrok 3: zapisuj zdarzenia, a potem oblicz widok. Zapisuj każde zdarzenie jako rekord append-only z order_id, type, occurred_at i opcjonalnym data (np. kod przewoźnika lub powód). UI powinno być generowane z wydarzeń, nie z jednego zmiennego pola statusu.\n\nKrok 4: zwróć API gotowe do osi czasu. Odpowiedź powinna być prosta dla frontendu: kroki (z etykietami), indeks aktualnego kroku, znane czasy i krótki komunikat.\n\n```json{ "order_id": "123", "current_step": 3, "steps": [ {"key":"placed","label":"Order placed","at":"2026-01-09T10:11:00Z"}, {"key":"paid","label":"Payment confirmed","at":"2026-01-09T10:12:00Z"}, {"key":"packed","label":"Packed","at":"2026-01-09T14:40:00Z"}, {"key":"shipped","label":"Shipped","at":null,"message":"Waiting for carrier scan"} ], "last_update_at": "2026-01-09T14:40:00Z" } ```\n\n Dla osi czasu statusu zamówienia odpytywanie co 30–120 sekund jest często wystarczające podczas aktywnej wysyłki, a znacznie rzadziej, gdy nic się nie zmienia.\n\n Jeśli skan przewoźnika się opóźnia, pokaż ostatnią znaną aktualizację i wyraźną kolejną akcję: „Jeśli nie będzie aktualizacji do jutra, skontaktuj się z supportem podając zamówienie 123.”\n\nPraktyczny przykład: klient widzi „Spakowane” z timestampem, potem „Wysłane: oczekiwanie na skan przewoźnika” aż do nadejścia . Żadnych spersonalizowanych odpowiedzi, tylko uczciwy stan.\n\n## Przykład scenariusza: normalne zamówienie z realnym opóźnieniem\n\nKlient zamawia bluzę z kapturem. Płatność jest natychmiastowa, ale pakowanie zajmuje dwa dni robocze. Potem wysyłka trafia na opóźnienie u przewoźnika. Klient nadal powinien czuć się poinformowany bez konieczności kontaktu z supportem.\n\nOto oś czasu widoczna dzień po dniu (to samo UI, tylko dodawane nowe wpisy):\n\n| Dzień i czas | Pokazywany status | Komunikat prostym językiem | |---|---|---| | Pon 09:12 | Złożone zamówienie | „Otrzymaliśmy twoje zamówienie. Będziemy informować na bieżąco.” | | Pon 09:13 | Potwierdzenie płatności | „Płatność zaakceptowana. Następnie: przygotujemy paczkę.” | | Wt 16:40 | Przygotowujemy zamówienie | „Pakujemy twoje przedmioty. Szacowana data wysyłki: Śr.” | | Śr 14:05 | Wysłane | „Przekazano przewoźnikowi. Śledzenie będzie aktualizowane zgodnie ze skanami przewoźnika.” | | Czw 08:30 | W tranzycie | „W drodze. Aktualny szacunek: dostawa Pt.” | | Pt 10:10 | Dostawa opóźniona | „Przewoźnik zgłosił opóźnienie z powodu dużej ilości przesyłek. Nowy szacunek: Sb. Na razie nic nie trzeba robić.” | | Sb 12:22 | W drodze do dostawy | „U twojego lokalnego kuriera. Zazwyczaj przychodzi dziś.” | | Sb 18:05 | Dostarczone | „Dostarczono. Jeśli nie możesz znaleźć przesyłki, sprawdź wejście i sąsiadów.” | \nZauważ, co zmieniło się w piątek: nie tworzyłeś nowego przepływu. Dodałeś jedno zdarzenie (np. ) i odwzorowałeś je na jasny komunikat dla klienta. Wcześniejsze kroki pozostają bez zmian, a UI pozostaje stabilne.\n\nOpcja kontaktu powinna pojawiać się tylko po jasnym progu, by ludzie nie klikali jej z lęku. Prosta reguła działa dobrze: pokaż „Skontaktuj się z nami”, jeśli zamówienie jest 24 godziny po ostatnim obiecanym terminie dostawy albo jeśli status nie zmienił się przez 72 godziny podczas „W tranzycie”. Przed tym pokazuj zapewnienie i aktualny szacunek zamiast przycisku kontaktu.\n\n## Typowe błędy, które pogarszają śledzenie\n\nDobra oś czasu zmniejsza ilość pytań „gdzie jest moje zamówienie?”. Zła tworzy nowe pytania, bo UI i dane za nim nie zgadzają się z rzeczywistym doświadczeniem klientów.\n\n### Błąd 1: zbyt szczegółowa oś czasu\n\nJeśli ujawnisz każdy wewnętrzny krok, klienci się zgubią. Piętnaście mikrostatusów jak „picked”, „sorted”, „labeled”, „staged” i „queued” wygląda na zajęty, ale nie odpowiada na dwa realne pytania: „Kiedy dotrze?” i „Czy coś jest nie tak?” Trzymaj publiczną oś czasu do małego zestawu jasnych kamieni milowych, a resztę trzymaj wewnętrznie.\n\n### Błąd 2: tracenie historii i zmienianie przeszłości\n\nNadpisywanie aktualnego statusu bez zapisywania zdarzeń to szybka droga do sprzeczności. Klient widzi „Wysłane”, odświeża i nagle jest „Przygotowywane” z powodu retry lub ręcznej edycji. Zapisuj historię z timestampami i buduj aktualny status z tej historii.\n\nNajczęstsze pułapki są łatwe do zauważenia:\n\n- Etykiety brzmiące oficjalnie, ale nic nie mówiące (np. „W przetwarzaniu” bez wyjaśnienia)\n- Szacunki dostawy, których nie potraficie rzetelnie dotrzymać, a potem cisza gdy je mijacie\n- Brak jasnej drogi dla anulowań, zwrotów czy refundów, przez co oś czasu urywa się w połowie historii\n- Częściowe wysyłki pokazane jako jedna przesyłka, przez co „Dostarczone” wydaje się kłamstwem\n- Wyjątki przewoźnika ukryte lub zminimalizowane, przez co klienci dowiadują się o opóźnieniach od przewoźnika, a nie od ciebie\n\nDlatego ma to znaczenie. Jeden przedmiot wysyła się dziś, drugi jest na zamówieniu oczekującym. Jeśli pokazujesz tylko „Wysłane”, klient oczekuje wszystkiego. Jeśli pokażesz „Częściowo wysłano (1 z 2)” i powiążesz „Dostarczone” z każdą paczką, oś czasu pozostaje wiarygodna.\n\nTraktuj etykiety osi czasu jako copy produktowe, nie pola bazy danych. Pisz je dla ludzi, a potem mapuj swoje wewnętrzne zdarzenia na kilka przyjaznych klientowi kroków.\n\n## Szybka lista kontrolna przed wypuszczeniem\n\nZanim udostępnisz oś czasu wszystkim klientom, przejdź przez widok oczami klienta: „Gdybym zobaczył to o 23:00, czy nadal bym napisał zgłoszenie?” Cel to jasność bez brzmienia, że coś jest nie tak.\n\nZacznij od czasu i oczekiwań. Każde zamówienie powinno pokazywać ostatni czas aktualizacji i wskazówkę, co zwykle jest następne. „Ostatnia aktualizacja 2 godziny temu” plus „Następne: odbiór przez przewoźnika” zmniejsza poczucie utknięcia.\n\nUtrzymaj krótką listę przed premierą:\n\n- Każde zamówienie pokazuje jasny czas „Ostatnia aktualizacja” i prosty następny krok (nawet jeśli to „Następne: oczekiwanie na skan przewoźnika”).
Domyślnie stosuj małą, czytelną oś czasu, która odpowiada na trzy pytania: co się dzieje teraz, kiedy to się ostatnio zmieniło i co będzie dalej. Większość zgłoszeń wynika z niepewności, więc oś czasu powinna ograniczać zgadywanie (np. „Oczekiwanie na skan przewoźnika” zamiast niejasnego „W przetwarzaniu”).
Użyj stabilnego zestawu, który większość klientów zrozumie:
Dołącz też wyraźne zakończenia, takie jak Anulowane i Zwrócone. Trzymaj kroki wewnętrzne (picking/packing/batching/retry) poza widokiem klienta.
Pokaż timestamp dla każdego kroku i wyraźny czas „Ostatnia aktualizacja”. Jeśli sprzedajesz międzynarodowo, uwzględnij strefę czasową (lub uczyn ją jednoznaczną). Timestamp zmienia „nic się nie dzieje” w „to wydarzyło się niedawno”, co zapobiega dopasowaniom.
Traktuj to jako widoczny wyjątek, a nie normalny postęp. Dobry domyślny komunikat to:
Nie sugeruj ruchu, którego nie możesz udowodnić.
Oddziel fakty (zdarzenia) od osi czasu klienta (stanów). Przechowuj szczegółowe wewnętrzne zdarzenia, a potem mapuj je na kilka przyjaznych klientowi kroków. Dzięki temu UI pozostanie spójne nawet jeśli zmienią się procedury magazynowe.
Przechowuj zdarzenia jako append-only fakty (np.: label_created, picked_up, out_for_delivery, delivered) z:
Użyj idempotentności. Nadaj każdej nadchodzącej aktualizacji stabilny unikalny klucz (ID zdarzenia przewoźnika lub hash najważniejszych pól) i ignoruj duplikaty. To zapobiega wielokrotnemu pojawianiu się tych samych wpisów jak „W drodze do dostawy”.
Pokaż najlepsze znane oszacowanie i bądź szczery co do tego, na co czekasz. Jeśli nie masz ETA, powiedz to wprost (np. „Pokażemy ETA po pierwszym skanie przewoźnika”). Dokładność lepsza niż optymistyczne obietnice, które później łamią zaufanie.
Rób wyjątki oczywistymi i nastawionymi na działanie. Typowe:
Wyjątki powinny być bardziej widoczne niż normalny postęp i mówić klientowi, co (jeśli cokolwiek) ma zrobić.
Praktyczne reguły: pokaż opcje kontaktu dopiero po jasnym progu, na przykład:
Przed tym momentem pokazuj uspokajające informacje, ostatnią aktualizację i następny spodziewany krok, by zredukować nerwowe kliknięcia.
carrier.acceptedshipment_delayedorder_idevent_typehappened_atactor (system/warehouse/carrier/support)detailsNastępnie renderuj oś czasu z historii zdarzeń zamiast z pojedynczego modyfikowalnego pola statusu.