fix(auth): naprawa strony /login (subtitle, inline remember, auto-login z cookie)

- usuniety subtitle "Zaloguj sie, aby przejsc..." z widoku
- "Zapamietaj mnie (30 dni)" w jednej linii (selektor .form-field.remember-field)
- AuthController::showLogin woluje loginFromRememberToken() -> uzytkownik z waznym
  cookie remember (30 dni) jest auto-logowany po wygasnieciu sesji PHP zamiast
  ogladac formularz logowania

PLAN: .paul/plans/20260520-1200-fix-login-page-and-remember-me/

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-20 08:33:59 +02:00
parent 561e39f775
commit 2c1ad4a262
10 changed files with 321 additions and 7 deletions

View File

@@ -1,10 +1,10 @@
# STATE — orderPRO
**Tryb:** plan-first
**Ostatnia aktualizacja:** 2026-05-19
**Ostatnia aktualizacja:** 2026-05-20
## Aktywna praca
UNIFY zakonczony dla `.paul/plans/20260519-1730-refactor-delivery-status/`. Petla zamknieta, brak aktywnego planu.
UNIFY zakonczony dla `.paul/plans/20260520-1200-fix-login-page-and-remember-me/`. Petla zamknieta. Strona `/login`: usuniety subtitle, checkbox "Zapamiętaj mnie (30 dni)" w jednej linii (selektor `.form-field.remember-field`), `AuthController::showLogin` woluje `loginFromRememberToken()` -> uzytkownik z waznym cookie (30 dni) wraca automatycznie na `/settings/users` po wygasnieciu sesji. SUMMARY: `.paul/plans/20260520-1200-fix-login-page-and-remember-me/SUMMARY.md`.
```
PLAN ──▶ APPLY ──▶ UNIFY

View File

@@ -2,6 +2,22 @@
Chronologiczny log zmian technicznych (co i dlaczego). Najnowsze na gorze.
## 2026-05-20 — Strona logowania: usuniecie subtitle, inline "Zapamietaj mnie (30 dni)", auto-login z cookie
### Co
- `resources/views/auth/login.php`: usuniety element `<p class="login-subtitle">`.
- `resources/lang/pl.php`: `auth.login.remember_me` -> "Zapamiętaj mnie (30 dni)".
- `resources/scss/login.scss`: selektor `.remember-field` -> `.form-field.remember-field` (kompozytowy, specyficznosc 0,2,0 bije `.form-field { display: grid }` zdefiniowane ponizej). Compiled `public/assets/css/login.css` przebudowany przez `sass --style=compressed`.
- `src/Modules/Auth/AuthController.php`: `showLogin` woluje `loginFromRememberToken()` przed renderem formularza.
### Dlaczego
- Bug "Zapamietaj mnie nie dziala": `AuthMiddleware` poprawnie konsumowal cookie na route'ach chronionych, ale `/login` go nie wolal -> uzytkownik z waznym cookie po wygasnieciu sesji nadal widzial formularz. Backend (`AuthService::REMEMBER_DAYS=30`, httponly, SameSite=Lax, hash SHA-256 w `users.remember_token`) byl OK.
- Bug "label pod checkboxem": rownosc specyficznosci 0,1,0 + pozniejsza regula `.form-field { display: grid }` (linia 128) nadpisywala `.remember-field { display: flex }` (linia 104).
### Wplyw
- Brak zmian schematu DB, brak zmian kontraktow API, brak zmian w innych widokach.
- Plan: `.paul/plans/20260520-1200-fix-login-page-and-remember-me/PLAN.md`, SUMMARY: `.paul/plans/20260520-1200-fix-login-page-and-remember-me/SUMMARY.md`.
## 2026-05-19 — Dekompozycja DeliveryStatus (fasada + 3 wspolpracownikow)
### Co

View File

@@ -0,0 +1,7 @@
{"ts":"2026-05-20T06:23:51Z","tool":"Bash","cmd":"mkdir -p .paul/plans/20260520-1200-fix-login-page-and-remember-me\",\"description\":\"Create plan directory\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":fals","cwd":"/c/visual-studio-code/projekty/orderPRO"}
{"ts":"2026-05-20T06:25:19Z","tool":"Write","file":"C:\\\\visual-studio-code\\\\projekty\\\\orderPRO\\\\.paul\\\\plans\\\\20260520-1200-fix-login-page-and-remember-me\\\\PLAN.md","cwd":"/c/visual-studio-code/projekty/orderPRO"}
{"ts":"2026-05-20T06:25:32Z","tool":"Edit","file":"C:\\\\visual-studio-code\\\\projekty\\\\orderPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual-studio-code/projekty/orderPRO"}
{"ts":"2026-05-20T06:27:34Z","tool":"Edit","file":"C:\\\\visual-studio-code\\\\projekty\\\\orderPRO\\\\resources\\\\lang\\\\pl.php","cwd":"/c/visual-studio-code/projekty/orderPRO"}
{"ts":"2026-05-20T06:27:40Z","tool":"Edit","file":"C:\\\\visual-studio-code\\\\projekty\\\\orderPRO\\\\resources\\\\views\\\\auth\\\\login.php","cwd":"/c/visual-studio-code/projekty/orderPRO"}
{"ts":"2026-05-20T06:27:45Z","tool":"Edit","file":"C:\\\\visual-studio-code\\\\projekty\\\\orderPRO\\\\resources\\\\scss\\\\login.scss","cwd":"/c/visual-studio-code/projekty/orderPRO"}
{"ts":"2026-05-20T06:27:54Z","tool":"Bash","cmd":"npx --yes sass --style=compressed --no-source-map resources/scss/login.scss public/assets/css/login.css 2>&1 | tail -20\",\"description\":\"Build login CSS\"},\"tool_response\":{\"stdout\":\"\",\"stde","cwd":"/c/visual-studio-code/projekty/orderPRO"}

View File

@@ -0,0 +1,225 @@
---
plan_id: 20260520-1200-fix-login-page-and-remember-me
title: Strona logowania - usuniecie subtitle, inline checkbox "Zapamietaj mnie" (30 dni) + naprawa auto-logowania
storage: plan-first
legacy_phase: null
created: 2026-05-20T12:00:00+02:00
status: planned
type: execute
autonomous: true
delegation: auto
files_modified:
- resources/views/auth/login.php
- resources/lang/pl.php
- resources/scss/login.scss
- public/assets/css/login.css
- src/Modules/Auth/AuthController.php
quality_radar: ok
---
<objective>
## Goal
Poprawic UX i dzialanie strony logowania `/login`: usunac zbedny podtytul, ustawic etykiete checkboxa "Zapamietaj mnie" w jednej linii (z dopiskiem "(30 dni)") oraz upewnic sie, ze opcja zapamietania faktycznie loguje uzytkownika ponownie bez podawania hasla przez 30 dni.
## Purpose
Uzytkownik raportuje:
- zbedny tekst "Zaloguj sie, aby przejsc do obslugi zamowien i wysylek.",
- etykieta "Zapamietaj mnie" jest pod checkboxem zamiast obok,
- opcja "zapamietaj logowanie" nie dziala w praktyce (przy wejsciu na `/login` po wygasnieciu sesji uzytkownik jest proszony o ponowne logowanie, mimo zaznaczenia checkboxa przy poprzednim logowaniu).
## Output
- usuniety podtytul w widoku logowania,
- checkbox + label "Zapamietaj mnie (30 dni)" w jednej linii (flex),
- `AuthController::showLogin` proboje auto-zalogowac uzytkownika z cookie `remember_token` przed wyrenderowaniem formularza,
- nowy klucz tlumaczenia z czasem trwania (centralizacja literalu "30"),
- przebudowany `public/assets/css/login.css`.
</objective>
<context>
## Project Docs
@.paul/PROJECT.md
@.paul/STATE.md
@.paul/codebase/architecture.md
@.paul/codebase/impact_map.md
@.paul/codebase/quality_risks.md
## Source Files
@resources/views/auth/login.php
@resources/scss/login.scss
@resources/lang/pl.php
@src/Modules/Auth/AuthController.php
@src/Modules/Auth/AuthService.php
@src/Modules/Auth/AuthMiddleware.php
@src/Modules/Users/UserRepository.php
</context>
<clarifications>
- Czas zapamietania potwierdzony przez uzytkownika: **30 dni** (zgodne z `AuthService::REMEMBER_DAYS = 30`).
- Etykieta z liczba dni: dopisek "(30 dni)" w nawiasie przy "Zapamietaj mnie".
- Po pomyslnym auto-logowaniu z remember tokenem na `/login` przekierowanie zachowuje obecna logike: na `/settings/users` (jak `login()`).
</clarifications>
<impact_scan>
## Quality Radar
**Status:** ok
**Tools:** codebase-memory-mcp (architecture.md + impact_map.md przejrzane); jscpd/ast-grep disabled by policy.
## Affected Areas
- Auth UI: `resources/views/auth/login.php`, `resources/scss/login.scss`, `public/assets/css/login.css`.
- Auth backend: `src/Modules/Auth/AuthController.php` (dodanie auto-loginu w `showLogin`).
- I18n: `resources/lang/pl.php` (klucze `auth.login.subtitle` - do usuniecia z widoku, `auth.login.remember_me` - rozszerzenie o czas trwania).
## Duplicate / Hardcoded Risks
- Liczba dni "30" wystepuje w `AuthService::REMEMBER_DAYS`. Dopisek w UI to wartosc literal w tlumaczeniu - akceptowalne (UI string), ale wpisujemy ja w `pl.php` raz, nie w widoku. Jezeli kiedys zmieni sie `REMEMBER_DAYS`, trzeba zsynchronizowac napis - odnotowane w `Boundaries`.
## Explicit Deferrals
- Rotacja remember tokenu po uzyciu (`loginFromRememberToken`) - dobra praktyka, ale poza zakresem tego planu. Odlozone.
- Usuwanie samego klucza `auth.login.subtitle` z `pl.php` - zostawiamy klucz (moze byc uzyty gdzie indziej; obecnie tylko w widoku logowania). Nie usuwamy, tylko przestajemy go renderowac.
</impact_scan>
<skills>
SPECIAL-FLOWS.md nie istnieje w projekcie - sekcja pomijana.
</skills>
<acceptance_criteria>
## AC-1: Usuniecie podtytulu na stronie logowania
```gherkin
Given uzytkownik otwiera `/login`
When strona zostaje wyrenderowana
Then nie pojawia sie tekst "Zaloguj sie, aby przejsc do obslugi zamowien i wysylek."
And w DOM nie wystepuje element `.login-subtitle` w karcie logowania
```
## AC-2: Checkbox "Zapamietaj mnie" w jednej linii z dopiskiem (30 dni)
```gherkin
Given uzytkownik widzi formularz logowania
When patrzy na pole "Zapamietaj mnie"
Then checkbox i etykieta sa w jednej linii (flex, align-items: center)
And etykieta brzmi "Zapamietaj mnie (30 dni)"
And nie powstaja regresje wizualne w pozostalych polach formularza
```
## AC-3: Auto-logowanie z cookie remember na `/login`
```gherkin
Given uzytkownik wczesniej zalogowal sie z zaznaczonym "Zapamietaj mnie"
And jego sesja PHP wygasla
And cookie `remember_token` (30 dni) jest nadal wazne
When uzytkownik wchodzi bezposrednio na `/login`
Then `AuthController::showLogin` woluje `AuthService::loginFromRememberToken()`
And jezeli token jest wazny, uzytkownik zostaje zalogowany i przekierowany na `/settings/users`
And nie widzi formularza logowania
```
## AC-4: Persistencja remember tokenu 30 dni
```gherkin
Given uzytkownik loguje sie z zaznaczonym "Zapamietaj mnie"
When `AuthService::createRememberToken` wystawia cookie
Then cookie `remember_token` ma `expires = time() + 30*86400` (REMEMBER_DAYS = 30)
And cookie ma `httponly=true`, `samesite=Lax`, `secure` zgodnie z HTTPS requestu
And hash tokenu zostaje zapisany w `users.remember_token`
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Widok logowania - usun subtitle i dodaj dopisek 30 dni</name>
<files>resources/views/auth/login.php, resources/lang/pl.php</files>
<action>
1. W `resources/views/auth/login.php` usun caly element `<p class="login-subtitle">...</p>` (linia 5).
2. W `resources/lang/pl.php` (klucz `auth.login.remember_me`) zmien wartosc z `'Zapamietaj mnie'` na `'Zapamietaj mnie (30 dni)'`. Klucza `auth.login.subtitle` nie usuwamy (zachowanie wstecznej kompatybilnosci slownika).
3. Sprawdz, ze nie ma innych miejsc renderujacych `auth.login.subtitle` poza `login.php` (rg).
</action>
<verify>php -l resources/views/auth/login.php; rg "auth\.login\.(subtitle|remember_me)" resources/ -n</verify>
<done>AC-1, AC-2 (czesc tekstowa)</done>
</task>
<task type="auto">
<name>Task 2: SCSS + rebuild CSS - checkbox w jednej linii</name>
<files>resources/scss/login.scss, public/assets/css/login.css</files>
<action>
1. W `resources/scss/login.scss` dodaj override specyficznosci dla `.remember-field`, aby pokonal regule `.form-field { display: grid }` zdefiniowana ponizej. Najprostsza forma: zmien selektor `.remember-field` na `.form-field.remember-field` (dwie klasy = wyzsza specyficznosc; struktura HTML juz ma obie klasy: `class="form-field form-field--inline remember-field"`).
2. Upewnij sie, ze blok zachowuje `display: flex; align-items: center; gap: 8px;` oraz style dla `input[type="checkbox"]` i `.field-label`.
3. Przebuduj CSS: `npm run build:css` (lub recznie `sass --style=compressed --no-source-map resources/scss/login.scss public/assets/css/login.css`). Jezeli `npm`/`sass` niedostepne w srodowisku agenta - zrob recznie analogiczna zmiane w `public/assets/css/login.css` (selektor `.form-field.remember-field { display: flex; align-items: center; gap: 8px; }` z zachowaniem reszty).
</action>
<verify>rg "remember-field" resources/scss/login.scss public/assets/css/login.css -n; otwarcie `/login` w przegladarce i wizualna weryfikacja (smoke).</verify>
<done>AC-2</done>
</task>
<task type="auto">
<name>Task 3: Auto-logowanie z remember tokenem na `/login`</name>
<files>src/Modules/Auth/AuthController.php</files>
<action>
W `AuthController::showLogin` przed renderem dodaj proba auto-loginu z cookie:
```php
public function showLogin(Request $request): Response
{
if ($this->auth->check() || $this->auth->loginFromRememberToken()) {
return Response::redirect('/settings/users');
}
// ... existing render
}
```
Nie zmieniaj `login()` ani `logout()`. Pozostaw obecna logike `Flash::get(...)` bez zmian.
</action>
<verify>php -l src/Modules/Auth/AuthController.php; ewentualny smoke: zaloguj z checkboxem, wyczysc cookie sesyjne (PHPSESSID), wejdz na `/login` -> powinno przekierowac na `/settings/users`.</verify>
<done>AC-3</done>
</task>
<task type="auto">
<name>Task 4: Weryfikacja parametrow cookie remember (read-only)</name>
<files>src/Modules/Auth/AuthService.php</files>
<action>
Bez modyfikacji kodu: potwierdz, ze `REMEMBER_DAYS = 30` i parametry cookie (`httponly`, `samesite=Lax`, `secure` zalezne od HTTPS) sa zgodne z AC-4. Jezeli `$_SERVER['HTTPS']` nie jest dostepne za reverse proxy - odnotowac jako known issue (nie zmieniamy w tym planie).
</action>
<verify>rg "REMEMBER_DAYS|REMEMBER_COOKIE|setcookie" src/Modules/Auth/AuthService.php -n</verify>
<done>AC-4</done>
</task>
</tasks>
<boundaries>
## Do Not Change
- `AuthService::attempt`, `AuthService::createRememberToken`, `AuthService::loginFromRememberToken`, `AuthService::logout` - bez zmian behawioralnych.
- `AuthMiddleware` - bez zmian.
- `UserRepository::updateRememberToken`, `findByRememberToken` - bez zmian.
- Schemat tabeli `users.remember_token` - bez zmian (migracja `000081`).
- Inne widoki (`settings/*`) nawet jesli uzywaja `.form-field--inline` - nie ruszamy ich, zmiana SCSS jest dla `.form-field.remember-field` (selektor kompozytowy uzywany tylko na stronie logowania).
- Pozostale klucze tlumaczen w `pl.php` poza `auth.login.remember_me`.
## Scope Limits
- Brak rotacji remember tokenu po uzyciu (odlozone).
- Brak nowych migracji.
- Brak nowych komponentow widokow.
- Brak zmian w `routes/web.php`.
- Synchronizacja literalu "30" miedzy `REMEMBER_DAYS` a tekstem PL jest manualna - jezeli ktos zmieni `REMEMBER_DAYS`, trzeba odswiezyc tlumaczenie (akceptowalne ryzyko).
</boundaries>
<verification>
- [ ] `php -l resources/views/auth/login.php` - OK.
- [ ] `php -l src/Modules/Auth/AuthController.php` - OK.
- [ ] `rg "login-subtitle" resources/views/auth/login.php` - brak trafien.
- [ ] `rg "Zapamietaj mnie \(30 dni\)" resources/lang/pl.php` - 1 trafienie.
- [ ] `rg ".form-field.remember-field" resources/scss/login.scss public/assets/css/login.css` - po jednym trafieniu.
- [ ] Smoke manualny `/login`: brak subtitle, checkbox + label w jednej linii, dopisek "(30 dni)".
- [ ] Smoke manualny: zaloguj z checkboxem -> wyczysc tylko PHPSESSID -> wejdz `/login` -> redirect na `/settings/users`.
- [ ] Quality Radar - brak nowych ryzyk; literal "30" odnotowany w Boundaries.
</verification>
<success_criteria>
- [ ] AC-1, AC-2, AC-3, AC-4 spelnione.
- [ ] Weryfikacja przebiegla bez bledow.
- [ ] Zaktualizowany `tech_changelog.md` w fazie UNIFY (nie w APPLY) z notatka o naprawie strony logowania.
</success_criteria>
<output>
SUMMARY.md path: `.paul/plans/20260520-1200-fix-login-page-and-remember-me/SUMMARY.md`
</output>

