feat(20-windows-client): aplikacja C# WinForms do zdalnego druku etykiet
- System tray app z NotifyIcon + ContextMenuStrip - Polling API orderPRO (GET /api/print/jobs/pending) - Pobieranie etykiet PDF i druk przez PdfiumViewer - Formularz ustawień: URL API, klucz, drukarka, interwał - Okno logów z historią (ciemny motyw, Consolas) - Self-contained .NET 8 publish (win-x64) - Milestone v0.7 Zdalne drukowanie etykiet — COMPLETE Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
324
.paul/phases/20-windows-client/20-01-PLAN.md
Normal file
324
.paul/phases/20-windows-client/20-01-PLAN.md
Normal file
@@ -0,0 +1,324 @@
|
||||
---
|
||||
phase: 20-windows-client
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: ["18-01", "19-01"]
|
||||
files_modified:
|
||||
- clients/windows/OrderPROPrint/OrderPROPrint.sln
|
||||
- clients/windows/OrderPROPrint/OrderPROPrint.csproj
|
||||
- clients/windows/OrderPROPrint/Program.cs
|
||||
- clients/windows/OrderPROPrint/TrayApplicationContext.cs
|
||||
- clients/windows/OrderPROPrint/Services/PrintApiClient.cs
|
||||
- clients/windows/OrderPROPrint/Services/PrintService.cs
|
||||
- clients/windows/OrderPROPrint/Services/PollingService.cs
|
||||
- clients/windows/OrderPROPrint/Forms/SettingsForm.cs
|
||||
- clients/windows/OrderPROPrint/Forms/SettingsForm.Designer.cs
|
||||
- clients/windows/OrderPROPrint/Models/PrintJob.cs
|
||||
- clients/windows/OrderPROPrint/Models/AppSettings.cs
|
||||
- clients/windows/OrderPROPrint/Properties/Resources.resx
|
||||
- clients/windows/OrderPROPrint/app.config
|
||||
autonomous: false
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Stworzyć aplikację C# WinForms działającą w system tray, która odpytuje API orderPRO o zlecenia wydruku, pobiera etykiety i drukuje je na drukarce termicznej Xprinter XP-420B (format A6).
|
||||
|
||||
## Purpose
|
||||
Użytkownik może zlecić wydruk etykiety z przeglądarki (faza 19) i etykieta automatycznie wydrukuje się na drukarce podłączonej do jego komputera — bez ręcznego pobierania pliku i drukowania.
|
||||
|
||||
## Output
|
||||
- Aplikacja WinForms (.NET 8) w `clients/windows/OrderPROPrint/`
|
||||
- System tray icon z menu kontekstowym
|
||||
- Polling API co N sekund (konfigurowalne)
|
||||
- Automatyczny druk etykiet A6 na wybranej drukarce
|
||||
- Formularz ustawień (URL API, klucz API, drukarka, interwał)
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Prior Work
|
||||
@.paul/phases/18-print-queue-backend/18-01-SUMMARY.md
|
||||
- API endpoints (API key auth via X-Api-Key header, SHA-256):
|
||||
- GET /api/print/jobs/pending — lista zleceń do wydruku (JSON array)
|
||||
- GET /api/print/jobs/{id}/download — pobieranie pliku etykiety (binary)
|
||||
- POST /api/print/jobs/{id}/complete — oznaczenie jako wydrukowane
|
||||
- Auth: header X-Api-Key z raw key (serwer hashuje SHA-256 i porównuje)
|
||||
|
||||
@.paul/phases/19-ui-integration/19-01-SUMMARY.md
|
||||
- UI tworzy print jobs przez POST /api/print/jobs i /api/print/jobs/bulk
|
||||
- Status "pending" = gotowe do pobrania przez Windows Client
|
||||
|
||||
## Source Files
|
||||
@src/Modules/Printing/PrintApiController.php — API endpoints consumed by this client
|
||||
@src/Modules/Printing/ApiKeyMiddleware.php — auth pattern (X-Api-Key header)
|
||||
</context>
|
||||
|
||||
<skills>
|
||||
## Required Skills (from SPECIAL-FLOWS.md)
|
||||
|
||||
| Skill | Priority | When to Invoke | Loaded? |
|
||||
|-------|----------|----------------|---------|
|
||||
| sonar-scanner | required | Po APPLY, przed UNIFY | ○ |
|
||||
|
||||
## Skill Invocation Checklist
|
||||
- [ ] sonar-scanner loaded (run before UNIFY) — uwaga: skanuje tylko PHP, C# poza zakresem
|
||||
</skills>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Aplikacja uruchamia się w system tray
|
||||
```gherkin
|
||||
Given aplikacja OrderPROPrint jest uruchomiona
|
||||
When użytkownik patrzy na system tray (obszar powiadomień)
|
||||
Then widzi ikonę aplikacji OrderPROPrint
|
||||
And po kliknięciu prawym przyciskiem widzi menu: "Ustawienia", "Wstrzymaj/Wznów", "O programie", "Zamknij"
|
||||
And dwuklik otwiera formularz ustawień
|
||||
```
|
||||
|
||||
## AC-2: Konfiguracja połączenia z API
|
||||
```gherkin
|
||||
Given użytkownik otworzył formularz ustawień
|
||||
When wpisuje URL API (np. https://orderpro.projectpro.pl), klucz API i wybiera drukarkę
|
||||
Then ustawienia zapisują się w pliku konfiguracyjnym (app.config lub JSON)
|
||||
And po ponownym uruchomieniu ustawienia są zapamiętane
|
||||
And przycisk "Testuj połączenie" weryfikuje poprawność URL + klucza
|
||||
```
|
||||
|
||||
## AC-3: Polling i pobieranie zleceń
|
||||
```gherkin
|
||||
Given aplikacja ma poprawną konfigurację API
|
||||
When polling timer odpytuje GET /api/print/jobs/pending
|
||||
Then pobiera listę zleceń ze statusem "pending"
|
||||
And dla każdego zlecenia pobiera etykietę GET /api/print/jobs/{id}/download
|
||||
And po wydruku oznacza POST /api/print/jobs/{id}/complete
|
||||
And ikona tray pokazuje liczbę przetworzonych zleceń (tooltip lub balloon)
|
||||
```
|
||||
|
||||
## AC-4: Drukowanie etykiety A6 na drukarce termicznej
|
||||
```gherkin
|
||||
Given pobrano plik etykiety (PDF)
|
||||
When aplikacja wysyła do wybranej drukarki
|
||||
Then etykieta drukuje się w formacie A6 (105×148mm)
|
||||
And orientacja i marginesy są poprawne dla drukarki termicznej
|
||||
And w razie błędu druku zlecenie nie jest oznaczane jako complete
|
||||
```
|
||||
|
||||
## AC-5: Obsługa błędów i odporność
|
||||
```gherkin
|
||||
Given aplikacja jest uruchomiona
|
||||
When API jest niedostępne lub klucz nieprawidłowy
|
||||
Then ikona tray zmienia się na stan "error" (np. czerwona)
|
||||
And tooltip pokazuje ostatni błąd
|
||||
And polling kontynuuje próby (nie crash)
|
||||
And po przywróceniu połączenia wraca do normalnego działania
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Projekt C# WinForms + system tray + ustawienia</name>
|
||||
<files>
|
||||
clients/windows/OrderPROPrint/OrderPROPrint.sln,
|
||||
clients/windows/OrderPROPrint/OrderPROPrint.csproj,
|
||||
clients/windows/OrderPROPrint/Program.cs,
|
||||
clients/windows/OrderPROPrint/TrayApplicationContext.cs,
|
||||
clients/windows/OrderPROPrint/Forms/SettingsForm.cs,
|
||||
clients/windows/OrderPROPrint/Forms/SettingsForm.Designer.cs,
|
||||
clients/windows/OrderPROPrint/Models/AppSettings.cs,
|
||||
clients/windows/OrderPROPrint/Properties/Resources.resx
|
||||
</files>
|
||||
<action>
|
||||
1. Utwórz projekt .NET 8 WinForms w `clients/windows/OrderPROPrint/`:
|
||||
- `dotnet new winforms -n OrderPROPrint`
|
||||
- Target: net8.0-windows
|
||||
- NuGet: System.Text.Json (wbudowane), żadnych zewnętrznych zależności
|
||||
|
||||
2. Program.cs:
|
||||
- Application.Run(new TrayApplicationContext())
|
||||
- Nie pokazuj głównego okna — tylko tray
|
||||
|
||||
3. TrayApplicationContext (dziedziczy ApplicationContext):
|
||||
- NotifyIcon z ikoną (embedded resource lub SystemIcons.Application)
|
||||
- ContextMenuStrip z pozycjami:
|
||||
- "Ustawienia" → otwiera SettingsForm
|
||||
- "Wstrzymaj" / "Wznów" → toggle polling
|
||||
- separator
|
||||
- "O programie" → MessageBox z wersją
|
||||
- "Zamknij" → Application.Exit()
|
||||
- DoubleClick na ikonę → otwiera SettingsForm
|
||||
- Tooltip: "OrderPRO Print — Oczekiwanie" (aktualizowany przez PollingService)
|
||||
|
||||
4. AppSettings (model):
|
||||
- ApiUrl (string), ApiKey (string), PrinterName (string), PollIntervalSeconds (int, default 10)
|
||||
- Zapis/odczyt do JSON: %APPDATA%/OrderPROPrint/settings.json
|
||||
- Metoda Load() i Save() — statyczne
|
||||
|
||||
5. SettingsForm (WinForms designer):
|
||||
- Pola: txtApiUrl, txtApiKey, cmbPrinter, nudPollInterval
|
||||
- cmbPrinter: wypełniony z System.Drawing.Printing.PrinterSettings.InstalledPrinters
|
||||
- Przycisk "Testuj połączenie" — GET /api/print/jobs/pending z podanym URL+key
|
||||
- Sukces: zielony label "Połączono ✓"
|
||||
- Błąd: czerwony label z komunikatem
|
||||
- Przycisk "Zapisz" — AppSettings.Save() + zamknij formularz
|
||||
- Przycisk "Anuluj" — zamknij bez zapisu
|
||||
|
||||
Avoid: Nie używaj WPF ani MAUI — czysty WinForms dla prostoty
|
||||
Avoid: Nie dodawaj auto-start na tym etapie (to future enhancement)
|
||||
</action>
|
||||
<verify>
|
||||
- `dotnet build` przechodzi bez błędów
|
||||
- Aplikacja uruchamia się i pojawia się ikona w system tray
|
||||
- Menu kontekstowe działa
|
||||
- Formularz ustawień otwiera się i zamyka
|
||||
- Lista drukarek się ładuje
|
||||
- Ustawienia zapisują się do %APPDATA%/OrderPROPrint/settings.json
|
||||
</verify>
|
||||
<done>AC-1 satisfied (system tray), AC-2 satisfied (konfiguracja)</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: API client + polling + drukowanie</name>
|
||||
<files>
|
||||
clients/windows/OrderPROPrint/Services/PrintApiClient.cs,
|
||||
clients/windows/OrderPROPrint/Services/PrintService.cs,
|
||||
clients/windows/OrderPROPrint/Services/PollingService.cs,
|
||||
clients/windows/OrderPROPrint/Models/PrintJob.cs,
|
||||
clients/windows/OrderPROPrint/TrayApplicationContext.cs
|
||||
</files>
|
||||
<action>
|
||||
1. PrintJob (model):
|
||||
- Id (int), OrderId (int), PackageId (int), LabelPath (string), Status (string)
|
||||
- Deserializacja z JSON response GET /api/print/jobs/pending
|
||||
|
||||
2. PrintApiClient:
|
||||
- Constructor: (string apiUrl, string apiKey)
|
||||
- HttpClient z default header X-Api-Key
|
||||
- async Task<List<PrintJob>> GetPendingJobsAsync()
|
||||
- GET {apiUrl}/api/print/jobs/pending
|
||||
- Deserializuj JSON response → List<PrintJob>
|
||||
- async Task<byte[]> DownloadLabelAsync(int jobId)
|
||||
- GET {apiUrl}/api/print/jobs/{jobId}/download
|
||||
- Return raw bytes (PDF)
|
||||
- async Task<bool> MarkCompleteAsync(int jobId)
|
||||
- POST {apiUrl}/api/print/jobs/{jobId}/complete
|
||||
- Return true jeśli 200
|
||||
- async Task<bool> TestConnectionAsync()
|
||||
- GET {apiUrl}/api/print/jobs/pending (sprawdza czy nie 401)
|
||||
- Obsługa błędów: HttpRequestException, TaskCanceledException (timeout)
|
||||
|
||||
3. PrintService:
|
||||
- static void PrintPdf(byte[] pdfBytes, string printerName)
|
||||
- Zapisz PDF do temp file → drukuj przez Process.Start z parametrami drukarki
|
||||
- Alternatywa: użyj System.Drawing.Printing.PrintDocument z renderowaniem PDF
|
||||
- Preferowane podejście: zapisz temp PDF, użyj SumatraPDF CLI (portable) do silent print:
|
||||
`SumatraPDF.exe -print-to "PrinterName" -silent label.pdf`
|
||||
- Jeśli SumatraPDF niedostępny: fallback na ShellExecute "print" verb
|
||||
- Ustaw rozmiar papieru: A6 (105×148mm) lub custom size z drukarki
|
||||
- Po wydruku: usuń temp file
|
||||
|
||||
4. PollingService:
|
||||
- Constructor: (PrintApiClient client, PrintService printService, Action<string> onStatusUpdate, Action<string> onError)
|
||||
- System.Threading.Timer z interwałem z AppSettings
|
||||
- Na każdy tick:
|
||||
a. GetPendingJobsAsync()
|
||||
b. Dla każdego job: DownloadLabelAsync → PrintPdf → MarkCompleteAsync
|
||||
c. Aktualizuj status (callback onStatusUpdate)
|
||||
d. Przy błędzie: callback onError, nie przerywaj pętli
|
||||
- Start() / Stop() / IsRunning property
|
||||
- Mutex/lock żeby nie nakładały się dwa ticki
|
||||
|
||||
5. Integracja w TrayApplicationContext:
|
||||
- Po załadowaniu ustawień: utwórz PrintApiClient + PollingService
|
||||
- onStatusUpdate → aktualizuj tooltip ikony ("Wydrukowano 3 etykiety")
|
||||
- onError → zmień ikonę na error state, tooltip z komunikatem
|
||||
- "Wstrzymaj" → PollingService.Stop(), zmień tekst menu na "Wznów"
|
||||
- Przy zmianie ustawień → restart PollingService z nowymi parametrami
|
||||
|
||||
Avoid: Nie blokuj UI thread — cała komunikacja HTTP i drukowanie async
|
||||
Avoid: Nie ignoruj błędów — każdy failed print musi być widoczny
|
||||
Avoid: Nie oznaczaj jako complete jeśli druk nie powiódł się
|
||||
</action>
|
||||
<verify>
|
||||
- `dotnet build` przechodzi
|
||||
- Aplikacja łączy się z API (test z prawdziwym kluczem)
|
||||
- Polling pobiera pending jobs
|
||||
- Etykieta drukuje się na wskazanej drukarce
|
||||
- Po wydruku job oznaczany jako complete w UI orderPRO
|
||||
- Błąd API nie crashuje aplikacji
|
||||
</verify>
|
||||
<done>AC-3 satisfied (polling), AC-4 satisfied (drukowanie), AC-5 satisfied (obsługa błędów)</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>
|
||||
Aplikacja Windows OrderPROPrint — system tray, polling API, drukowanie etykiet A6.
|
||||
</what-built>
|
||||
<how-to-verify>
|
||||
1. Otwórz `clients/windows/OrderPROPrint/` w Visual Studio lub uruchom `dotnet run`
|
||||
2. Sprawdź: ikona pojawia się w system tray
|
||||
3. Prawy klik → "Ustawienia" → wpisz URL + klucz API + wybierz drukarkę
|
||||
4. Kliknij "Testuj połączenie" → powinno być "Połączono ✓"
|
||||
5. Zapisz ustawienia, zamknij formularz
|
||||
6. W orderPRO: zlecij wydruk etykiety (przycisk "Drukuj" w widoku przesyłki)
|
||||
7. Poczekaj na polling (domyślnie 10s)
|
||||
8. Sprawdź: etykieta wydrukowana na drukarce Xprinter
|
||||
9. Sprawdź: w orderPRO kolejka wydruku → status "completed"
|
||||
10. Sprawdź: tooltip ikony pokazuje liczbę wydrukowanych
|
||||
11. Odłącz internet → sprawdź czy ikona zmienia się na "error"
|
||||
12. Podłącz z powrotem → polling powinien wrócić do normy
|
||||
</how-to-verify>
|
||||
<resume-signal>Type "approved" to continue, or describe issues to fix</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- src/Modules/Printing/* (backend API gotowe z fazy 18-19)
|
||||
- database/migrations/* (schemat stabilny)
|
||||
- Endpointy API — klient konsumuje istniejące, nie modyfikuje serwera
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie dodawaj auto-start z Windows (future enhancement)
|
||||
- Nie buduj instalatora MSI/Setup (na razie `dotnet publish`)
|
||||
- Nie dodawaj auto-update mechanizmu
|
||||
- Nie modyfikuj kodu PHP — to jest czysty C# project
|
||||
- Drukowanie tylko PDF — nie konwertuj do ZPL/EPL (drukarki termiczne z Windows driver)
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] `dotnet build` przechodzi bez błędów i ostrzeżeń
|
||||
- [ ] Aplikacja uruchamia się w system tray (brak głównego okna)
|
||||
- [ ] Formularz ustawień: URL, klucz API, drukarka, interwał
|
||||
- [ ] "Testuj połączenie" działa z prawdziwym API
|
||||
- [ ] Polling pobiera pending jobs z API
|
||||
- [ ] Etykieta PDF drukuje się na wybranej drukarce
|
||||
- [ ] Po wydruku job oznaczany jako complete
|
||||
- [ ] Błędy nie crashują aplikacji
|
||||
- [ ] Tooltip aktualizuje się z informacjami o statusie
|
||||
- [ ] Brak natywnych alert() — to WinForms, więc MessageBox jest OK
|
||||
- [ ] Wszystkie acceptance criteria spełnione
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Wszystkie 5 AC spełnione
|
||||
- Aplikacja stabilna — działa ciągle w tle bez crash
|
||||
- Komunikacja z API poprawna (auth, polling, download, complete)
|
||||
- Druk na drukarce termicznej w formacie A6
|
||||
- Kod czytelny, SRP, async/await
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/20-windows-client/20-01-SUMMARY.md`
|
||||
</output>
|
||||
149
.paul/phases/20-windows-client/20-01-SUMMARY.md
Normal file
149
.paul/phases/20-windows-client/20-01-SUMMARY.md
Normal file
@@ -0,0 +1,149 @@
|
||||
---
|
||||
phase: 20-windows-client
|
||||
plan: 01
|
||||
subsystem: desktop-app
|
||||
tags: [csharp, winforms, printing, system-tray, pdfium, thermal-printer]
|
||||
|
||||
requires:
|
||||
- phase: 18-print-queue-backend
|
||||
provides: REST API (pending, download, complete), API key auth
|
||||
- phase: 19-ui-integration
|
||||
provides: UI creating print jobs (single + bulk)
|
||||
provides:
|
||||
- Windows tray application polling API and printing labels
|
||||
- Self-contained .exe with PdfiumViewer for PDF rendering
|
||||
affects: []
|
||||
|
||||
tech-stack:
|
||||
added: [".NET 8 WinForms", "PdfiumViewer 2.13.0", "pdfium native x64"]
|
||||
patterns: [polling-service, tray-application-context, synchronization-context-ui-updates]
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- clients/windows/OrderPROPrint/OrderPROPrint.sln
|
||||
- clients/windows/OrderPROPrint/OrderPROPrint.csproj
|
||||
- clients/windows/OrderPROPrint/Program.cs
|
||||
- clients/windows/OrderPROPrint/TrayApplicationContext.cs
|
||||
- clients/windows/OrderPROPrint/Models/AppSettings.cs
|
||||
- clients/windows/OrderPROPrint/Models/PrintJob.cs
|
||||
- clients/windows/OrderPROPrint/Services/PrintApiClient.cs
|
||||
- clients/windows/OrderPROPrint/Services/PrintService.cs
|
||||
- clients/windows/OrderPROPrint/Services/PollingService.cs
|
||||
- clients/windows/OrderPROPrint/Forms/SettingsForm.cs
|
||||
- clients/windows/OrderPROPrint/Forms/SettingsForm.Designer.cs
|
||||
- clients/windows/OrderPROPrint/Forms/LogForm.cs
|
||||
- clients/windows/OrderPROPrint/Forms/LogForm.Designer.cs
|
||||
|
||||
key-decisions:
|
||||
- "PdfiumViewer instead of SumatraPDF — zero external dependencies, native PDF rendering"
|
||||
- "Directory publish instead of single-file — pdfium.dll native not compatible with PublishSingleFile"
|
||||
- "SynchronizationContext.Post for thread-safe tray icon updates from polling timer"
|
||||
- "Option C (single client) — multi-client support deferred"
|
||||
|
||||
patterns-established:
|
||||
- "C# desktop app lives in clients/windows/ subdirectory of orderPRO repo"
|
||||
- "PendingJobsResponse wrapper for API JSON deserialization ({jobs: [...]})"
|
||||
|
||||
duration: ~2h
|
||||
started: 2026-03-22T20:30:00Z
|
||||
completed: 2026-03-22T22:45:00Z
|
||||
---
|
||||
|
||||
# Phase 20 Plan 01: Windows Client Summary
|
||||
|
||||
**Aplikacja C# WinForms w system tray — polling API orderPRO, automatyczne pobieranie i drukowanie etykiet PDF na drukarce termicznej via PdfiumViewer.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~2h |
|
||||
| Started | 2026-03-22 |
|
||||
| Completed | 2026-03-22 |
|
||||
| Tasks | 3 (2 auto + 1 checkpoint) |
|
||||
| Files created | 13 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Aplikacja w system tray | Pass | NotifyIcon + ContextMenuStrip z Ustawienia, Logi, Wstrzymaj, O programie, Zamknij |
|
||||
| AC-2: Konfiguracja API | Pass | SettingsForm z URL, klucz, drukarka, interwał + Test połączenia |
|
||||
| AC-3: Polling i pobieranie | Pass | Timer co N sekund, download label, mark complete |
|
||||
| AC-4: Drukowanie A6 | Pass | PdfiumViewer CreatePrintDocument z PaperSize A6 (105x148mm) |
|
||||
| AC-5: Obsługa błędów | Pass | Error icon, tooltip, logi — polling kontynuuje |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Standalone .exe (self-contained .NET 8, ~170MB z runtime)
|
||||
- System tray z menu kontekstowym i dwuklikiem na ustawienia
|
||||
- Polling z konfigurowalnym interwałem (5-300s)
|
||||
- PdfiumViewer renderuje PDF i drukuje bez zewnętrznych narzędzi
|
||||
- Okno logów z historią (ciemny motyw, Consolas, kopiowanie)
|
||||
- Thread-safe UI updates via SynchronizationContext
|
||||
- Settings w %APPDATA%/OrderPROPrint/settings.json
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Scope additions | 1 | Okno logów (request użytkownika) |
|
||||
| Auto-fixed | 3 | JSON deserialization, pdfium.dll, NotifyIcon API |
|
||||
|
||||
**Total impact:** Lepsza diagnostyka dzięki logom, stabilniejszy druk.
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. JSON deserialization mismatch**
|
||||
- **Issue:** API zwraca `{"jobs": [...]}`, klient deserializował jako `List<PrintJob>`
|
||||
- **Fix:** Dodano `PendingJobsResponse` wrapper, poprawiono property names
|
||||
- **Files:** Models/PrintJob.cs, Services/PrintApiClient.cs
|
||||
|
||||
**2. pdfium.dll missing in publish**
|
||||
- **Issue:** `PublishSingleFile` nie dołącza natywnej DLL z NuGet
|
||||
- **Fix:** Directory publish + MSBuild target `CopyPdfiumNative`
|
||||
- **Files:** OrderPROPrint.csproj
|
||||
|
||||
**3. NotifyIcon API incompatibility**
|
||||
- **Issue:** `NotifyIcon.IsDisposed` i `.Invoke()` nie istnieją w .NET 8
|
||||
- **Fix:** SynchronizationContext.Post + własny `_isDisposed` flag
|
||||
- **Files:** TrayApplicationContext.cs
|
||||
|
||||
### Scope Additions
|
||||
|
||||
**1. Okno logów (LogForm)**
|
||||
- **Requested by:** użytkownik podczas testów
|
||||
- **Purpose:** diagnostyka błędów druku
|
||||
- **Files:** Forms/LogForm.cs, Forms/LogForm.Designer.cs
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| PdfiumViewer zamiast SumatraPDF | Zero external deps, native rendering | Większy .exe ale prostsze wdrożenie |
|
||||
| Directory publish zamiast single-file | pdfium.dll native incompatible | Cały folder publish/ do skopiowania |
|
||||
| Opcja C — jeden klient | Multi-client wymaga zmian backendu | Dla wielu komputerów trzeba dodać api_key_id |
|
||||
| .NET 8 SDK zainstalowany via winget | Potrzebny do budowania projektu | Nowa zależność dev na maszynie |
|
||||
|
||||
## SonarQube Scan
|
||||
|
||||
**Not applicable** — sonar-scanner skonfigurowany tylko dla PHP. Projekt C# jest poza zakresem skanowania.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Milestone v0.7 kompletny — wszystkie 3 fazy zakończone
|
||||
- Pełny flow: zlecenie z UI → API → Windows Client → drukarka
|
||||
|
||||
**Concerns:**
|
||||
- Multi-client: wielu klientów drukuje duplikaty (opcja A/B do rozważenia w przyszłości)
|
||||
- Rozmiar .exe: 170MB (self-contained z runtime .NET)
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 20-windows-client, Plan: 01*
|
||||
*Completed: 2026-03-22*
|
||||
Reference in New Issue
Block a user