Log aktywności
View on GitHubLog aktywności
Każda zmieniająca projekt akcja w całej aplikacji zapisuje zwykły tekstowy wiersz ActivityLog — "Changed Jane's role to admin", "Deleted the "Owner" role" — i jest dziś odczytywany z powrotem w dwóch miejscach: panelu "Recent Work Activity" na Dashboardzie (na poziomie użytkownika, przez ActivityLogService::paginateForUser()) oraz widoku issues Activity projektu (na poziomie projektu, przez paginateForProject()), oba z paginacją. Ta kategoria dokumentuje stronę zapisu (już używaną wszędzie) oraz to, jak wyświetlić stronę odczytu w trzecim miejscu.
Przewodniki, w kolejności, w jakiej faktycznie będziesz ich potrzebować
- Zaloguj nowy rodzaj aktywnościLog aktywnościZaloguj nowy rodzaj aktywnościNie ma żadnego enuma, żadnego kroku rejestracji, żadnej mapy na froncie do zaktualizowania — zalogowanie nowego rodzaju aktywności to pojedyncze wywołanie… — jednolinijkowy wzorzec, jakiego już trzyma się każdy Service; przeczytaj to najpierw, ponieważ to wzorzec, którego prawie na pewno już poprawnie używasz, nie zdając sobie sprawy, że to "system logu aktywności".
- Wyświetl log aktywności w UILog aktywnościWyświetl log aktywności w UIActivityLogService::paginateForUser() już zasila panel "Recent Work Activity" na Dashboardzie, a paginateForProject() już zasila widok issues Activity proj… — przećwiczony przykład dodania trzeciej powierzchni odczytu (aktywność konta), ponownie wykorzystujący dokładnie te same komponenty
ActivityLogs/ActivityLogItem, które Dashboard i widok Activity projektu już współdzielą.
Architektura w jednym akapicie
ActivityLog (app/Models/ActivityLog.php) jest celowo niestrukturalny: project_id (nullable — null dla aktywności na poziomie konta, jak zmiana hasła), user_id oraz pojedyncza kolumna wolnego tekstu body — nie ma żadnego enuma type/action do rozszerzenia, w przeciwieństwie do powiadomieńPowiadomienia (w aplikacji i mailowe)Orbit informuje użytkowników o aktywności na dwa sposoby: powiadomieniem w aplikacji (dropdown z dzwonkiem) oraz mailem, w podziale na App\Enums\Notificati… czy alertówAlerty UI (toasty)Mały, przejściowy toast w prawym górnym rogu ekranu — jednolinijkowe potwierdzenie lub błąd, znikający automatycznie po kilku sekundach — to zupełnie inny…. ActivityLogService::log(?int $projectId, string $body, ?int $userId = null) to jedyna metoda, jaką wywołuje każdy inny Service jako efekt uboczny tego, co faktycznie robi — $userId domyślnie to auth()->id(), gdy pominięty, co jest powodem, dla którego większość miejsc wywołania (zobacz np. RoleService/ProjectMemberService) nigdy nie przekazuje go jawnie. Po stronie odczytu ActivityLogRepository ma paginateForUser() i paginateForProject() (od najnowszych, z id jako dodatkowym kluczem sortowania, dociągające eager-loadingiem user, z rozmiarem strony ograniczonym do ActivityLogRepository::MAX_PER_PAGE) oraz niepaginowane getRecentForUser() i getRecentForProject() (latest()->limit($limit)) dla powierzchni, które potrzebują tylko krótkiej listy. DashboardController::index() mapuje paginateForUser($userId, 10) na prop activityLogs, a ProjectController::show() mapuje paginateForProject($project->id, 20) na swój własny prop activityLogs dla widoku issues Activity (zobacz ../issue-views/README.mdWidoki issues (List / Board / Calendar / Activity)Issues projektu renderują się jako sortowalna tabela, pogrupowana po statusie tablica Kanban, kalendarz ułożony wg dat issue, albo chronologiczny feed akty…). Te propsy to paginatory Laravela ({ data, links, from, to, total, ... }, mapowane przez ->through()), a pager czyta numer i rozmiar strony z activityPage/activityPerPage (ActivityLogService::PAGE_NAME/PER_PAGE_NAME), żeby na stronie projektu nie kolidował z page/perPage listy issues. Po stronie frontendu obie strony renderują wspólną molekułę Pagination pod listą, przekazując pageParam, perPageParam i defaultPerPage (ACTIVITY_PAGE_PARAM/ACTIVITY_PER_PAGE_PARAM w resources/js/utils/activityLog.ts). Oba Controllery mapują do dokładnie tego samego kształtu wpisu — { id, body, userName, createdAt }, createdAt już sformatowane po stronie serwera przez created_at->diffForHumans() — typowanego raz jako ActivityLogEntry (resources/js/types/ActivityLog.ts) i renderowanego przez tę samą parę komponentów frontendowych niezależnie od strony: ActivityLogs (lista + pusty stan) opakowujący ActivityLogItem (jeden wpis — kolorowa kropka wyprowadzona z treści body przez getActivityLogVisual() w resources/js/utils/activityLog.ts, imię użytkownika, treść i znacznik czasu). Ponowne wykorzystanie dokładnie tej pary komponentów sprawia, że trzecia powierzchnia z przewodnika 2 to mała zmiana, a nie nowy UI od zera.