View File

@@ -0,0 +1,67 @@
---
plan_id: 20260520-1200-fix-login-page-and-remember-me
title: Strona logowania - usuniecie subtitle, inline checkbox "Zapamietaj mnie" (30 dni) + naprawa auto-logowania
status: completed
completed: 2026-05-20
quality_radar: ok
---
## Cel
Poprawa UX i dzialania strony `/login`:
1. usuniecie zbednego podtytulu,
2. ustawienie etykiety checkboxa "Zapamietaj mnie" w jednej linii z dopiskiem "(30 dni)",
3. naprawa auto-logowania z cookie `remember_token` przy wejsciu na `/login` po wygasnieciu sesji.
## Wynik
| Acceptance Criterion | Status |
|---|---|
| AC-1 Usuniecie podtytulu | ✅ |
| AC-2 Checkbox + label w jednej linii, dopisek "(30 dni)" | ✅ |
| AC-3 Auto-login z remember tokenem na `/login` | ✅ |
| AC-4 Persistencja cookie 30 dni (`REMEMBER_DAYS=30`, httponly, SameSite=Lax) | ✅ (read-only weryfikacja) |
## Zmienione pliki
| Plik | Zmiana |
|---|---|
| `resources/views/auth/login.php` | usuniety `<p class="login-subtitle">` |
| `resources/lang/pl.php` | `auth.login.remember_me` -> "Zapamiętaj mnie (30 dni)" |
| `resources/scss/login.scss` | selektor `.remember-field` -> `.form-field.remember-field` (wyzsza specyficznosc bije `.form-field { display: grid }`) |
| `public/assets/css/login.css` | rebuild `sass --style=compressed` |
| `src/Modules/Auth/AuthController.php` | `showLogin` woluje `loginFromRememberToken()` przed renderem |
## Diagnoza root cause "Zapamietaj mnie nie dziala"
Backend byl poprawny:
- `AuthService::REMEMBER_DAYS = 30`,
- cookie `remember_token`: `httponly=true`, `samesite=Lax`, `secure` zalezne od HTTPS,
- hash SHA-256 zapisany w `users.remember_token`,
- `AuthMiddleware` poprawnie woluje `loginFromRememberToken()` na route'ach chronionych.
Bug: **`AuthController::showLogin` nie konsumowal cookie remember**. Gdy uzytkownikowi wygasala sesja PHP (`PHPSESSID`) i wracal bezposrednio na `/login`, formularz byl renderowany pomimo waznego cookie - zaden middleware nie chronil samego `/login`. Fix: dodanie `|| $this->auth->loginFromRememberToken()` w warunku redirectu.
## Diagnoza root cause "label pod checkboxem"
W `login.scss` regula `.form-field { display: grid; gap: 7px; }` (linia 128) byla zdefiniowana **po** `.remember-field { display: flex; ... }` (linia 104). Element ma obie klasy (`form-field form-field--inline remember-field`), wiec przy rownej specyficznosci pojedynczej klasy wygrywa pozniejsza regula -> grid -> stack. Fix: zmiana selektora na kompozytowy `.form-field.remember-field` (specyficznosc 0,2,0 vs 0,1,0).
## Weryfikacja
- `php -l` na wszystkich modyfikowanych plikach PHP: clean.
- Compiled CSS zawiera `.form-field.remember-field{display:flex;align-items:center;gap:8px}`.
- Brak trafien na `login-subtitle` w `resources/views/auth/login.php`.
- Sass build wykonany przez `npx sass`.
## Smoke test (do wykonania recznie)
1. `/login` - brak podtytulu, checkbox + "Zapamiętaj mnie (30 dni)" w jednej linii.
2. Login z checkboxem -> wyczysc `PHPSESSID` -> wejdz `/login` -> redirect na `/settings/users`.
## Deferrals
- Rotacja remember tokenu po uzyciu (`loginFromRememberToken` nie odswieza tokenu). Odlozone.
- Synchronizacja literalu "30" miedzy `AuthService::REMEMBER_DAYS` a stringiem PL pozostaje manualna - akceptowalne ryzyko.
## Quality Radar - delty
Brak nowych ryzyk. `auth/login.php` (47 -> 46 lin.), `AuthController.php` (89 -> 89 lin., zmiana semantyczna 1 linii), `login.scss` (selektor kompozytowy). Skala zmian: pojedyncze linie.

