diff --git a/.paul/PROJECT.md b/.paul/PROJECT.md
index f89205c..faf97ab 100644
--- a/.paul/PROJECT.md
+++ b/.paul/PROJECT.md
@@ -12,6 +12,7 @@ Jednoosobowa aplikacja finansowa w CodeIgniter 4.7.3 i MySQL do ewidencji finans
- ✓ Instrumenty, operacje, wyceny i pulpity inwestycji — Plan 2.
- ✓ Import paragonów przez OpenAI Responses API z podpowiedziami kategorii — Plan 3.
- ✓ Zapamiętywanie logowania przez 30 dni na wielu urządzeniach — Plan 4.
+- ✓ Skanowanie paragonu kamerą telefonu i mobilny przegląd wyniku AI — Plan 20260713-2111.
## Trwałe wymagania i ograniczenia
@@ -29,4 +30,4 @@ Jednoosobowa aplikacja finansowa w CodeIgniter 4.7.3 i MySQL do ewidencji finans
| 2026-07-12 | Bez panelu urządzeń i „wyloguj wszędzie” | YAGNI; brak takiego wymagania |
---
-*Last updated: 2026-07-12 after Plan 20260712-1647*
+*Last updated: 2026-07-13 after Plan 20260713-2111*
diff --git a/.paul/STATE.md b/.paul/STATE.md
index e0013df..e14ba23 100644
--- a/.paul/STATE.md
+++ b/.paul/STATE.md
@@ -1,6 +1,6 @@
# STATE — finansePRO
-**Aktualizacja:** 2026-07-12T17:31:28+02:00
+**Aktualizacja:** 2026-07-13T21:38:51+02:00
## Project Reference
@@ -8,10 +8,14 @@ CodeIgniter 4.7.3 + MySQL na hostido. Jedno konto z `.env`. Tryb PAUL: plan-firs
## Current Position
-- Plan `20260712-1647-zapamietywanie-logowania`: **UKOŃCZONY**.
+- Plan `20260713-2111-skanowanie-paragonu-kamera`: **UKOŃCZONY**.
- Status: PLAN → APPLY → UNIFY zamknięte; gotowe do następnego planu.
-- Dostarczono tokeny logowania ważne 30 dni, niezależne dla wielu urządzeń.
-- Zdalna tabela `remember_tokens` została utworzona i zweryfikowana.
+- Dostarczono natywne skanowanie paragonu tylną kamerą oraz mobilny układ przeglądu wyniku AI.
+- Quality Radar post-apply: ok; UAT mobilny zatwierdzony.
+- Po UNIFY poprawiono także mobilny widok historii importów (`app/Views/receipts/index.php`).
+- Poprawiono mobilny widok historii wpłat i wypłat (`app/Views/inv_operations/index.php`).
+- Poprawiono mobilny widok historii wycen (`app/Views/valuations/index.php`).
+- Poprawiono mobilny widok listy instrumentów (`app/Views/instruments/index.php`).
## Loop Position
@@ -22,25 +26,18 @@ PLAN ──▶ APPLY ──▶ UNIFY
## Session Continuity
-- Last session: 2026-07-12T17:31:28+02:00
-- Stopped at: UNIFY planu zapamiętywania logowania.
-- Next action: opcjonalnie potwierdzić UAT w dwóch profilach albo uruchomić `$paul-plan` dla kolejnej funkcji.
-- Resume file: `.paul/plans/20260712-1647-zapamietywanie-logowania/SUMMARY.md`
+- Last session: 2026-07-13T21:38:51+02:00
+- Stopped at: wszystkie zgłoszone widoki historii oraz lista instrumentów poprawione na mobile.
+- Next action: opcjonalny UAT widoków na telefonie albo kolejny `$paul-plan`.
+- Resume file: `.paul/plans/20260713-2111-skanowanie-paragonu-kamera/SUMMARY.md`
## Aktywne sprawy / do weryfikacji
-- UAT: dwa profile przeglądarki, zamknięcie zwykłej sesji oraz wylogowanie tylko jednego urządzenia.
-- `vendor/bin/phpunit` nie jest dostępny lokalnie; test `RememberTokenModelTest.php` został tylko sprawdzony przez `php -l`.
-- Deploy hostido nie uruchamia migracji automatycznie; tabela dla tego planu została już utworzona zdalnie.
+- UAT poprawionych historii na telefonie jest opcjonalny; kontrola składni i struktury responsywnej przeszła.
+- Repozytorium ma wcześniejszą, niezwiązaną zmianę `.vscode/ftp-kr.sync.cache.json`; nie obejmować jej.
## Decisions
-- Token per urządzenie; kolejne logowanie nie usuwa wcześniejszych tokenów.
-- Wylogowanie usuwa wyłącznie token bieżącego cookie.
-- Bez panelu urządzeń i globalnego wylogowania do czasu realnej potrzeby.
-
-## Uwagi
-
-- Radar post-apply: degraded z powodu niepełnego wyniku indeksu i braku PHPUnit.
-- Repozytorium Git ma liczne wcześniejsze i niezwiązane zmiany; commit fazowy pominięty zgodnie z `preferences.auto_commit: false`.
-- `ROADMAP.md` nie istnieje i pozostaje opcjonalny w trybie plan-first.
+- Natywne `capture="environment"`; bez WebRTC i nowych zależności.
+- Zachować jeden istniejący przepływ `Receipts::create`.
+- Widoki mobilne adaptować responsywnym HTML/CSS bez zmian backendu.
diff --git a/.paul/changelog/2026-07-13.md b/.paul/changelog/2026-07-13.md
new file mode 100644
index 0000000..0eb2cd0
--- /dev/null
+++ b/.paul/changelog/2026-07-13.md
@@ -0,0 +1,20 @@
+# 2026-07-13
+
+## Co zrobiono
+
+- [Plan 20260713-2111] Dodano skanowanie paragonu bezpośrednio tylną kamerą telefonu.
+- Zachowano istniejący upload JPG/JPEG/PNG/PDF/JSON i przepływ analizy AI.
+- Poprawiono responsywność formularza przeglądu pozycji po analizie AI.
+- Po zamknięciu planu poprawiono mobilny widok historii importów: wiersze tabeli zmieniają się w czytelne karty.
+- Poprawiono mobilny widok historii wpłat i wypłat inwestycyjnych tym samym układem kart.
+- Poprawiono mobilny widok historii wycen inwestycyjnych.
+- Poprawiono mobilny widok listy instrumentów inwestycyjnych.
+
+## Zmienione pliki
+
+- `app/Views/receipts/new.php`
+- `app/Views/receipts/review.php`
+- `app/Views/receipts/index.php`
+- `app/Views/inv_operations/index.php`
+- `app/Views/valuations/index.php`
+- `app/Views/instruments/index.php`
diff --git a/.paul/codebase/impact_map.md b/.paul/codebase/impact_map.md
index b63b06b..f7a28dc 100644
--- a/.paul/codebase/impact_map.md
+++ b/.paul/codebase/impact_map.md
@@ -1,5 +1,18 @@
# Impact Map
+## Post-apply 20260713-2111
+- Zmieniono wyłącznie `app/Views/receipts/new.php` i `app/Views/receipts/review.php`.
+- Aparat korzysta z istniejącego pola `receipt` i `Receipts::create`; backend, routing, parser, modele i baza bez zmian.
+- Mobilny przegląd pozycji używa układu kart poniżej `md`, zachowując nazwy pól i skrypt grupowania.
+- UAT aparatu i poprawionego przeglądu zatwierdzony przez użytkownika.
+
+## Plan 20260713-2111 — skanowanie paragonu kamerą telefonu
+- UI: `app/Views/receipts/new.php`; istniejące pole `receipt` może użyć natywnego `capture="environment"`.
+- Backend: `Receipts::create` już obsługuje JPG/JPEG/PNG do 10 MB i przekazuje obraz do obecnego parsera; bez zmiany kontrolera.
+- Trasy, parser AI, modele, baza i zapis operacji pozostają bez zmian.
+- UAT: telefon mobilny — tylna kamera, wysłanie zdjęcia, przejście do przeglądu oraz zachowany wybór istniejącego pliku.
+- Ukryte sprzężenie: zachować `name="receipt"`, `multipart/form-data` i obecny `accept`, aby nie rozdzielić przepływu uploadu.
+
## Post-apply 20260712-1647
- Mechanizm wdrożony w kontrolerze logowania, filtrze, widoku, modelu i migracji.
- Zdalna tabela `remember_tokens` istnieje.
diff --git a/.paul/codebase/quality_risks.md b/.paul/codebase/quality_risks.md
index bd36598..90b34ed 100644
--- a/.paul/codebase/quality_risks.md
+++ b/.paul/codebase/quality_risks.md
@@ -1,5 +1,18 @@
# Quality Risks
+## Post-apply 20260713-2111
+- **Obsłużone:** jeden istniejący upload, natywne `capture="environment"`, brak nowej zależności i zmian backendu.
+- **Obsłużone po UAT:** mobilny overflow formularza przeglądu — tabela zmienia się w karty poniżej `md`.
+- **Potwierdzone:** nazwy pól, `accept`, `required`, limit 10 MB i skrypt grupowania pozostały spójne.
+- **Zaakceptowane:** zachowanie selektora aparatu zależy od przeglądarki; działa na docelowym telefonie.
+
+## Plan 20260713-2111 — skanowanie paragonu kamerą telefonu
+- **Jedno źródło uploadu:** użyć istniejącego pola `receipt` i `Receipts::create`; bez drugiego endpointu, kontrolera lub parsera.
+- **Natywna funkcja platformy:** `capture="environment"`; bez WebRTC, JavaScriptu i nowej zależności.
+- **Zgodność urządzeń:** `capture` jest wskazówką dla przeglądarki; wymagany manualny UAT na docelowym telefonie.
+- **Regresja formatów:** zachować istniejący `accept` dla JPG/JPEG/PNG/PDF/JSON i sprawdzić wybór pliku obok aparatu.
+- **Odroczone:** podgląd na żywo, kadrowanie, kompresja i wiele zdjęć — brak wymagania (YAGNI).
+
## Post-apply 20260712-1647
- **Obsłużone:** osobne tokeny urządzeń, hash walidatora, `hash_equals`, selektywne wylogowanie.
- **Obsłużone operacyjnie:** tabela utworzona na zdalnej bazie po wykryciu błędu braku migracji.
diff --git a/.paul/codebase/radar/codebase-memory-plan.txt b/.paul/codebase/radar/codebase-memory-plan.txt
index 28ba140..dcc28b6 100644
--- a/.paul/codebase/radar/codebase-memory-plan.txt
+++ b/.paul/codebase/radar/codebase-memory-plan.txt
@@ -1,15 +1,13 @@
-Tryb: plan
-Data: 2026-07-12T16:47:19+02:00
-Projekt: C-visual-studio-code-finansePRO
-Status indeksu: indexed, moderate, 789 nodes, 1015 edges
-
-Zapytania: logowanie, sesja, filtr auth, logout, remember.
-
-Najważniejsze pliki:
-- `app/Controllers/Auth.php`
-- `app/Filters/AuthFilter.php`
-- `app/Views/auth/login.php`
-- `app/Config/Session.php`
-- `app/Config/Cookie.php`
-
-Wniosek: osobne trwałe tokeny per urządzenie odtwarzające istniejącą sesję; bez wydłużania globalnej sesji.
+Plan scan: 2026-07-13T21:11:21+02:00
+Scope: skanowanie paragonu bezpośrednio z kamery telefonu
+Project: C-visual-studio-code-finansePRO
+Index status: ready; 789 nodes; 1015 edges
+Relevant symbols:
+- app/Controllers/Receipts.php: Receipts::new
+- app/Controllers/Receipts.php: Receipts::create (lines 41-147; accepts JPG/JPEG/PNG and routes them through existing parser)
+- app/Views/receipts/new.php: existing multipart input name="receipt", accepts image/* plus PDF/JSON
+- app/Config/Routes.php: GET receipts/new, POST receipts
+Impact conclusion: native HTML capture hint in the existing view; no backend, route, parser, model, or database change.
+Trace note: trace_path did not resolve the qualified method despite search_graph result; code snippet and connected graph search were sufficient for this one-view change.
+jscpd: disabled by policy
+ast-grep: disabled by policy
diff --git a/.paul/codebase/radar/codebase-memory-post-apply.txt b/.paul/codebase/radar/codebase-memory-post-apply.txt
index 15e2b4b..594ef06 100644
--- a/.paul/codebase/radar/codebase-memory-post-apply.txt
+++ b/.paul/codebase/radar/codebase-memory-post-apply.txt
@@ -1,12 +1,16 @@
-Tryb: post-apply
-Data: 2026-07-12T17:31:28+02:00
-Projekt: C-visual-studio-code-finansePRO
-
-- Indeks moderate odświeżony: 789 nodes, 1015 edges.
-- `search_graph` nie zwrócił nowych symboli `RememberTokenModel`.
-- `search_code` zakończył się błędem tworzenia pliku tymczasowego.
-- Fallback: bezpośredni lint 6 zmienionych plików — wszystkie poprawne.
-- Zdalna tabela `remember_tokens`: 6 kolumn, 3 indeksy.
-- PHPUnit: niedostępny, brak `vendor/bin/phpunit`.
-
-Status: degraded — kod i schemat sprawdzone, brak automatycznego testu runtime.
+Post-apply scan: 2026-07-13T21:29:46+02:00
+Plan: 20260713-2111-skanowanie-paragonu-kamera
+Project: C-visual-studio-code-finansePRO
+Index refreshed in fast mode: ready; 835 nodes; 1048 edges
+Modified source files:
+- app/Views/receipts/new.php
+- app/Views/receipts/review.php
+Impact conclusion:
+- Existing Receipts::create flow, routes, parser, models, and database remain unchanged.
+- Native camera capture reuses the receipt upload field.
+- Mobile review layout changes presentation only; form names and grouping script remain intact.
+Verification:
+- php -l both views: pass
+- mobile UAT camera and review: approved by user
+jscpd: disabled by policy
+ast-grep: disabled by policy
diff --git a/.paul/codebase/tooling_status.md b/.paul/codebase/tooling_status.md
index 6db5448..cfdb4a4 100644
--- a/.paul/codebase/tooling_status.md
+++ b/.paul/codebase/tooling_status.md
@@ -1,11 +1,12 @@
# Tooling Status
-- Timestamp: 2026-07-12T17:31:28+02:00
+- Timestamp: 2026-07-13T21:29:46+02:00
- Scan mode: post-apply
-- codebase-memory-mcp: degraded; indeks odświeżony, ale nowe symbole nie zostały zwrócone, a `search_code` zgłosił błąd pliku tymczasowego
-- fallback: `php -l` dla 6 plików — pass
-- PHPUnit: unavailable (`vendor/bin/phpunit` missing)
+- codebase-memory-mcp: ok; CLI 0.6.1, indeks odświeżony w trybie fast (835 węzłów, 1048 krawędzi)
+- Queries: `index_repository`, `index_status`, `search_graph`
+- PHP syntax: pass dla `app/Views/receipts/new.php` i `app/Views/receipts/review.php`
+- Mobile UAT: pass, potwierdzony przez użytkownika
- jscpd: disabled by policy
- ast-grep: disabled by policy
- Raw output: `.paul/codebase/radar/codebase-memory-post-apply.txt`
-- Next action: opcjonalny UAT dwóch profili przeglądarki.
+- Next action: pętla zamknięta; gotowe do kolejnego planu.
diff --git a/.paul/plans/20260713-2111-skanowanie-paragonu-kamera/PLAN.md b/.paul/plans/20260713-2111-skanowanie-paragonu-kamera/PLAN.md
new file mode 100644
index 0000000..47a26a1
--- /dev/null
+++ b/.paul/plans/20260713-2111-skanowanie-paragonu-kamera/PLAN.md
@@ -0,0 +1,147 @@
+---
+plan_id: 20260713-2111-skanowanie-paragonu-kamera
+title: Skanowanie paragonu kamerą telefonu
+storage: plan-first
+legacy_phase: null
+created: 2026-07-13T21:11:21+02:00
+status: planned
+type: execute
+autonomous: false
+delegation: auto
+files_modified:
+ - app/Views/receipts/new.php
+quality_radar: ok
+---
+
+
+## Cel
+Umożliwić wykonanie zdjęcia paragonu tylną kamerą telefonu bezpośrednio z formularza importu.
+
+## Uzasadnienie
+Użytkownik nie musi wcześniej zapisywać zdjęcia ani wybierać go z galerii; wykonane zdjęcie trafia do istniejącego importu i analizy AI.
+
+## Wynik
+Formularz `receipts/new` korzysta z natywnego mechanizmu HTML `capture="environment"`, zachowując dotychczasowy upload JPG/PNG/PDF/JSON i istniejący endpoint `Receipts::create`.
+
+
+
+## Dokumentacja projektu
+@.paul/PROJECT.md
+@.paul/STATE.md
+@.paul/plans/20260706-2046-import-paragonow/SUMMARY.md
+@.paul/codebase/architecture.md
+@.paul/codebase/impact_map.md
+@.paul/codebase/quality_risks.md
+
+## Pliki źródłowe
+@app/Views/receipts/new.php
+@app/Controllers/Receipts.php
+@app/Config/Routes.php
+
+
+
+- Przyjęto natywny wybór tylnej kamery telefonu; nie jest potrzebny podgląd obrazu na żywo ani własny interfejs aparatu.
+
+
+
+## Quality Radar
+
+**Status:** ok
+**Narzędzia:** codebase-memory-mcp 0.6.1 (indeks gotowy, 789 węzłów / 1015 krawędzi); jscpd i ast-grep wyłączone polityką.
+
+## Obszary wpływu
+
+- `app/Views/receipts/new.php` — jedyne miejsce wymagające zmiany: natywna wskazówka użycia tylnej kamery i jasny tekst formularza.
+- `Receipts::create` — bez zmian; już waliduje, zapisuje i parsuje JPG/JPEG/PNG do 10 MB.
+- Routing, parser AI, modele i baza danych — bez zmian.
+- Weryfikacja — telefon z przeglądarką mobilną oraz kontrola zachowania dotychczasowego wyboru pliku.
+
+## Ryzyka duplikacji / hardcodowania
+
+- Nie tworzyć drugiego endpointu ani drugiego przepływu uploadu; zdjęcie ma użyć istniejącego pola `receipt` i `Receipts::create`.
+- Atrybut `capture` jest wskazówką dla przeglądarki, więc zachowanie aparatu zależy od telefonu; obsłużone przez checkpoint UAT.
+
+## Jawne odroczenia
+
+- WebRTC, własny podgląd kamery, kadrowanie, kompresja i skanowanie wielu zdjęć — poza zakresem; dodać dopiero po osobnym wymaganiu.
+
+
+
+
+
+
+## AC-1: Uruchomienie tylnej kamery na telefonie
+```gherkin
+Given użytkownik otworzył formularz nowego importu na obsługiwanym telefonie
+When wybiera pole skanu paragonu
+Then przeglądarka umożliwia wykonanie zdjęcia tylną kamerą
+```
+
+## AC-2: Zdjęcie korzysta z istniejącego importu
+```gherkin
+Given użytkownik wykonał zdjęcie paragonu w formularzu
+When wysyła formularz
+Then zdjęcie trafia jako pole `receipt` do `Receipts::create` i przechodzi dotychczasowy przepływ parsowania oraz przeglądu
+```
+
+## AC-3: Dotychczasowe formaty pozostają dostępne
+```gherkin
+Given użytkownik nie chce wykonać zdjęcia aparatem
+When korzysta z formularza importu
+Then nadal może przekazać obsługiwany plik JPG, JPEG, PNG, PDF lub JSON
+```
+
+
+
+
+
+
+ Task 1: Włączyć natywne przechwytywanie zdjęcia
+ app/Views/receipts/new.php
+
+ Dodać do istniejącego `input type="file"` atrybut `capture="environment"`, bez JavaScriptu i bez nowego formularza lub endpointu. Zachować `name="receipt"`, `required`, wszystkie obecne typy `accept` oraz limit opisany w UI. Zmienić etykietę i krótki tekst pomocniczy tak, aby użytkownik telefonu rozumiał możliwość zrobienia zdjęcia i dalszą analizę AI.
+
+ `php -l app/Views/receipts/new.php` oraz kontrola źródła: dokładnie jedno pole `name="receipt"` z `capture="environment"`.
+ AC-1, AC-2 i AC-3 są pokryte w kodzie formularza, a dotychczasowy backend pozostaje nietknięty.
+
+
+
+ Task 2: Potwierdzić import zdjęcia na telefonie
+ app/Views/receipts/new.php
+
+ Na telefonie otworzyć `/receipts/new`, uruchomić pole pliku, wykonać zdjęcie paragonu tylną kamerą i wysłać formularz. Potwierdzić przejście do przeglądu rozpoznanych pozycji. Następnie sprawdzić, że formularz nadal pozwala wybrać wcześniej istniejący obsługiwany plik.
+
+ Manualny UAT na docelowym telefonie: aparat tylny → zdjęcie → upload → ekran przeglądu; dodatkowo wybór istniejącego pliku.
+ AC-1, AC-2 i AC-3 potwierdzone na urządzeniu mobilnym.
+
+
+
+
+
+## Nie zmieniać
+- `app/Controllers/Receipts.php`, `app/Libraries/ReceiptParser.php`, tras, modeli, migracji ani schematu bazy.
+- Istniejącego limitu 10 MB, whitelisty rozszerzeń oraz półautomatycznego zatwierdzania pozycji.
+
+## Limity zakresu
+- Jedno zdjęcie / jeden plik na import.
+- Bez WebRTC, JavaScriptowego podglądu, kadrowania, filtrów obrazu, kompresji i galerii wielu stron.
+- Bez nowych zależności.
+
+
+
+- [ ] `php -l app/Views/receipts/new.php`
+- [ ] Pole `receipt` zawiera `capture="environment"` i zachowuje obecny `accept`.
+- [ ] Manualny UAT aparatu i dotychczasowego wyboru pliku na telefonie.
+- [ ] Nie zmieniono backendu, routingu ani bazy danych.
+- [ ] Ryzyka Quality Radar obsłużono lub jawnie odroczono.
+
+
+
+- [ ] Wszystkie AC przechodzą.
+- [ ] Weryfikacja automatyczna i mobilna jest zakończona.
+- [ ] Zmiana obejmuje tylko istniejący widok formularza.
+
+
+
+SUMMARY.md path: `.paul/plans/20260713-2111-skanowanie-paragonu-kamera/SUMMARY.md`
+
diff --git a/.paul/plans/20260713-2111-skanowanie-paragonu-kamera/SUMMARY.md b/.paul/plans/20260713-2111-skanowanie-paragonu-kamera/SUMMARY.md
new file mode 100644
index 0000000..a7d596f
--- /dev/null
+++ b/.paul/plans/20260713-2111-skanowanie-paragonu-kamera/SUMMARY.md
@@ -0,0 +1,66 @@
+---
+plan_id: 20260713-2111-skanowanie-paragonu-kamera
+title: Skanowanie paragonu kamerą telefonu
+completed: 2026-07-13T21:29:46+02:00
+storage: plan-first
+quality_radar: ok
+---
+
+# Summary: Skanowanie paragonu kamerą telefonu
+
+## Objective
+
+Umożliwić wykonanie zdjęcia paragonu tylną kamerą telefonu bezpośrednio z formularza importu i przekazać je do istniejącej analizy AI.
+
+## What Was Built
+
+| Obszar | Rezultat |
+|--------|----------|
+| Import | Istniejące pole `receipt` otrzymało natywne `capture="environment"`; zachowano wybór JPG/JPEG/PNG/PDF/JSON. |
+| Przegląd AI | Tabela pozycji zmienia się na czytelne karty na ekranach poniżej `md`; przyciski i podgląd księgowania układają się responsywnie. |
+| Backend | Bez zmian; oba widoki korzystają z istniejącego `Receipts::create`, nazw pól i logiki grupowania. |
+
+## Files Modified
+
+- `app/Views/receipts/new.php` — uruchomienie tylnej kamery i doprecyzowany tekst formularza.
+- `app/Views/receipts/review.php` — mobilny układ pozycji po analizie AI.
+
+## Acceptance Criteria Results
+
+| Kryterium | Status | Dowód |
+|-----------|--------|-------|
+| AC-1: tylna kamera telefonu | Pass | `capture="environment"`; użytkownik zatwierdził UAT na telefonie. |
+| AC-2: istniejący import | Pass | Zachowano pojedyncze `name="receipt"`; zdjęcie przeszło do ekranu przeglądu. |
+| AC-3: dotychczasowe formaty | Pass | Zachowano pełne `accept` dla JPG/JPEG/PNG/PDF/JSON; UAT zatwierdzony. |
+
+## Verification Results
+
+| Kontrola | Wynik | Uwagi |
+|----------|-------|-------|
+| `php -l app/Views/receipts/new.php` | Pass | Brak błędów składni. |
+| `php -l app/Views/receipts/review.php` | Pass | Brak błędów składni. |
+| Kontrola formularza | Pass | Jedno pole `receipt`, zachowane `required`, `accept`, limit 10 MB i UTF-8 bez BOM. |
+| Kontrola granic | Pass | Kontroler, parser, routing, modele i baza bez zmian. |
+| Mobilny UAT | Pass | Użytkownik potwierdził aparat oraz poprawiony ekran przeglądu. |
+
+## Quality Radar Results
+
+**Status:** ok
+
+- Nowe ryzyka: brak.
+- Rozwiązane ryzyka: mobilny overflow formularza przeglądu usunięty bez drugiego przepływu uploadu.
+- Odroczone ryzyka: zachowanie `capture` zależy od przeglądarki; potwierdzone na docelowym telefonie. WebRTC, kadrowanie i wiele zdjęć pozostają poza zakresem.
+- Raw outputs: `.paul/codebase/radar/codebase-memory-post-apply.txt`
+
+## Deviations
+
+- Po pierwszym UAT użytkownik zgłosił rozjeżdżający się mobilny formularz po analizie AI. Zakres rozszerzono o `app/Views/receipts/review.php`; zmiana nie naruszyła backendu ani kontraktu formularza.
+
+## Key Decisions / Patterns
+
+- Natywne HTML i responsywny CSS/Bootstrap zamiast WebRTC lub nowej zależności.
+- Jeden istniejący endpoint i jedno źródło logiki importu.
+
+## Follow-up
+
+- Poprawić mobilny widok historii importów — osobne zlecenie użytkownika po zamknięciu tej pętli.
diff --git a/.vscode/ftp-kr.sync.cache.json b/.vscode/ftp-kr.sync.cache.json
index 67f6adf..462a144 100644
--- a/.vscode/ftp-kr.sync.cache.json
+++ b/.vscode/ftp-kr.sync.cache.json
@@ -58,16 +58,22 @@
"lmtime": 1783364116680,
"modified": false
},
+ "2026-07-06-200005_AddDisplayNameToReceiptItems.php": {
+ "type": "-",
+ "size": 580,
+ "lmtime": 1783366821444,
+ "modified": false
+ },
".gitkeep": {
"type": "-",
"size": 0,
"lmtime": 0,
"modified": false
},
- "2026-07-06-200005_AddDisplayNameToReceiptItems.php": {
+ "2026-07-12-160001_CreateRememberTokens.php": {
"type": "-",
- "size": 580,
- "lmtime": 1783366821444,
+ "size": 1003,
+ "lmtime": 1783867980803,
"modified": false
}
}
@@ -117,8 +123,14 @@
},
"ReceiptModel.php": {
"type": "-",
- "size": 937,
- "lmtime": 1783364152838,
+ "size": 1082,
+ "lmtime": 1783870528100,
+ "modified": false
+ },
+ "RememberTokenModel.php": {
+ "type": "-",
+ "size": 2015,
+ "lmtime": 1783868007680,
"modified": false
},
"ValuationModel.php": {
@@ -145,8 +157,8 @@
"Controllers": {
"Auth.php": {
"type": "-",
- "size": 1314,
- "lmtime": 0,
+ "size": 2159,
+ "lmtime": 1783868092101,
"modified": false
},
"BaseController.php": {
@@ -199,8 +211,8 @@
},
"Receipts.php": {
"type": "-",
- "size": 11901,
- "lmtime": 1783366894415,
+ "size": 12169,
+ "lmtime": 1783870528101,
"modified": false
},
"Valuations.php": {
@@ -219,7 +231,14 @@
}
},
"Views": {
- "auth": {},
+ "auth": {
+ "login.php": {
+ "type": "-",
+ "size": 1378,
+ "lmtime": 1783868092103,
+ "modified": false
+ }
+ },
"categories": {},
"dashboard": {},
"errors": {},
@@ -229,8 +248,8 @@
"layout": {
"main.php": {
"type": "-",
- "size": 3815,
- "lmtime": 1783364283479,
+ "size": 3980,
+ "lmtime": 1783870528102,
"modified": false
}
},
@@ -281,6 +300,14 @@
"modified": false
}
}
+ },
+ "Filters": {
+ "AuthFilter.php": {
+ "type": "-",
+ "size": 1020,
+ "lmtime": 1783868092102,
+ "modified": false
+ }
}
},
"builds": {
@@ -340,26 +367,57 @@
"size": 4897,
"lmtime": 1783367953541,
"modified": false
+ },
+ "2026-07-12.md": {
+ "type": "-",
+ "size": 634,
+ "lmtime": 1783870371082,
+ "modified": false
}
},
"codebase": {
"impact_map.md": {
"type": "-",
- "size": 3932,
- "lmtime": 1783368033490,
+ "size": 4777,
+ "lmtime": 1783870371083,
"modified": false
},
"quality_risks.md": {
"type": "-",
- "size": 2521,
- "lmtime": 1783368045743,
+ "size": 3382,
+ "lmtime": 1783870371084,
"modified": false
},
- "radar": {},
+ "radar": {
+ "codebase-memory-plan.txt": {
+ "type": "-",
+ "size": 483,
+ "lmtime": 1783867802646,
+ "modified": false
+ },
+ "codebase-memory-post-apply.txt": {
+ "type": "-",
+ "size": 553,
+ "lmtime": 1783870371080,
+ "modified": false
+ }
+ },
"tooling_status.md": {
"type": "-",
- "size": 1103,
- "lmtime": 1783364442507,
+ "size": 513,
+ "lmtime": 1783870371086,
+ "modified": false
+ },
+ "architecture.md": {
+ "type": "-",
+ "size": 652,
+ "lmtime": 1783870371081,
+ "modified": false
+ },
+ "db_schema.md": {
+ "type": "-",
+ "size": 598,
+ "lmtime": 1783870371081,
"modified": false
}
},
@@ -371,6 +429,8 @@
},
"handoffs": {},
"plans": {
+ "20260706-1248-fundament-wydatki-przychody": {},
+ "20260706-1430-modul-inwestycji": {},
"20260706-2046-import-paragonow": {
"PLAN.md": {
"type": "-",
@@ -384,19 +444,42 @@
"lmtime": 1783367919539,
"modified": false
}
+ },
+ "20260712-1647-zapamietywanie-logowania": {
+ "PLAN.md": {
+ "type": "-",
+ "size": 7269,
+ "lmtime": 1783867802645,
+ "modified": false
+ },
+ "SUMMARY.md": {
+ "type": "-",
+ "size": 3439,
+ "lmtime": 1783870407435,
+ "modified": false
+ }
}
},
"PROJECT.md": {
"type": "-",
- "size": 645,
- "lmtime": 0,
+ "size": 1407,
+ "lmtime": 1783870371088,
+ "modified": false
+ },
+ "rules": {
+ "ast-grep": {},
+ "jscpd": {}
+ },
+ "run_remember_tokens_migration.php": {
+ "type": "-",
+ "size": 1746,
+ "lmtime": 1783869602000,
"modified": false
},
- "rules": {},
"STATE.md": {
"type": "-",
- "size": 2648,
- "lmtime": 1783368004681,
+ "size": 1853,
+ "lmtime": 1783870407436,
"modified": false
}
},
@@ -451,9 +534,50 @@
"lmtime": 0,
"modified": false
},
- "tests": {},
+ "tests": {
+ "unit": {
+ "HealthTest.php": {
+ "type": "-",
+ "size": 1440,
+ "lmtime": 0,
+ "modified": false
+ },
+ "RememberTokenModelTest.php": {
+ "type": "-",
+ "size": 914,
+ "lmtime": 1783868195270,
+ "modified": false
+ },
+ "ReceiptModelTest.php": {
+ "type": "-",
+ "size": 737,
+ "lmtime": 1783870528103,
+ "modified": false
+ }
+ }
+ },
"vendor": {},
- "writable": {}
+ "writable": {},
+ ".codebase-memory": {
+ ".gitattributes": {
+ "type": "-",
+ "size": 123,
+ "lmtime": 1783867604274,
+ "modified": false
+ },
+ "artifact.json": {
+ "type": "-",
+ "size": 263,
+ "lmtime": 1783867604273,
+ "modified": false
+ },
+ "graph.db.zst": {
+ "type": "-",
+ "size": 315382,
+ "lmtime": 1783867604244,
+ "modified": false
+ }
+ }
}
},
"$version": 1
diff --git a/app/Views/instruments/index.php b/app/Views/instruments/index.php
index 779ce6c..a47b67e 100644
--- a/app/Views/instruments/index.php
+++ b/app/Views/instruments/index.php
@@ -3,14 +3,14 @@
= $this->section('content') ?>
number_format((float) $v, 2, ',', ' ') . ' zł'; ?>
-
+
-
+
Instrument
@@ -25,17 +25,17 @@
Brak instrumentów.
-
- = esc($i['name']) ?>
- = $fmt($i['net_invested']) ?>
- = $i['has_value'] ? $fmt($i['value']) : 'brak wyceny ' ?>
-
+
+ = esc($i['name']) ?>
+ = $fmt($i['net_invested']) ?>
+ = $i['has_value'] ? $fmt($i['value']) : 'brak wyceny ' ?>
+
= $i['has_value'] ? $fmt($i['profit']) : '—' ?>
-
+
= $i['percent'] !== null && $i['has_value'] ? number_format($i['percent'], 2, ',', ' ') . '%' : '—' ?>
-
+
Edytuj
Usuń
@@ -46,4 +46,33 @@
+
+
= $this->endSection() ?>
diff --git a/app/Views/inv_operations/index.php b/app/Views/inv_operations/index.php
index 2f40f9f..0862736 100644
--- a/app/Views/inv_operations/index.php
+++ b/app/Views/inv_operations/index.php
@@ -3,7 +3,7 @@
= $this->section('content') ?>
number_format((float) $v, 2, ',', ' ') . ' zł'; ?>
-
+
Operacje inwestycyjne
+ Nowa operacja
@@ -47,7 +47,7 @@
-
+
Data
@@ -63,19 +63,19 @@
Brak operacji dla wybranych filtrów.
-
- = esc($o['date']) ?>
- = esc($o['instrument_name'] ?? '—') ?>
-
+
+ = esc($o['date']) ?>
+ = esc($o['instrument_name'] ?? '—') ?>
+
= $isDep ? 'Wpłata' : 'Wypłata' ?>
- = esc($o['description'] ?? '') ?>
-
+ = esc($o['description'] ?? '') ?>
+
= $isDep ? '+' : '−' ?>= $fmt($o['amount']) ?>
-
+
Edytuj
Usuń
@@ -86,4 +86,33 @@
+
+
= $this->endSection() ?>
diff --git a/app/Views/receipts/index.php b/app/Views/receipts/index.php
index 1e6a151..1fcb989 100644
--- a/app/Views/receipts/index.php
+++ b/app/Views/receipts/index.php
@@ -11,14 +11,14 @@ $badges = [
];
?>
-
+
Import paragonów
+ Nowy import
-
+
Data
@@ -34,14 +34,14 @@ $badges = [
Brak zaimportowanych paragonów.
-
- = esc($r['receipt_date'] ?: mb_substr((string) $r['created_at'], 0, 10)) ?>
- = esc($r['merchant'] ?: '—') ?>
- = $r['total'] !== null ? $fmt($r['total']) : '—' ?>
- = (int) $r['items_count'] ?>
- = (int) $r['operations_count'] ?>
- = esc($r['status']) ?>
-
+
+ = esc($r['receipt_date'] ?: mb_substr((string) $r['created_at'], 0, 10)) ?>
+ = esc($r['merchant'] ?: '—') ?>
+ = $r['total'] !== null ? $fmt($r['total']) : '—' ?>
+ = (int) $r['items_count'] ?>
+ = (int) $r['operations_count'] ?>
+ = esc($r['status']) ?>
+
Przegląd
@@ -56,4 +56,30 @@ $badges = [
+
+
= $this->endSection() ?>
diff --git a/app/Views/receipts/new.php b/app/Views/receipts/new.php
index 3bb1787..8d719c2 100644
--- a/app/Views/receipts/new.php
+++ b/app/Views/receipts/new.php
@@ -10,11 +10,12 @@
= form_open_multipart('receipts', ['class' => 'row g-3']) ?>
-
Plik paragonu
+
Zrób zdjęcie lub wybierz plik paragonu
+ accept=".jpg,.jpeg,.png,.pdf,.json,image/*,application/pdf,application/json"
+ capture="environment">
- Obsługiwane formaty: skan JPG/PNG, PDF lub JSON. Maks. 10 MB.
+ Na telefonie możesz zrobić zdjęcie paragonu. Obsługiwane formaty: JPG/PNG, PDF lub JSON. Maks. 10 MB.
Po wgraniu AI odczyta pozycje i zaproponuje kategorie do zatwierdzenia.
diff --git a/app/Views/receipts/review.php b/app/Views/receipts/review.php
index aae0345..29679b7 100644
--- a/app/Views/receipts/review.php
+++ b/app/Views/receipts/review.php
@@ -3,7 +3,7 @@
= $this->section('content') ?>
number_format((float) $v, 2, ',', ' ') . ' zł'; ?>
-
+
@@ -23,7 +23,7 @@
(podpowiedź zaznaczona; „— pomiń —” = nie księguj)
-
+
Pozycja
@@ -37,17 +37,17 @@
-
+
z paragonu: = esc($it['name']) ?>
-
+
-
+
— pomiń —
@@ -71,12 +71,28 @@
-
+
= form_close() ?>
+
+
= $this->endSection() ?>
= $this->section('scripts') ?>
@@ -104,7 +120,7 @@
return;
}
preview.innerHTML = keys.map((k) =>
- `
${groups[k].label} ${fmt(groups[k].sum)} `
+ `
${groups[k].label} ${fmt(groups[k].sum)} `
).join('');
}
rows.forEach((row) => {
diff --git a/app/Views/valuations/index.php b/app/Views/valuations/index.php
index 4795e5f..195ad8a 100644
--- a/app/Views/valuations/index.php
+++ b/app/Views/valuations/index.php
@@ -3,7 +3,7 @@
= $this->section('content') ?>
number_format((float) $v, 2, ',', ' ') . ' zł'; ?>
-
+
@@ -31,7 +31,7 @@
-
+
Data
@@ -44,11 +44,11 @@
Brak wycen.
-
- = esc($v['date']) ?>
- = esc($v['instrument_name'] ?? '—') ?>
- = $fmt($v['value']) ?>
-
+
+ = esc($v['date']) ?>
+ = esc($v['instrument_name'] ?? '—') ?>
+ = $fmt($v['value']) ?>
+
Edytuj
Usuń
@@ -59,4 +59,33 @@
+
+
= $this->endSection() ?>