diff --git a/.paul/STATE.md b/.paul/STATE.md index 0da641d..bea2c1a 100644 --- a/.paul/STATE.md +++ b/.paul/STATE.md @@ -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 diff --git a/.paul/codebase/tech_changelog.md b/.paul/codebase/tech_changelog.md index 038bd48..de16d5c 100644 --- a/.paul/codebase/tech_changelog.md +++ b/.paul/codebase/tech_changelog.md @@ -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 `

`. +- `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 diff --git a/.paul/governance/governance_2026-05-20.jsonl b/.paul/governance/governance_2026-05-20.jsonl new file mode 100644 index 0000000..8828ccd --- /dev/null +++ b/.paul/governance/governance_2026-05-20.jsonl @@ -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"} diff --git a/.paul/plans/20260520-1200-fix-login-page-and-remember-me/PLAN.md b/.paul/plans/20260520-1200-fix-login-page-and-remember-me/PLAN.md new file mode 100644 index 0000000..25a8830 --- /dev/null +++ b/.paul/plans/20260520-1200-fix-login-page-and-remember-me/PLAN.md @@ -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 +--- + + +## 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`. + + + +## 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 + + + +- 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()`). + + + +## 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. + + + +SPECIAL-FLOWS.md nie istnieje w projekcie - sekcja pomijana. + + + + +## 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` +``` + + + + + + + Task 1: Widok logowania - usun subtitle i dodaj dopisek 30 dni + resources/views/auth/login.php, resources/lang/pl.php + +1. W `resources/views/auth/login.php` usun caly element `

...

