430 lines
14 KiB
Markdown
430 lines
14 KiB
Markdown
---
|
|
phase: 94-order-preview-popup
|
|
plan: 01
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- src/Modules/Orders/OrdersController.php
|
|
- routes/web.php
|
|
- resources/views/orders/list.php
|
|
- resources/views/orders/partials/preview-modal.php
|
|
- resources/scss/app.scss
|
|
- resources/scss/components/_order-preview-modal.scss
|
|
- resources/lang/pl.php
|
|
autonomous: true
|
|
delegation: off
|
|
---
|
|
|
|
<objective>
|
|
## Goal
|
|
Dodanie przycisku "Podglad" na liscie zamowien, ktory otwiera popup (modal) ze szczegolami zamowienia. Uzytkownik moze szybko podejrzec dane i skopiowac je do schowka bez wchodzenia w szczegoly.
|
|
|
|
## Purpose
|
|
Oszczednosc czasu — sprzedawca nie musi wchodzic na strone szczegolowy zamowienia, zeby skopiowac adres, numer zamowienia, dane kupujacego itp. Jeden klik otwiera podglad z ikonami kopiowania.
|
|
|
|
## Output
|
|
- Endpoint AJAX: `GET /api/orders/{id}/preview` zwracajacy HTML fragmentu podgladu
|
|
- Partial: `resources/views/orders/partials/preview-modal.php` — modal overlay
|
|
- Przycisk oka w kolumnie `order_ref` kazdego wiersza na liscie
|
|
- Ikony kopiowania przy kluczowych polach (adres dostawy, kupujacy, nr zamowienia)
|
|
</objective>
|
|
|
|
<context>
|
|
## Project Context
|
|
@.paul/PROJECT.md
|
|
@.paul/ROADMAP.md
|
|
@.paul/STATE.md
|
|
|
|
## Source Files
|
|
@src/Modules/Orders/OrdersController.php
|
|
@src/Modules/Orders/OrdersRepository.php
|
|
@resources/views/orders/list.php
|
|
@resources/views/orders/show.php
|
|
@resources/views/components/table-list.php
|
|
@resources/views/orders/partials/email-send-modal.php
|
|
@routes/web.php
|
|
@resources/scss/app.scss
|
|
@resources/lang/pl.php
|
|
</context>
|
|
|
|
<acceptance_criteria>
|
|
|
|
## AC-1: Przycisk podgladu widoczny na liscie zamowien
|
|
```gherkin
|
|
Given lista zamowien /orders/list jest wyswietlona
|
|
When uzytkownik widzi wiersz zamowienia
|
|
Then w kolumnie numeru zamowienia (order_ref) widoczna jest ikona oka (podglad)
|
|
And ikona ma tooltip "Podglad"
|
|
```
|
|
|
|
## AC-2: Klikniecie ikony otwiera popup z danymi zamowienia
|
|
```gherkin
|
|
Given lista zamowien jest wyswietlona
|
|
When uzytkownik klika ikone podgladu przy zamowieniu
|
|
Then otwiera sie modal overlay z danymi zamowienia zaladowanymi przez AJAX
|
|
And modal zawiera: dane kupujacego, adres dostawy, produkty, podsumowanie kwot, nr zamowienia
|
|
And podczas ladowania widoczny jest wskaznik ladowania (spinner/tekst)
|
|
```
|
|
|
|
## AC-3: Kopiowanie danych do schowka
|
|
```gherkin
|
|
Given popup podgladu zamowienia jest otwarty
|
|
When uzytkownik klika ikone kopiowania przy polu (np. adres dostawy, imie kupujacego, nr zamowienia)
|
|
Then tresc pola jest kopiowana do schowka
|
|
And pojawia sie krotkie potwierdzenie (np. zmiana ikony na checkmark na 1.5s)
|
|
```
|
|
|
|
## AC-4: Zamykanie popupu
|
|
```gherkin
|
|
Given popup podgladu jest otwarty
|
|
When uzytkownik klika przycisk X lub tlo (backdrop) lub naciska Escape
|
|
Then popup sie zamyka
|
|
```
|
|
|
|
## AC-5: Link do pelnych szczegolow z popupu
|
|
```gherkin
|
|
Given popup podgladu jest otwarty
|
|
When uzytkownik klika "Pelne szczegoly" w stopce popupu
|
|
Then zostaje przekierowany na /orders/{id}
|
|
```
|
|
|
|
</acceptance_criteria>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Endpoint AJAX preview + routing</name>
|
|
<files>
|
|
src/Modules/Orders/OrdersController.php,
|
|
routes/web.php
|
|
</files>
|
|
<action>
|
|
1. W `OrdersController` dodac metode `preview(Request $request): Response`:
|
|
- Pobierac `id` z request
|
|
- Wywolac `$this->orders->findDetails($orderId)` (istniejaca metoda)
|
|
- Jezeli null -> Response::json(['error' => 'Not found'], 404)
|
|
- Przygotowac dane do widoku: order, items, addresses (customer/delivery/invoice), payments summary
|
|
- Wyrenderowac partial `orders/partials/preview-content` (sam content, bez modala — modal jest w liscie)
|
|
- Zwrocic Response::html($html) (fragment HTML, nie pelna strona)
|
|
|
|
2. W `routes/web.php` dodac route:
|
|
```php
|
|
$router->get('/api/orders/{id}/preview', [$ordersController, 'preview'], [$authMiddleware]);
|
|
```
|
|
Dodac PO linii z `/api/orders/search`.
|
|
|
|
Metoda `preview()` renderuje partial BEZ layoutu (nie przekazywac layoutu do template->render).
|
|
Uzyc `$this->template->renderPartial('orders/partials/preview-content', $data)` lub render bez layoutu.
|
|
Sprawdzic jak dziala renderPartial w Template — jezeli nie istnieje, uzyc `render()` z trzecim parametrem null/pustym.
|
|
|
|
Avoid: NIE zwracac JSON — zwracamy gotowy HTML fragment do wstawienia w modal
|
|
</action>
|
|
<verify>
|
|
GET /api/orders/{id}/preview zwraca HTML fragment z danymi zamowienia (nie pelna strone)
|
|
</verify>
|
|
<done>AC-2 (backend) satisfied — endpoint zwraca HTML podgladu</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Modal partial + przycisk w liscie + JS fetch + kopiowanie</name>
|
|
<files>
|
|
resources/views/orders/partials/preview-modal.php,
|
|
resources/views/orders/list.php,
|
|
src/Modules/Orders/OrdersController.php
|
|
</files>
|
|
<action>
|
|
**preview-modal.php** — kontener modala (pusty, wypelniany AJAXem):
|
|
```php
|
|
<div class="order-preview-overlay" id="order-preview-overlay" style="display:none">
|
|
<div class="order-preview-modal">
|
|
<div class="order-preview-modal__header">
|
|
<h3 class="order-preview-modal__title">Podglad zamowienia</h3>
|
|
<button type="button" class="order-preview-modal__close" id="order-preview-close">×</button>
|
|
</div>
|
|
<div class="order-preview-modal__body" id="order-preview-body">
|
|
<div class="order-preview-loading">Ladowanie...</div>
|
|
</div>
|
|
<div class="order-preview-modal__footer">
|
|
<a href="#" class="btn btn--primary btn--sm" id="order-preview-details-link">Pelne szczegoly</a>
|
|
<button type="button" class="btn btn--secondary btn--sm" id="order-preview-close-btn">Zamknij</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
**preview-content partial** (renderowane przez endpoint, wstawiane do #order-preview-body):
|
|
Nowy plik `resources/views/orders/partials/preview-content.php`:
|
|
- Sekcja: Kupujacy (imie, email, telefon) — z ikonami kopiowania (data-copy-value)
|
|
- Sekcja: Nr zamowienia (internal_order_number, external_order_id) — z ikonami kopiowania
|
|
- Sekcja: Adres dostawy (pelny adres sformatowany w jednej linii) — z ikona kopiowania calego adresu
|
|
- Sekcja: Produkty — lista (nazwa, qty, cena) — kompaktowa tabela
|
|
- Sekcja: Podsumowanie (total, oplacono, waluta, typ platnosci)
|
|
|
|
Kazde pole z kopiowaniem: `<span class="copy-field" data-copy-value="WARTOSC"><span class="copy-field__text">WARTOSC</span><button type="button" class="copy-field__btn" title="Kopiuj">📋</button></span>`
|
|
Zamiast emoji uzyc SVG inline lub znaku Unicode — np. ikona clipboard (ten sam pattern co Phase 92 buyer name copy).
|
|
|
|
**list.php** — dodac:
|
|
1. Na koncu pliku (przed zamykajacym tagiem) dolaczyc partial: `<?php require __DIR__ . '/partials/preview-modal.php'; ?>`
|
|
2. Dolaczyc skrypt JS (inline lub osobny plik) obslugi:
|
|
|
|
**JS w list.php** (inline `<script>`):
|
|
- Delegacja klikniecia: `document.addEventListener('click', ...)` na `.js-order-preview-btn`
|
|
- Po kliknieciu:
|
|
a) Pobrac `data-order-id` z przycisku
|
|
b) Otworzyc overlay (display:flex)
|
|
c) Ustawic body na "Ladowanie..."
|
|
d) fetch(`/api/orders/${id}/preview`) → wstawic HTML do `#order-preview-body`
|
|
e) Ustawic href `#order-preview-details-link` na `/orders/${id}`
|
|
- Zamykanie: klik X, klik backdrop (overlay), Escape
|
|
- Delegacja kopiowania: klik na `.copy-field__btn`:
|
|
a) Pobrac `data-copy-value` z rodzica `.copy-field`
|
|
b) `navigator.clipboard.writeText(value)`
|
|
c) Zmienic ikone na checkmark (✓) na 1.5s, potem przywrocic
|
|
|
|
**OrdersController::toTableRow()** — dodac ikone podgladu w kolumnie `order_ref`:
|
|
Dodac przycisk z ikona oka PRZED linkiem zamowienia:
|
|
```php
|
|
'<button type="button" class="btn-icon js-order-preview-btn" data-order-id="' . (int) ($row['id'] ?? 0) . '" title="Podglad">👁</button>'
|
|
```
|
|
Uzyc ikony SVG inline lub unicode eye (👁) — preferowac prosty SVG jak w istniejacych ikonach projektu.
|
|
Dodac ten przycisk w `order_ref` HTML, np. na poczatku diva `orders-ref`.
|
|
|
|
Avoid:
|
|
- NIE uzywac natywnych alert()/confirm()
|
|
- NIE ladowac pelnej strony w modalu (tylko fragment)
|
|
- NIE dodawac nowych zaleznosci JS
|
|
</action>
|
|
<verify>
|
|
1. Na liscie zamowien widoczna ikona oka przy kazdym zamowieniu
|
|
2. Klikniecie ikony otwiera modal z danymi zamowienia (AJAX)
|
|
3. Ikona kopiowania kopiuje dane do schowka
|
|
4. X / backdrop / Escape zamyka modal
|
|
5. "Pelne szczegoly" prowadzi do /orders/{id}
|
|
</verify>
|
|
<done>AC-1, AC-2, AC-3, AC-4, AC-5 satisfied</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 3: Style SCSS + tlumaczenia</name>
|
|
<files>
|
|
resources/scss/components/_order-preview-modal.scss,
|
|
resources/scss/app.scss,
|
|
resources/lang/pl.php
|
|
</files>
|
|
<action>
|
|
**_order-preview-modal.scss** — nowy plik SCSS:
|
|
```scss
|
|
.order-preview-overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 1000;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 20px;
|
|
}
|
|
|
|
.order-preview-modal {
|
|
background: var(--c-surface);
|
|
border: 1px solid var(--c-border);
|
|
border-radius: 10px;
|
|
box-shadow: 0 16px 48px rgba(0,0,0,0.18);
|
|
width: 100%;
|
|
max-width: 640px;
|
|
max-height: 85vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
&__header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 16px 20px;
|
|
border-bottom: 1px solid var(--c-border);
|
|
}
|
|
|
|
&__title {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
margin: 0;
|
|
}
|
|
|
|
&__close {
|
|
background: none;
|
|
border: none;
|
|
font-size: 22px;
|
|
cursor: pointer;
|
|
color: var(--c-muted);
|
|
padding: 0 4px;
|
|
line-height: 1;
|
|
&:hover { color: var(--c-text); }
|
|
}
|
|
|
|
&__body {
|
|
padding: 16px 20px;
|
|
overflow-y: auto;
|
|
flex: 1;
|
|
}
|
|
|
|
&__footer {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
gap: 8px;
|
|
padding: 12px 20px;
|
|
border-top: 1px solid var(--c-border);
|
|
}
|
|
}
|
|
|
|
.order-preview-loading {
|
|
text-align: center;
|
|
padding: 32px;
|
|
color: var(--c-muted);
|
|
}
|
|
|
|
.order-preview-section {
|
|
margin-bottom: 14px;
|
|
|
|
&__title {
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
color: var(--c-muted);
|
|
margin-bottom: 6px;
|
|
letter-spacing: 0.04em;
|
|
}
|
|
}
|
|
|
|
.order-preview-kv {
|
|
display: grid;
|
|
grid-template-columns: auto 1fr;
|
|
gap: 2px 12px;
|
|
font-size: 13px;
|
|
|
|
dt { color: var(--c-muted); white-space: nowrap; }
|
|
dd { margin: 0; }
|
|
}
|
|
|
|
.order-preview-items {
|
|
width: 100%;
|
|
font-size: 13px;
|
|
border-collapse: collapse;
|
|
|
|
th, td { padding: 4px 8px; text-align: left; }
|
|
th { font-weight: 600; font-size: 11px; text-transform: uppercase; color: var(--c-muted); }
|
|
tbody tr + tr { border-top: 1px solid var(--c-border); }
|
|
}
|
|
|
|
.copy-field {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
|
|
&__btn {
|
|
background: none;
|
|
border: none;
|
|
cursor: pointer;
|
|
font-size: 13px;
|
|
color: var(--c-muted);
|
|
padding: 0 2px;
|
|
line-height: 1;
|
|
opacity: 0.6;
|
|
transition: opacity 0.15s;
|
|
|
|
&:hover { opacity: 1; color: var(--c-primary, #4f6ef7); }
|
|
&.is-copied { color: #22c55e; opacity: 1; }
|
|
}
|
|
}
|
|
|
|
.btn-icon.js-order-preview-btn {
|
|
background: none;
|
|
border: none;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
color: var(--c-muted);
|
|
padding: 2px 4px;
|
|
line-height: 1;
|
|
opacity: 0.6;
|
|
transition: opacity 0.15s;
|
|
vertical-align: middle;
|
|
|
|
&:hover { opacity: 1; color: var(--c-primary, #4f6ef7); }
|
|
}
|
|
```
|
|
|
|
**app.scss** — dodac import:
|
|
Znalezc istniejace importy komponentow i dodac: `@use "components/order-preview-modal";`
|
|
|
|
**pl.php** — dodac klucze tlumaczen w sekcji `orders`:
|
|
```php
|
|
'preview' => [
|
|
'title' => 'Podglad zamowienia',
|
|
'buyer' => 'Kupujacy',
|
|
'order_number' => 'Nr zamowienia',
|
|
'delivery_address' => 'Adres dostawy',
|
|
'products' => 'Produkty',
|
|
'summary' => 'Podsumowanie',
|
|
'full_details' => 'Pelne szczegoly',
|
|
'close' => 'Zamknij',
|
|
'loading' => 'Ladowanie...',
|
|
'copy_tooltip' => 'Kopiuj',
|
|
],
|
|
```
|
|
|
|
Po zmianach: `npm run build:css`
|
|
|
|
Avoid: NIE trzymac styli w pliku widoku — SCSS w osobnym pliku
|
|
</action>
|
|
<verify>
|
|
1. SCSS buduje sie bez bledow
|
|
2. Modal ma poprawne style — centrowany, zaokraglony, z cieniem
|
|
3. Ikony kopiowania sa widoczne i reaguja na hover
|
|
4. Tlumaczenia dostepne w pl.php
|
|
</verify>
|
|
<done>AC-1 (style), AC-2 (style), AC-3 (style) satisfied</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<boundaries>
|
|
|
|
## DO NOT CHANGE
|
|
- resources/views/components/table-list.php (komponent reuzywany — nie modyfikowac)
|
|
- src/Modules/Orders/OrdersRepository.php (findDetails juz istnieje — uzyc bez zmian)
|
|
- resources/views/orders/show.php (strona szczegolow — bez zmian)
|
|
- resources/views/orders/partials/email-send-modal.php (istniejacy modal — bez zmian)
|
|
|
|
## SCOPE LIMITS
|
|
- Nie dodajemy edycji z popupu — tylko podglad i kopiowanie
|
|
- Nie dodajemy nowych zaleznosci npm/composer
|
|
- Nie modyfikujemy OrdersRepository — uzywamy istniejacego findDetails()
|
|
- Popup nie obsluguje akcji (zmiana statusu, platnosc itp.)
|
|
|
|
</boundaries>
|
|
|
|
<verification>
|
|
Before declaring plan complete:
|
|
- [ ] Ikona oka widoczna w kolumnie nr zamowienia na /orders/list
|
|
- [ ] Klikniecie ikony otwiera modal z danymi zamowienia (AJAX fetch)
|
|
- [ ] Dane w modalu: kupujacy, adres dostawy, produkty, podsumowanie
|
|
- [ ] Ikony kopiowania dzialaja (clipboard API)
|
|
- [ ] Zamykanie: X, backdrop, Escape
|
|
- [ ] "Pelne szczegoly" prowadzi do /orders/{id}
|
|
- [ ] SCSS zbudowane do CSS
|
|
- [ ] Brak bledow w konsoli przegladarki
|
|
- [ ] All acceptance criteria met
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- All tasks completed
|
|
- All verification checks pass
|
|
- No errors or warnings introduced
|
|
- Uzytkownik moze szybko podejrzec i skopiowac dane zamowienia z listy
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.paul/phases/94-order-preview-popup/94-01-SUMMARY.md`
|
|
</output>
|