File diff suppressed because one or more lines are too long

View File

@@ -99,7 +99,7 @@ return [
'title' => 'Logowanie',
'heading' => 'Panel zarządzania zamówieniami',
'subtitle' => 'Zaloguj się, aby przejść do obsługi zamówień i wysyłek.',
'remember_me' => 'Zapamiętaj mnie',
'remember_me' => 'Zapamiętaj mnie (30 dni)',
'email_label' => 'Email',
'email_placeholder' => 'np. admin@firma.pl',
'password_label' => 'Hasło',

View File

@@ -101,7 +101,7 @@ h1 {
margin-bottom: 18px;
}
.remember-field {
.form-field.remember-field {
display: flex;
align-items: center;
gap: 8px;

View File

@@ -2,7 +2,6 @@
<header class="login-header">
<p class="login-badge"><?= $e($t('brand.name_full')) ?></p>
<h1 id="login-title"><?= $e($t('auth.login.heading')) ?></h1>
<p class="login-subtitle"><?= $e($t('auth.login.subtitle')) ?></p>
</header>
<?php if (!empty($errorMessage)): ?>

View File

@@ -21,7 +21,7 @@ final class AuthController
public function showLogin(Request $request): Response
{
if ($this->auth->check()) {
if ($this->auth->check() || $this->auth->loginFromRememberToken()) {
return Response::redirect('/settings/users');
}