` (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). + + php -l resources/views/auth/login.php; rg "auth\.login\.(subtitle|remember_me)" resources/ -n + AC-1, AC-2 (czesc tekstowa) + + + + Task 2: SCSS + rebuild CSS - checkbox w jednej linii + resources/scss/login.scss, public/assets/css/login.css + +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). + + rg "remember-field" resources/scss/login.scss public/assets/css/login.css -n; otwarcie `/login` w przegladarce i wizualna weryfikacja (smoke). + AC-2 + + + + Task 3: Auto-logowanie z remember tokenem na `/login` + src/Modules/Auth/AuthController.php + +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. + + php -l src/Modules/Auth/AuthController.php; ewentualny smoke: zaloguj z checkboxem, wyczysc cookie sesyjne (PHPSESSID), wejdz na `/login` -> powinno przekierowac na `/settings/users`. + AC-3 + + + + Task 4: Weryfikacja parametrow cookie remember (read-only) + src/Modules/Auth/AuthService.php + +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). + + rg "REMEMBER_DAYS|REMEMBER_COOKIE|setcookie" src/Modules/Auth/AuthService.php -n + AC-4 + + + + + +## 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). + + + +- [ ] `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. + + + +- [ ] 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. + + + +SUMMARY.md path: `.paul/plans/20260520-1200-fix-login-page-and-remember-me/SUMMARY.md` + diff --git a/.paul/plans/20260520-1200-fix-login-page-and-remember-me/SUMMARY.md b/.paul/plans/20260520-1200-fix-login-page-and-remember-me/SUMMARY.md new file mode 100644 index 0000000..a6442bb --- /dev/null +++ b/.paul/plans/20260520-1200-fix-login-page-and-remember-me/SUMMARY.md @@ -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 `

` | +| `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. diff --git a/public/assets/css/login.css b/public/assets/css/login.css index 16e87b8..2c0fea1 100644 --- a/public/assets/css/login.css +++ b/public/assets/css/login.css @@ -1 +1 @@ -:root{--c-primary: #6690f4;--c-primary-dark: #3164db;--c-action-primary: #0f766e;--c-action-primary-dark: #0b5f59;--c-bg: #f4f6f9;--c-surface: #ffffff;--c-text: #4e5e6a;--c-text-strong: #2d3748;--c-muted: #718096;--c-border: #b0bec5;--c-danger: #cc0000;--focus-ring: 0 0 0 3px rgba(102, 144, 244, 0.15);--focus-ring-action: 0 0 0 3px rgba(15, 118, 110, 0.18);--shadow-card: 0 1px 4px rgba(0, 0, 0, 0.06)}.btn{display:inline-flex;align-items:center;justify-content:center;min-height:34px;padding:6px 12px;border:1px solid rgba(0,0,0,0);border-radius:8px;font:inherit;font-weight:600;text-decoration:none;cursor:pointer;transition:background-color .2s ease,border-color .2s ease,color .2s ease,transform .1s ease}.btn--primary{color:#fff;background:var(--c-action-primary)}.btn--primary:hover{background:var(--c-action-primary-dark)}.btn--secondary{color:var(--c-text-strong);border-color:var(--c-border);background:var(--c-surface)}.btn--secondary:hover{border-color:#cbd5e0;background:#f8fafc}.btn--danger{color:#fff;border-color:#b91c1c;background:#dc2626}.btn--danger:hover{border-color:#991b1b;background:#b91c1c}.btn--sm{min-height:28px;padding:3px 10px;font-size:12px}.btn--block{width:100%}.btn--disabled{opacity:.3;cursor:not-allowed;pointer-events:none}.btn:active{transform:translateY(1px)}.btn:focus-visible{outline:none;box-shadow:var(--focus-ring-action);border-color:var(--c-action-primary)}.form-control{width:100%;min-height:30px;border:1px solid var(--c-border);border-radius:6px;padding:4px 8px;font:inherit;color:var(--c-text-strong);background:#fff;transition:border-color .2s ease,box-shadow .2s ease}.form-control:focus{outline:none;border-color:var(--c-primary);box-shadow:var(--focus-ring)}.input{min-height:34px;border:1px solid var(--c-border);border-radius:8px;padding:5px 10px;font:inherit;color:var(--c-text-strong);background:#fff}.input--sm{min-height:28px;padding:3px 8px;font-size:12px}.flash{padding:8px 12px;border-radius:6px;font-size:13px}.flash--success{border:1px solid #b7ebcf;background:#f0fff6;color:#0f6b39}.flash--error{border:1px solid #fed7d7;background:#fff5f5;color:var(--c-danger)}.alert{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border-radius:8px;border:1px solid rgba(0,0,0,0);font-size:13px;min-height:44px;line-height:1.4}.alert__icon{flex:0 0 18px;display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;line-height:1;color:inherit}.alert__icon svg{width:18px;height:18px;display:block}.alert__body{flex:1;min-width:0;word-break:break-word}.alert__dismiss{flex:0 0 auto;margin-left:auto;align-self:flex-start;background:rgba(0,0,0,0);border:0;padding:2px 6px;cursor:pointer;color:inherit;opacity:.55;font-size:16px;line-height:1;border-radius:4px;transition:opacity .15s ease,background-color .15s ease}.alert__dismiss:hover,.alert__dismiss:focus-visible{opacity:1;background-color:rgba(0,0,0,.06);outline:none}.alert--info{border-color:#bfdbfe;background:#eff6ff;color:#1e3a8a}.alert--danger{border-color:#fed7d7;background:#fff5f5;color:var(--c-danger)}.alert--success{border-color:#b7ebcf;background:#f0fff6;color:#0f6b39}.alert--warning{border-color:#f7dd8b;background:#fff8e8;color:#815500}.alerts-stack{display:flex;flex-direction:column;gap:8px;margin-bottom:12px}.form-field{display:grid;gap:5px}.field-label{color:var(--c-text-strong);font-size:13px;font-weight:600}.table-wrap{width:100%;overflow-x:auto}.table-wrap--visible{overflow:visible !important;overflow-x:visible !important}.table{width:100%;border-collapse:collapse;background:var(--c-surface)}.table th,.table td{padding:10px 12px;border-bottom:1px solid var(--c-border);text-align:left}.table th{color:var(--c-text-strong);font-weight:700;background:#f8fafc}.table--details th{white-space:nowrap}.table--details th:first-child,.table--details td:first-child{width:36px;text-align:center}.pagination{display:flex;align-items:center;flex-wrap:wrap;gap:8px}.pagination__item{display:inline-flex;align-items:center;justify-content:center;min-width:36px;height:36px;padding:0 10px;border-radius:8px;border:1px solid var(--c-border);color:var(--c-text-strong);background:var(--c-surface);text-decoration:none;font-weight:600}.pagination__item:hover{border-color:#cbd5e0;background:#f8fafc}.pagination__item.is-active{border-color:var(--c-primary);color:var(--c-primary);background:#edf2ff}.receipt-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:16px;padding-bottom:12px;border-bottom:2px solid var(--c-text-strong)}.receipt-header__seller{flex:1}.receipt-header__seller strong{font-size:14px;display:block;margin-bottom:4px}.receipt-header__title{text-align:right}.receipt-header__title h1{font-size:18px;font-weight:700;margin-bottom:4px}.receipt-print{max-width:700px;margin:0 auto}@media print{.receipt-print{max-width:100%}}.copy-name-row{display:flex;align-items:center;gap:6px}.copy-btn-inline{display:inline-flex;align-items:center;justify-content:center;background:none;border:none;padding:2px;cursor:pointer;color:var(--c-text-muted, #999);border-radius:3px;transition:color .15s;flex-shrink:0}.copy-btn-inline:hover{color:var(--c-primary)}.copy-btn-inline .check-icon{color:var(--c-action-primary)}:root{--shadow-card: 0 20px 50px rgba(22, 34, 58, 0.14)}*{box-sizing:border-box}html,body{min-height:100%}body{margin:0;font-family:"Roboto","Segoe UI",sans-serif;color:var(--c-text);background:var(--c-bg);overflow-x:hidden}.bg-orb{position:fixed;width:460px;height:460px;border-radius:999px;filter:blur(28px);z-index:0;opacity:.45;pointer-events:none}.bg-orb-left{top:-200px;left:-180px;background:radial-gradient(circle, rgba(102, 144, 244, 0.48) 0%, rgba(102, 144, 244, 0) 70%)}.bg-orb-right{right:-200px;bottom:-220px;background:radial-gradient(circle, rgba(30, 42, 58, 0.36) 0%, rgba(30, 42, 58, 0) 70%)}.login-page{min-height:100vh;display:grid;place-items:center;padding:32px 20px;position:relative;z-index:1}.login-card{width:100%;max-width:430px;background:var(--c-surface);border:1px solid var(--c-border);border-radius:12px;box-shadow:var(--shadow-card);padding:34px 30px 28px;animation:card-enter 420ms ease-out}.login-header{margin-bottom:24px}.login-badge{display:inline-block;margin:0 0 14px;padding:5px 12px;border-radius:999px;border:1px solid #d9e2ff;background:#eef2ff;color:#3f5faf;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.06em}h1{margin:0;color:var(--c-text-strong);font-size:clamp(1.6rem,2.5vw,1.9rem);line-height:1.15;font-weight:700}.login-subtitle{margin:10px 0 0;font-size:15px;line-height:1.55;color:var(--c-muted)}.login-alert{margin-bottom:18px}.remember-field{display:flex;align-items:center;gap:8px}.remember-field input[type=checkbox]{width:16px;height:16px;accent-color:var(--c-primary, #4f6ef7);cursor:pointer}.remember-field .field-label{font-weight:400;cursor:pointer;user-select:none}.login-form{display:grid;gap:16px}.form-field{display:grid;gap:7px}.field-label{color:var(--c-text-strong);font-size:13px;font-weight:600}.login-form .form-control{min-height:46px;padding:0 14px;border-width:2px}.login-form .form-control::placeholder{color:#cbd5e0}.login-submit{margin-top:2px;font-size:15px;min-height:48px}@keyframes card-enter{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@media(max-width: 640px){.login-page{padding:18px 14px}.login-card{padding:24px 20px 20px}h1{font-size:1.55rem}} +:root{--c-primary: #6690f4;--c-primary-dark: #3164db;--c-action-primary: #0f766e;--c-action-primary-dark: #0b5f59;--c-bg: #f4f6f9;--c-surface: #ffffff;--c-text: #4e5e6a;--c-text-strong: #2d3748;--c-muted: #718096;--c-border: #b0bec5;--c-danger: #cc0000;--focus-ring: 0 0 0 3px rgba(102, 144, 244, 0.15);--focus-ring-action: 0 0 0 3px rgba(15, 118, 110, 0.18);--shadow-card: 0 1px 4px rgba(0, 0, 0, 0.06)}.btn{display:inline-flex;align-items:center;justify-content:center;min-height:34px;padding:6px 12px;border:1px solid rgba(0,0,0,0);border-radius:8px;font:inherit;font-weight:600;text-decoration:none;cursor:pointer;transition:background-color .2s ease,border-color .2s ease,color .2s ease,transform .1s ease}.btn--primary{color:#fff;background:var(--c-action-primary)}.btn--primary:hover{background:var(--c-action-primary-dark)}.btn--secondary{color:var(--c-text-strong);border-color:var(--c-border);background:var(--c-surface)}.btn--secondary:hover{border-color:#cbd5e0;background:#f8fafc}.btn--danger{color:#fff;border-color:#b91c1c;background:#dc2626}.btn--danger:hover{border-color:#991b1b;background:#b91c1c}.btn--sm{min-height:28px;padding:3px 10px;font-size:12px}.btn--block{width:100%}.btn--disabled{opacity:.3;cursor:not-allowed;pointer-events:none}.btn:active{transform:translateY(1px)}.btn:focus-visible{outline:none;box-shadow:var(--focus-ring-action);border-color:var(--c-action-primary)}.form-control{width:100%;min-height:30px;border:1px solid var(--c-border);border-radius:6px;padding:4px 8px;font:inherit;color:var(--c-text-strong);background:#fff;transition:border-color .2s ease,box-shadow .2s ease}.form-control:focus{outline:none;border-color:var(--c-primary);box-shadow:var(--focus-ring)}.input{min-height:34px;border:1px solid var(--c-border);border-radius:8px;padding:5px 10px;font:inherit;color:var(--c-text-strong);background:#fff}.input--sm{min-height:28px;padding:3px 8px;font-size:12px}.flash{padding:8px 12px;border-radius:6px;font-size:13px}.flash--success{border:1px solid #b7ebcf;background:#f0fff6;color:#0f6b39}.flash--error{border:1px solid #fed7d7;background:#fff5f5;color:var(--c-danger)}.alert{display:flex;align-items:flex-start;gap:10px;padding:12px 14px;border-radius:8px;border:1px solid rgba(0,0,0,0);font-size:13px;min-height:44px;line-height:1.4}.alert__icon{flex:0 0 18px;display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;line-height:1;color:inherit}.alert__icon svg{width:18px;height:18px;display:block}.alert__body{flex:1;min-width:0;word-break:break-word}.alert__dismiss{flex:0 0 auto;margin-left:auto;align-self:flex-start;background:rgba(0,0,0,0);border:0;padding:2px 6px;cursor:pointer;color:inherit;opacity:.55;font-size:16px;line-height:1;border-radius:4px;transition:opacity .15s ease,background-color .15s ease}.alert__dismiss:hover,.alert__dismiss:focus-visible{opacity:1;background-color:rgba(0,0,0,.06);outline:none}.alert--info{border-color:#bfdbfe;background:#eff6ff;color:#1e3a8a}.alert--danger{border-color:#fed7d7;background:#fff5f5;color:var(--c-danger)}.alert--success{border-color:#b7ebcf;background:#f0fff6;color:#0f6b39}.alert--warning{border-color:#f7dd8b;background:#fff8e8;color:#815500}.alerts-stack{display:flex;flex-direction:column;gap:8px;margin-bottom:12px}.form-field{display:grid;gap:5px}.field-label{color:var(--c-text-strong);font-size:13px;font-weight:600}.table-wrap{width:100%;overflow-x:auto}.table-wrap--visible{overflow:visible !important;overflow-x:visible !important}.table{width:100%;border-collapse:collapse;background:var(--c-surface)}.table th,.table td{padding:10px 12px;border-bottom:1px solid var(--c-border);text-align:left}.table th{color:var(--c-text-strong);font-weight:700;background:#f8fafc}.table--details th{white-space:nowrap}.table--details th:first-child,.table--details td:first-child{width:36px;text-align:center}.pagination{display:flex;align-items:center;flex-wrap:wrap;gap:8px}.pagination__item{display:inline-flex;align-items:center;justify-content:center;min-width:36px;height:36px;padding:0 10px;border-radius:8px;border:1px solid var(--c-border);color:var(--c-text-strong);background:var(--c-surface);text-decoration:none;font-weight:600}.pagination__item:hover{border-color:#cbd5e0;background:#f8fafc}.pagination__item.is-active{border-color:var(--c-primary);color:var(--c-primary);background:#edf2ff}.receipt-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:16px;padding-bottom:12px;border-bottom:2px solid var(--c-text-strong)}.receipt-header__seller{flex:1}.receipt-header__seller strong{font-size:14px;display:block;margin-bottom:4px}.receipt-header__title{text-align:right}.receipt-header__title h1{font-size:18px;font-weight:700;margin-bottom:4px}.receipt-print{max-width:700px;margin:0 auto}@media print{.receipt-print{max-width:100%}}.copy-name-row{display:flex;align-items:center;gap:6px}.copy-btn-inline{display:inline-flex;align-items:center;justify-content:center;background:none;border:none;padding:2px;cursor:pointer;color:var(--c-text-muted, #999);border-radius:3px;transition:color .15s;flex-shrink:0}.copy-btn-inline:hover{color:var(--c-primary)}.copy-btn-inline .check-icon{color:var(--c-action-primary)}:root{--shadow-card: 0 20px 50px rgba(22, 34, 58, 0.14)}*{box-sizing:border-box}html,body{min-height:100%}body{margin:0;font-family:"Roboto","Segoe UI",sans-serif;color:var(--c-text);background:var(--c-bg);overflow-x:hidden}.bg-orb{position:fixed;width:460px;height:460px;border-radius:999px;filter:blur(28px);z-index:0;opacity:.45;pointer-events:none}.bg-orb-left{top:-200px;left:-180px;background:radial-gradient(circle, rgba(102, 144, 244, 0.48) 0%, rgba(102, 144, 244, 0) 70%)}.bg-orb-right{right:-200px;bottom:-220px;background:radial-gradient(circle, rgba(30, 42, 58, 0.36) 0%, rgba(30, 42, 58, 0) 70%)}.login-page{min-height:100vh;display:grid;place-items:center;padding:32px 20px;position:relative;z-index:1}.login-card{width:100%;max-width:430px;background:var(--c-surface);border:1px solid var(--c-border);border-radius:12px;box-shadow:var(--shadow-card);padding:34px 30px 28px;animation:card-enter 420ms ease-out}.login-header{margin-bottom:24px}.login-badge{display:inline-block;margin:0 0 14px;padding:5px 12px;border-radius:999px;border:1px solid #d9e2ff;background:#eef2ff;color:#3f5faf;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.06em}h1{margin:0;color:var(--c-text-strong);font-size:clamp(1.6rem,2.5vw,1.9rem);line-height:1.15;font-weight:700}.login-subtitle{margin:10px 0 0;font-size:15px;line-height:1.55;color:var(--c-muted)}.login-alert{margin-bottom:18px}.form-field.remember-field{display:flex;align-items:center;gap:8px}.form-field.remember-field input[type=checkbox]{width:16px;height:16px;accent-color:var(--c-primary, #4f6ef7);cursor:pointer}.form-field.remember-field .field-label{font-weight:400;cursor:pointer;user-select:none}.login-form{display:grid;gap:16px}.form-field{display:grid;gap:7px}.field-label{color:var(--c-text-strong);font-size:13px;font-weight:600}.login-form .form-control{min-height:46px;padding:0 14px;border-width:2px}.login-form .form-control::placeholder{color:#cbd5e0}.login-submit{margin-top:2px;font-size:15px;min-height:48px}@keyframes card-enter{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@media(max-width: 640px){.login-page{padding:18px 14px}.login-card{padding:24px 20px 20px}h1{font-size:1.55rem}} diff --git a/resources/lang/pl.php b/resources/lang/pl.php index 606fd68..be55d38 100644 --- a/resources/lang/pl.php +++ b/resources/lang/pl.php @@ -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', diff --git a/resources/scss/login.scss b/resources/scss/login.scss index 4ad19bc..411febf 100644 --- a/resources/scss/login.scss +++ b/resources/scss/login.scss @@ -101,7 +101,7 @@ h1 { margin-bottom: 18px; } -.remember-field { +.form-field.remember-field { display: flex; align-items: center; gap: 8px; diff --git a/resources/views/auth/login.php b/resources/views/auth/login.php index 0c38a7d..9719526 100644 --- a/resources/views/auth/login.php +++ b/resources/views/auth/login.php @@ -2,7 +2,6 @@

-

diff --git a/src/Modules/Auth/AuthController.php b/src/Modules/Auth/AuthController.php index 854e4ff..810d8b6 100644 --- a/src/Modules/Auth/AuthController.php +++ b/src/Modules/Auth/AuthController.php @@ -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'); }