diff --git a/.paul/HANDOFF-2026-03-16.md b/.paul/HANDOFF-2026-03-16.md new file mode 100644 index 0000000..df45811 --- /dev/null +++ b/.paul/HANDOFF-2026-03-16.md @@ -0,0 +1,91 @@ +# PAUL Handoff + +**Date:** 2026-03-16 +**Status:** paused — checkpoint human-verify in progress + +--- + +## READ THIS FIRST + +You have no prior context. This document tells you everything. + +**Project:** orderPRO — aplikacja do zarządzania zamówieniami z wielu platform +**Core value:** Sprzedawca może obsługiwać zamówienia ze wszystkich kanałów sprzedaży i nadawać przesyłki bez przełączania się między platformami. + +--- + +## Current State + +**Milestone:** v0.4 Moduł E-mail +**Phase:** 14 of 3 (in milestone) — Szablony wiadomości e-mail +**Plan:** 14-01 — APPLY in progress (checkpoint human-verify) + +**Loop Position:** +``` +PLAN ──▶ APPLY ──▶ UNIFY + ✓ ◐ ○ [APPLY in progress — Task 1+2 done, Task 3 checkpoint awaiting approval] +``` + +--- + +## What Was Done + +- Task 1: Created `EmailTemplateRepository` + `EmailTemplateController` + routes (6 endpoints) +- Task 2: Created view `email-templates.php` with Quill.js CDN editor, variable panel, preview modal, AJAX toggle +- Added sidebar link "Szablony e-mail" in `layouts/app.php` +- Compiled SCSS (modal-overlay, email-tpl-editor styles) +- Fixed 2 bugs discovered during deploy: + - `AuthService` namespace: `App\Core\Auth\AuthService` → `App\Modules\Auth\AuthService` + - `Flash` namespace: `App\Core\Session\Flash` → `App\Core\Support\Flash` + +--- + +## What's In Progress + +- **Task 3 checkpoint:human-verify** — user was testing the deployed page when session paused +- User reported 2 namespace errors which were fixed, needs to re-test + +--- + +## What's Next + +**Immediate:** User re-tests `/settings/email-templates` after namespace fixes. If approved → APPLY complete. + +**After that:** Run `sonar-scanner` (required skill), then `/paul:unify .paul/phases/14-email-templates/14-01-PLAN.md` + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `.paul/STATE.md` | Live project state | +| `.paul/ROADMAP.md` | Phase overview | +| `.paul/phases/14-email-templates/14-01-PLAN.md` | Current plan | +| `src/Modules/Settings/EmailTemplateController.php` | Controller (CRUD + preview + variables) | +| `src/Modules/Settings/EmailTemplateRepository.php` | Repository (DB operations) | +| `resources/views/settings/email-templates.php` | View (list + form + Quill.js + variable panel) | +| `routes/web.php` | Routes (6 new endpoints) | +| `resources/views/layouts/app.php` | Sidebar link added | +| `resources/scss/app.scss` | Styles (modal-overlay, email-tpl-editor) | + +--- + +## Namespace Fixes Applied + +These were wrong in the initial controller and have been corrected: +- `use App\Core\Auth\AuthService` → `use App\Modules\Auth\AuthService` +- `use App\Core\Session\Flash` → `use App\Core\Support\Flash` + +--- + +## Resume Instructions + +1. Read `.paul/STATE.md` for latest position +2. Check loop position — APPLY in progress, Task 3 checkpoint +3. Run `/paul:resume` or ask user to re-test `/settings/email-templates` +4. On approval → finalize APPLY → sonar-scanner → `/paul:unify` + +--- + +*Handoff created: 2026-03-16* diff --git a/.paul/ROADMAP.md b/.paul/ROADMAP.md index 5819821..d6d812e 100644 --- a/.paul/ROADMAP.md +++ b/.paul/ROADMAP.md @@ -13,7 +13,7 @@ Skrzynki pocztowe SMTP, szablony wiadomości z systemem zmiennych (Quill.js), wy | Phase | Name | Plans | Status | |-------|------|-------|--------| | 13 | DB + Skrzynki pocztowe | 1/1 | Complete ✓ | -| 14 | Szablony wiadomości | TBD | Not started | +| 14 | Szablony wiadomości | 0/1 | Planning | | 15 | Wysyłka e-mail z zamówień | TBD | Not started | ## Completed Milestones diff --git a/.paul/STATE.md b/.paul/STATE.md index 7f5a982..b1c935b 100644 --- a/.paul/STATE.md +++ b/.paul/STATE.md @@ -10,10 +10,10 @@ See: .paul/PROJECT.md (updated 2026-03-12) ## Current Position Milestone: v0.4 Moduł E-mail -Phase: [1] of [3] (DB + Skrzynki pocztowe) — Complete -Plan: 13-01 complete -Status: Phase 13 complete, ready for Phase 14 -Last activity: 2026-03-15 — UNIFY 13-01 complete, phase transition done +Phase: [2] of [3] (Szablony wiadomości) — Planning +Plan: 14-01 created, awaiting approval +Status: PLAN created, ready for APPLY +Last activity: 2026-03-16 — Created .paul/phases/14-email-templates/14-01-PLAN.md Progress: - v0.1 Initial Release: [██████████] 100% ✓ @@ -21,7 +21,7 @@ Progress: - v0.3 Moduł Paragonów: [██████████] 100% ✓ - v0.4 Moduł E-mail: [███░░░░░░░] 33% - Phase 13: [██████████] 100% ✓ - - Phase 14: [░░░░░░░░░░] 0% + - Phase 14: [░░░░░░░░░░] 0% ← planning - Phase 15: [░░░░░░░░░░] 0% ## Loop Position @@ -29,7 +29,7 @@ Progress: Current loop state: ``` PLAN ──▶ APPLY ──▶ UNIFY - ✓ ✓ ✓ [Loop complete — Phase 13 done, ready for Phase 14] + ✓ ◐ ○ [APPLY in progress — Task 3 checkpoint awaiting approval] ``` ## Accumulated Context @@ -147,7 +147,7 @@ PLAN ──▶ APPLY ──▶ UNIFY - **Delivery mapping "Szukaj..." layout** — JS `attachSelectFilter()` w allegro.php tworzy input search dla InPost/Apaczka selectów, wizualnie wygląda jakby należał do wiersza powyżej. Pre-existing bug, do naprawy osobno. ### Git State -Last commit: 22fc330 (feat(11-12-accounting): phases 11-12 complete — milestone v0.3 done) +Last commit: 3223aac (feat(13-email-mailboxes): phase 13 complete — email DB foundation + SMTP mailbox CRUD) Branch: main Feature branches merged: none @@ -156,16 +156,17 @@ Brak. ## Session Continuity -Last session: 2026-03-15 -Stopped at: Phase 13 complete -Next action: /paul:plan for Phase 14 (Szablony wiadomości) -Resume file: .paul/phases/13-email-mailboxes/13-01-SUMMARY.md +Last session: 2026-03-16 +Stopped at: APPLY 14-01 in progress — Task 3 checkpoint human-verify (2 namespace bugs fixed, awaiting re-test) +Next action: User re-tests /settings/email-templates → if approved → finalize APPLY → sonar-scanner → /paul:unify +Resume file: .paul/HANDOFF-2026-03-16.md Resume context: - v0.1: COMPLETE ✓ (6 phases, 15 plans) - v0.2: COMPLETE ✓ (1 phase, 5 plans) - v0.3: COMPLETE ✓ (5 phases, 5 plans) — Moduł Paragonów -- v0.4: IN PROGRESS — Phase 13 complete, Phase 14 next +- v0.4: IN PROGRESS — Phase 13 complete, Phase 14 APPLY checkpoint - Faza 0 (nieaktywne przyciski) zrobiona poza planem +- 2 namespace fixes applied: AuthService, Flash --- *STATE.md — Updated after every significant action* diff --git a/.paul/phases/14-email-templates/14-01-PLAN.md b/.paul/phases/14-email-templates/14-01-PLAN.md new file mode 100644 index 0000000..6a00f4d --- /dev/null +++ b/.paul/phases/14-email-templates/14-01-PLAN.md @@ -0,0 +1,319 @@ +--- +phase: 14-email-templates +plan: 01 +type: execute +wave: 1 +depends_on: ["13-01"] +files_modified: + - src/Modules/Settings/EmailTemplateController.php + - src/Modules/Settings/EmailTemplateRepository.php + - resources/views/settings/email-templates.php + - resources/views/settings/email-template-form.php + - routes/web.php + - resources/views/layouts/app.php + - resources/scss/app.scss + - public/assets/css/app.css +autonomous: false +--- + + +## Goal +CRUD szablonów wiadomości e-mail z edytorem rich-text (Quill.js) i systemem zmiennych do personalizacji treści (np. {{order.number}}, {{buyer.name}}). + +## Purpose +Użytkownik musi mieć możliwość tworzenia i zarządzania szablonami e-mail, które w fazie 15 będą używane do wysyłki wiadomości z poziomu zamówień. Szablony muszą obsługiwać zmienne dynamiczne rozwiązywane z danych zamówienia. + +## Output +- EmailTemplateController + EmailTemplateRepository (CRUD) +- Widok listy szablonów + formularz edycji z Quill.js +- System zmiennych: definicja dostępnych zmiennych, wstawianie z UI, podgląd +- Link w sidebarze Settings + + + +## Project Context +@.paul/PROJECT.md +@.paul/ROADMAP.md +@.paul/STATE.md + +## Prior Work +@.paul/phases/13-email-mailboxes/13-01-SUMMARY.md +- email_templates table already created (Phase 13) +- EmailMailboxRepository::listActive() ready for mailbox select +- Pattern: EmailMailboxController CRUD (same structure to follow) + +## Source Files +@database/migrations/20260315_000055_create_email_templates_table.sql +@src/Modules/Settings/EmailMailboxController.php +@src/Modules/Settings/EmailMailboxRepository.php +@resources/views/settings/email-mailboxes.php +@routes/web.php +@resources/views/layouts/app.php + + + +## Required Skills (from SPECIAL-FLOWS.md) + +| Skill | Priority | When to Invoke | Loaded? | +|-------|----------|----------------|---------| +| sonar-scanner | required | Po APPLY, przed UNIFY | ○ | + +## Skill Invocation Checklist +- [ ] sonar-scanner uruchomiony po APPLY + + + + +## AC-1: Lista szablonów +```gherkin +Given użytkownik jest zalogowany i przechodzi do Ustawienia > Szablony e-mail +When strona się ładuje +Then widzi tabelę z kolumnami: Nazwa, Temat, Skrzynka, Status, Akcje +And każdy szablon ma przyciski: Edytuj, Włącz/Wyłącz, Usuń +``` + +## AC-2: Tworzenie i edycja szablonu +```gherkin +Given użytkownik jest na stronie szablonów e-mail +When wypełnia formularz (nazwa, temat, skrzynka, treść HTML) i zapisuje +Then szablon jest zapisany w bazie z poprawnym body_html z Quill.js +And po edycji istniejącego szablonu treść Quill jest załadowana poprawnie +``` + +## AC-3: Edytor Quill.js z toolbarem +```gherkin +Given użytkownik edytuje treść szablonu +When korzysta z edytora +Then dostępne są: bold, italic, underline, listy, linki, nagłówki, kolory, wyrównanie +And treść jest zapisywana jako HTML w polu body_html +``` + +## AC-4: System zmiennych — wstawianie +```gherkin +Given użytkownik edytuje szablon +When klika przycisk "Wstaw zmienną" i wybiera zmienną z listy (np. {{zamowienie.numer}}) +Then zmienna jest wstawiana do treści edytora w miejscu kursora +And zmienne są wizualnie wyróżnione w edytorze (np. badge/tag) +``` + +## AC-5: System zmiennych — definicja +```gherkin +Given system zmiennych jest zaimplementowany +When użytkownik otwiera listę zmiennych +Then widzi pogrupowane zmienne: + - Zamówienie: numer, źródło, kwota, waluta, data + - Kupujący: imię i nazwisko, email, telefon, login + - Adres dostawy: ulica, miasto, kod pocztowy, kraj + - Firma: nazwa, NIP (jeśli faktura) +And każda zmienna ma opis i placeholder (np. {{zamowienie.numer}}) +``` + +## AC-6: Toggle statusu i usuwanie +```gherkin +Given szablon istnieje na liście +When użytkownik klika Włącz/Wyłącz +Then status is_active się zmienia (toggle AJAX) +When użytkownik klika Usuń i potwierdzi (OrderProAlerts.confirm) +Then szablon jest usunięty z bazy +``` + +## AC-7: Podgląd szablonu z przykładowymi danymi +```gherkin +Given użytkownik edytuje szablon z zmiennymi +When klika przycisk "Podgląd" +Then widzi modal z treścią szablonu gdzie zmienne są zastąpione przykładowymi danymi +And temat wiadomości jest również rozwiązany z przykładowymi danymi +``` + + + + + + + Task 1: EmailTemplateRepository + EmailTemplateController + routes + + src/Modules/Settings/EmailTemplateRepository.php, + src/Modules/Settings/EmailTemplateController.php, + routes/web.php + + + **EmailTemplateRepository** (wzorowany na EmailMailboxRepository): + - listAll(): array — pobiera wszystkie szablony JOIN email_mailboxes (nazwa skrzynki), ORDER BY name + - findById(int $id): ?array — jeden szablon po ID + - save(array $data): void — INSERT lub UPDATE (id > 0 = update) + - delete(int $id): void — DELETE po ID + - toggleStatus(int $id): void — toggle is_active (UPDATE SET is_active = NOT is_active) + - listActive(): array — szablony z is_active=1 (do użycia w fazie 15) + - Medoo + prepared statements, bez surowego SQL + + **EmailTemplateController** (wzorowany na EmailMailboxController): + - __construct(TemplateEngine, Translator, Auth, EmailTemplateRepository, EmailMailboxRepository) + - index(Request): Response — lista szablonów + formularz (edit mode jeśli ?edit=ID) + - save(Request): Response — walidacja (name, subject, body_html required) + CSRF + zapis + redirect z Flash + - delete(Request): Response — CSRF + usunięcie + redirect z Flash + - toggleStatus(Request): Response — AJAX POST, JSON response {success: true} + - preview(Request): Response — AJAX POST, przyjmuje subject + body_html, zwraca JSON z resolved variables (przykładowe dane) + - getVariables(Request): Response — AJAX GET, zwraca JSON z definicją dostępnych zmiennych pogrupowanych + + **Definicja zmiennych** (statyczna metoda lub stała w kontrolerze): + ``` + Zamówienie: + {{zamowienie.numer}} — Numer wewnętrzny (OP...) + {{zamowienie.numer_zewnetrzny}} — Numer z platformy + {{zamowienie.zrodlo}} — Źródło (Allegro/shopPRO/...) + {{zamowienie.kwota}} — Kwota brutto + {{zamowienie.waluta}} — Waluta (PLN/EUR/...) + {{zamowienie.data}} — Data zamówienia + Kupujący: + {{kupujacy.imie_nazwisko}} — Imię i nazwisko + {{kupujacy.email}} — Adres e-mail + {{kupujacy.telefon}} — Telefon + {{kupujacy.login}} — Login platformy + Adres dostawy: + {{adres.ulica}} — Ulica z numerem + {{adres.miasto}} — Miasto + {{adres.kod_pocztowy}} — Kod pocztowy + {{adres.kraj}} — Kraj + Firma: + {{firma.nazwa}} — Nazwa firmy + {{firma.nip}} — NIP + ``` + + **Metoda resolveVariables(string $text, array $sampleData): string** + - Zamienia {{klucz.pole}} na wartości z $sampleData + - Dla podglądu: używa przykładowych (hardcoded) danych + - Dla wysyłki (faza 15): otrzyma prawdziwe dane zamówienia + + **Routes** (dodać w web.php obok email-mailboxes): + - GET /settings/email-templates → index + - POST /settings/email-templates/save → save + - POST /settings/email-templates/delete → delete + - POST /settings/email-templates/toggle → toggleStatus + - POST /settings/email-templates/preview → preview (AJAX) + - GET /settings/email-templates/variables → getVariables (AJAX) + + + - Klasy się ładują bez Fatal Error (PHP syntax check) + - Routes zarejestrowane poprawnie + - Strona /settings/email-templates renderuje się bez błędów + + AC-1, AC-2, AC-5, AC-6, AC-7 backend satisfied + + + + Task 2: Widoki — lista szablonów + formularz z Quill.js + system zmiennych UI + + resources/views/settings/email-templates.php, + resources/views/settings/email-template-form.php, + resources/views/layouts/app.php, + resources/scss/app.scss, + public/assets/css/app.css + + + **Widok listy** (resources/views/settings/email-templates.php): + - Tabela: Nazwa, Temat, Skrzynka (nazwa lub "—"), Status (badge), Akcje + - Przycisk "Edytuj" → ?edit=ID + - Toggle status: AJAX POST do /settings/email-templates/toggle (jak w mailboxes) + - Usuwanie: window.OrderProAlerts.confirm() → POST /settings/email-templates/delete + - Flash messages (success/error) + - Przycisk "Nowy szablon" → przejście do formularza + + **Formularz edycji** (osobny plik lub sekcja w tym samym widoku): + - Pola: nazwa (input), temat (input — z możliwością wpisania zmiennych ręcznie), skrzynka (select z listActive), status (checkbox) + - Edytor Quill.js: + - Załadować z CDN: quill@2 (https://cdn.quilljs.com/2.0.3/quill.snow.css + quill.js) + - Toolbar: bold, italic, underline, strike, headers (h1-h3), lists (ordered/bullet), link, color, background, align + - Hidden input #body_html synchronizowany z Quill content on form submit + - Panel zmiennych (sidebar lub dropdown): + - Przycisk "Wstaw zmienną" otwiera listę pogrupowaną (pobrana AJAX z /variables) + - Klik na zmienną → wstawia {{zmienna.pole}} w miejsce kursora w Quill + - Przycisk "Podgląd": + - AJAX POST do /settings/email-templates/preview z subject + body_html + - Wynik w modalu (resolved subject + resolved body_html) + - Przycisk "Zapisz" → submit formularza + + **Sidebar** (resources/views/layouts/app.php): + - Dodać link "Szablony e-mail" pod "Skrzynki pocztowe" w sekcji Settings + - activeSettings === 'email-templates' + + **Style** (resources/scss/app.scss → public/assets/css/app.css): + - Styl dla kontenera Quill (.ql-editor min-height: 200px) + - Styl dla panelu zmiennych (.email-var-list, .email-var-group, .email-var-item) + - Styl dla modala podglądu (reuse istniejący .modal jeśli jest, lub prosty modal) + - Kompaktowy layout zgodnie z zasadami projektu + + **Nie używać** natywnych alert()/confirm() — tylko OrderProAlerts. + **Quill CDN** — nie dodawać jako zależność npm/composer, wystarczy CDN w widoku. + + + - Quill.js ładuje się i renderuje edytor + - Formularz zapisuje poprawnie (body_html zawiera HTML z Quill) + - Zmienne wstawiane w edytor poprawnie + - Podgląd wyświetla resolved template + - SCSS skompilowane do CSS + + AC-1, AC-2, AC-3, AC-4, AC-5, AC-6, AC-7 UI satisfied + + + + CRUD szablonów e-mail z Quill.js i systemem zmiennych + + 1. Otwórz: /settings/email-templates + 2. Sprawdź: link "Szablony e-mail" widoczny w sidebarze + 3. Kliknij "Nowy szablon": + a. Wypełnij nazwę, temat (np. "Potwierdzenie zamówienia {{zamowienie.numer}}") + b. Wybierz skrzynkę z listy + c. W Quill wpisz treść, użyj formatowania (bold, listy) + d. Kliknij "Wstaw zmienną" → wybierz np. {{kupujacy.imie_nazwisko}} + e. Kliknij "Podgląd" → sprawdź czy zmienne zamienione na przykładowe dane + f. Zapisz szablon + 4. Na liście: sprawdź czy szablon się pojawił z poprawną nazwą/tematem/skrzynką + 5. Edytuj szablon — sprawdź czy Quill załadował treść HTML poprawnie + 6. Toggle status (włącz/wyłącz) — badge powinien się zmienić + 7. Usuń szablon — potwierdzenie OrderProAlerts, usunięcie z listy + + Type "approved" to continue, or describe issues to fix + + + + + + +## DO NOT CHANGE +- database/migrations/* (tabela email_templates już istnieje z fazy 13) +- src/Modules/Settings/EmailMailboxController.php (tylko import/reuse) +- src/Modules/Settings/EmailMailboxRepository.php (tylko wywołanie listActive) + +## SCOPE LIMITS +- Nie implementować wysyłki e-mail (faza 15) +- Nie dodawać PHPMailer/SwiftMailer (faza 15) +- Nie modyfikować tabeli email_templates (schemat zamrożony) +- Quill.js z CDN — bez npm/bundlera +- Zmienne rozwiązywane tylko z przykładowymi danymi (prawdziwe dane zamówienia w fazie 15) + + + + +Before declaring plan complete: +- [ ] /settings/email-templates renderuje się bez błędów PHP +- [ ] CRUD: create, read, update, delete szablonów działa +- [ ] Quill.js edytor ładuje się i zapisuje HTML +- [ ] System zmiennych: lista, wstawianie, podgląd z przykładowymi danymi +- [ ] Toggle statusu via AJAX działa +- [ ] Usuwanie z potwierdzeniem OrderProAlerts działa +- [ ] Link w sidebarze Settings aktywny +- [ ] SCSS skompilowane, brak inline styles w widokach +- [ ] Brak natywnych alert()/confirm() +- [ ] CSRF walidowany we wszystkich POST + + + +- Wszystkie AC-1 do AC-7 spełnione +- Wszystkie taski zakończone +- Checkpoint human-verify approved +- Brak błędów PHP, brak broken UI + + + +After completion, create `.paul/phases/14-email-templates/14-01-SUMMARY.md` + diff --git a/public/assets/css/app.css b/public/assets/css/app.css index 3624162..5d187f8 100644 --- a/public/assets/css/app.css +++ b/public/assets/css/app.css @@ -1 +1 @@ -:root{--c-primary: #6690f4;--c-primary-dark: #3164db;--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);--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-primary)}.btn--primary:hover{background:var(--c-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);border-color:var(--c-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{padding:12px 14px;border-radius:8px;border:1px solid rgba(0,0,0,0);font-size:13px;min-height:44px}.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}.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%}}*{box-sizing:border-box}html,body{min-height:100%}body{margin:0;font-family:"Roboto","Segoe UI",sans-serif;font-size:13px;color:var(--c-text);background:var(--c-bg)}a{color:var(--c-primary)}.app-shell{min-height:100vh;display:flex}.sidebar{width:260px;min-width:260px;flex-shrink:0;overflow:hidden;transition:width .22s ease,min-width .22s ease;border-right:1px solid #243041;background:#111a28;padding:18px 10px;display:flex;flex-direction:column}.sidebar.is-collapsed{width:52px;min-width:52px}.sidebar__brand{display:flex;align-items:center;justify-content:space-between;margin:4px 4px 16px;gap:6px;min-width:0}.sidebar__brand-text{color:#e9f0ff;font-size:24px;font-weight:300;letter-spacing:-0.02em;white-space:nowrap;overflow:hidden;flex:1;min-width:0}.sidebar__brand-text strong{font-weight:700}.sidebar__collapse-btn{flex-shrink:0;width:28px;height:28px;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0);border:1px solid #2a3a54;border-radius:6px;color:#64748b;cursor:pointer;padding:0;transition:background .15s,color .15s}.sidebar__collapse-btn:hover{background:#1b2a3f;color:#cbd5e1}.sidebar__collapse-icon{display:block;transition:transform .22s ease;flex-shrink:0}.sidebar.is-collapsed .sidebar__collapse-icon{transform:rotate(180deg)}.sidebar__nav{display:grid;gap:4px}.sidebar__link{border-radius:8px;padding:10px 12px;text-decoration:none;color:#cbd5e1;font-weight:600}.sidebar__link:hover{color:#f8fafc;background:#1b2a3f}.sidebar__link.is-active{color:#fff;background:#2e4f93}.sidebar__group{display:grid;gap:2px}.sidebar__group-toggle{list-style:none;border-radius:8px;padding:9px 10px;color:#cbd5e1;font-weight:600;cursor:pointer;display:flex;align-items:center;gap:9px;white-space:nowrap;user-select:none}.sidebar__group-toggle::-webkit-details-marker{display:none}.sidebar__group:hover .sidebar__group-toggle,.sidebar__group-toggle:hover{color:#f8fafc;background:#1b2a3f}.sidebar__group.is-active .sidebar__group-toggle{color:#fff;background:#2e4f93}.sidebar__icon{flex-shrink:0;width:18px;height:18px;display:flex;align-items:center;justify-content:center;opacity:.85}.sidebar__label{flex:1;min-width:0;overflow:hidden}.sidebar__toggle-arrow{flex-shrink:0;margin-left:auto;opacity:.5;transition:transform .18s ease}details[open]>.sidebar__group-toggle .sidebar__toggle-arrow{transform:rotate(180deg)}.sidebar__group-links{display:grid;gap:2px;padding-left:12px;overflow:hidden}.sidebar__sublink{border-radius:6px;padding:7px 10px 7px 8px;text-decoration:none;color:#94a3b8;font-size:12.5px;font-weight:500;display:flex;align-items:center;gap:8px;white-space:nowrap}.sidebar__sublink::before{content:"";flex-shrink:0;width:5px;height:5px;border-radius:50%;background:rgba(148,163,184,.3);transition:background .15s}.sidebar__sublink:hover{color:#e2e8f0;background:#1b2a3f}.sidebar__sublink:hover::before{background:rgba(148,163,184,.65)}.sidebar__sublink.is-active{color:#fff;background:rgba(46,79,147,.55)}.sidebar__sublink.is-active::before{background:#93c5fd}.app-main{flex:1;min-width:0}.topbar{height:50px;border-bottom:1px solid var(--c-border);background:var(--c-surface);display:flex;align-items:center;justify-content:space-between;padding:0 20px;position:sticky;top:0;z-index:100}.brand{font-size:22px;font-weight:300;letter-spacing:-0.02em;color:var(--c-text-strong)}.brand strong{font-weight:700}.container{max-width:none;width:calc(100% - 20px);margin:12px 10px;padding:0 4px 14px}.card{background:var(--c-surface);border-radius:10px;box-shadow:var(--shadow-card);padding:14px}.card h1{margin:0 0 10px;color:var(--c-text-strong);font-size:24px;font-weight:700}.muted{color:var(--c-muted)}.accent{color:var(--c-primary);font-weight:600}.users-form{display:grid;gap:14px;max-width:460px}.form-field{margin-bottom:12px}.section-title{margin:0;color:var(--c-text-strong);font-size:18px;font-weight:700}h2.section-title,h3.section-title,h4.section-title{display:flex;align-items:center;gap:6px;font-weight:600;padding:6px 0;border-bottom:1px solid #e2e8f0;color:var(--c-primary, #2563eb)}h2.section-title::before,h3.section-title::before,h4.section-title::before{content:"■";font-size:.55em;opacity:.5}h3.section-title,h4.section-title{font-size:15px}h3.section-title::before,h4.section-title::before{content:"◆";font-size:.5em}.mt-0{margin-top:0}.mt-4{margin-top:4px}.mt-12{margin-top:8px}.mt-16{margin-top:12px}.settings-grid{display:grid;grid-template-columns:repeat(3, minmax(0, 1fr));gap:12px}.settings-nav{display:flex;gap:8px;flex-wrap:wrap}.settings-nav__link{text-decoration:none;border:1px solid var(--c-border);border-radius:8px;padding:8px 12px;color:var(--c-text-strong);font-weight:600}.settings-nav__link:hover{background:#f8fafc}.settings-nav__link.is-active{border-color:var(--c-primary);color:var(--c-primary);background:#edf2ff}.settings-stat{border:1px solid var(--c-border);border-radius:8px;padding:12px;background:#f8fafc}.settings-stat__label{display:block;color:var(--c-muted);font-size:12px;margin-bottom:4px}.settings-stat__value{color:var(--c-text-strong);font-size:20px}.settings-logs{margin:0;padding:12px;border-radius:8px;border:1px solid var(--c-border);background:#0b1220;color:#d1d5db;font-size:12px;line-height:1.5;overflow:auto}.settings-allegro-callback{display:block;width:100%;padding:8px 10px;border:1px solid var(--c-border);border-radius:8px;background:#f8fafc;color:var(--c-text-strong);font-size:12px;line-height:1.45;word-break:break-all}.page-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.filters-grid{display:grid;grid-template-columns:repeat(3, minmax(0, 1fr));gap:12px}.filters-actions{display:flex;align-items:center;gap:8px}.product-form .form-control{width:100%}.form-grid{display:grid;grid-template-columns:repeat(2, minmax(0, 1fr));gap:12px}.form-grid-2{display:grid;grid-template-columns:repeat(2, minmax(0, 1fr));gap:12px;align-items:start}.form-grid-3{display:grid;grid-template-columns:repeat(3, minmax(0, 1fr));gap:12px;align-items:start}.form-grid-4{display:grid;grid-template-columns:repeat(4, minmax(0, 1fr));gap:12px;align-items:start}.form-actions{display:flex;gap:8px;flex-wrap:wrap;align-items:flex-start}.form-actions .btn{align-self:flex-start}.statuses-form{display:grid;gap:8px;grid-template-columns:repeat(2, minmax(0, 1fr))}.statuses-form .form-actions{grid-column:1/-1}.statuses-color-input{min-height:32px;padding:2px}.statuses-hint{grid-column:1/-1;margin:0}.statuses-group-block{border:1px solid var(--c-border);border-radius:10px;padding:8px;background:#fbfdff}.statuses-group-block__head{display:flex;align-items:center;justify-content:space-between;gap:6px;flex-wrap:wrap}.statuses-group-block__title{margin:0;display:inline-flex;align-items:center;gap:6px;color:var(--c-text-strong);font-size:14px}.statuses-color-dot{width:12px;height:12px;border-radius:999px;border:1px solid rgba(15,23,42,.15)}.statuses-dnd-list{margin:6px 0 0;padding:0;list-style:none;display:grid;gap:6px}.statuses-dnd-item{display:grid;grid-template-columns:24px 1fr;gap:6px;border:1px solid #dce4f0;border-radius:8px;background:#fff;padding:6px}.statuses-dnd-item__content{display:flex;align-items:center;gap:6px;min-width:0}.statuses-dnd-item.is-dragging{opacity:.6}.statuses-dnd-item__drag{display:inline-flex;align-items:center;justify-content:center;border:1px dashed #cbd5e1;border-radius:6px;color:#64748b;cursor:grab;user-select:none;font-weight:700;font-size:12px}.statuses-dnd-item__drag:active{cursor:grabbing}.statuses-inline-form{display:grid;gap:6px}.statuses-inline-form--row{grid-template-columns:minmax(180px, 1.4fr) minmax(150px, 1fr) auto auto auto;align-items:center;flex:1 1 auto;min-width:0}.statuses-inline-form--row-group{grid-template-columns:minmax(180px, 1.5fr) 56px auto auto auto;align-items:center;flex:1 1 auto;min-width:0}.statuses-inline-form--row .form-control,.statuses-inline-form--row-group .form-control{min-height:30px;padding:4px 8px}.statuses-inline-form--row .btn,.statuses-inline-form--row-group .btn,.statuses-inline-delete .btn{min-height:30px;padding:4px 10px;font-size:12px}.statuses-inline-check{margin-top:0;white-space:nowrap;font-size:12px}.statuses-inline-delete{margin:0;flex:0 0 auto}.statuses-code-label{font-size:12px;color:var(--c-muted)}.statuses-code-readonly{display:inline-flex;align-items:center;gap:6px;white-space:nowrap;font-size:12px}.statuses-code-readonly code{background:#eef2f7;border-radius:6px;padding:1px 6px;color:#1f2937;font-size:12px}.field-inline{display:flex;align-items:center;gap:8px;margin-top:2px}.modal-backdrop{position:fixed;inset:0;background:rgba(15,23,42,.5);display:flex;align-items:center;justify-content:center;padding:16px;z-index:200}.modal-backdrop[hidden]{display:none}.modal{width:min(560px,100%);background:#fff;border-radius:10px;box-shadow:0 20px 40px rgba(15,23,42,.35);overflow:hidden}.modal__header{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:16px 18px;border-bottom:1px solid var(--c-border)}.modal__header h3{margin:0;font-size:18px;color:var(--c-text-strong)}.modal__body{padding:16px 18px 18px}.status-pill{display:inline-flex;align-items:center;justify-content:center;border:1px solid #fed7d7;background:#fff5f5;color:#9b2c2c;padding:2px 8px;border-radius:999px;font-size:12px;font-weight:600}.status-pill.is-active{border-color:#b7ebcf;background:#f0fff6;color:#0f6b39}.table-row-actions{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap}.table-row-actions form{margin:0}.table-list{display:grid;gap:14px}.table-list__header{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap}.table-list__left{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}.table-list-header-actions{display:inline-flex;align-items:center;gap:10px;flex-wrap:wrap}.js-filter-toggle-btn.is-active{border-color:#cbd5e0;background:#edf2ff;color:var(--c-primary-dark)}.table-filter-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;font-size:11px;font-weight:700;color:#fff;background:var(--c-primary);border-radius:999px}.table-filters-wrapper{display:none}.table-filters-wrapper.is-open{display:block}.table-list-filters{display:grid;gap:12px;grid-template-columns:repeat(auto-fit, minmax(170px, 1fr));align-items:end}.table-col-toggle-wrapper{position:relative}.table-col-toggle-dropdown{display:none;position:absolute;right:0;top:calc(100% + 6px);z-index:30;width:260px;max-height:360px;overflow:auto;border:1px solid var(--c-border);border-radius:10px;background:#fff;box-shadow:0 10px 25px rgba(15,23,42,.12)}.table-col-toggle-dropdown.is-open{display:block}.table-col-toggle-header{padding:10px 12px;border-bottom:1px solid var(--c-border);font-size:12px;font-weight:700;color:var(--c-muted)}.table-col-toggle-item{display:flex;align-items:center;gap:10px;padding:8px 12px;font-size:13px;color:var(--c-text-strong)}.table-col-toggle-item:hover{background:#f8fafc}.table-col-toggle-footer{border-top:1px solid var(--c-border);padding:8px 12px}.table-col-hidden{display:none}.table-col-switch{position:relative;display:inline-block;width:34px;min-width:34px;height:18px}.table-col-switch input{opacity:0;width:0;height:0;position:absolute}.table-col-switch-slider{position:absolute;top:0;left:0;right:0;bottom:0;background:#cbd5e1;border-radius:999px;transition:background-color .2s ease}.table-col-switch-slider::before{content:"";position:absolute;height:14px;width:14px;left:2px;bottom:2px;background:#fff;border-radius:50%;transition:transform .2s ease}.table-col-switch input:checked+.table-col-switch-slider{background:#16a34a}.table-col-switch input:checked+.table-col-switch-slider::before{transform:translateX(16px)}.table-sort-link{display:inline-flex;align-items:center;gap:6px;color:var(--c-text-strong);text-decoration:none}.table-sort-link:hover{color:var(--c-primary-dark)}.table-sort-icon.is-muted{color:#a0aec0}.table-list__footer{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}.table-list-per-page-form{display:inline-flex;align-items:center;gap:8px}.table-list-per-page-form .form-control{min-width:90px}.table-select-col{width:44px;text-align:center}.table-select-toggle{display:inline-flex;align-items:center;justify-content:center}.table-select-toggle input[type=checkbox]{width:16px;height:16px}.orders-page .orders-head{background:linear-gradient(120deg, #f8fbff 0%, #eef5ff 100%);border:1px solid #dbe7fb}.orders-page .table-list{border:1px solid #dde5f2;border-radius:12px;box-shadow:0 6px 16px rgba(20,44,86,.08)}.orders-page .table-list__header{padding:10px 6px 2px}.orders-page .table-list-filters{padding:6px 6px 2px;border-top:1px solid #ebf0f7;border-bottom:1px solid #ebf0f7;background:#f9fbff}.orders-page .table-wrap{border-radius:10px;overflow:hidden;border:1px solid #e7edf6}.orders-page .table thead th{background:#f3f7fd;color:#30435f;font-size:12px;text-transform:uppercase;letter-spacing:.03em}.orders-page .table tbody td{vertical-align:middle;padding-top:10px;padding-bottom:10px;border-bottom-color:#edf2f8}.orders-page .table tbody tr:hover td{background:#f9fcff}.orders-list-page{padding:10px;margin-bottom:10px}.orders-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap}.orders-stats{display:inline-grid;grid-template-columns:repeat(3, minmax(86px, auto));gap:8px}.orders-stat{border:1px solid #d8e2f0;background:#f8fbff;border-radius:8px;padding:6px 8px;line-height:1.15}.orders-stat__label{display:block;color:#5f6f83;font-size:11px;margin-bottom:2px}.orders-stat__value{color:#12233a;font-size:16px;font-weight:700}.orders-ref{display:grid;gap:2px;min-width:170px}.orders-ref__main{font-weight:700;color:#0f1f35;font-size:14px}.orders-ref__meta{display:inline-flex;flex-wrap:wrap;gap:4px 10px;color:#64748b;font-size:12px}.orders-buyer{display:grid;gap:2px}.orders-buyer__name{color:#0f172a;font-weight:600;font-size:14px}.orders-buyer__meta{display:inline-flex;flex-wrap:wrap;gap:4px 10px;color:#64748b;font-size:12px}.orders-status-wrap{display:inline-flex;align-items:center;gap:5px;flex-wrap:wrap}.order-tag{display:inline-flex;align-items:center;justify-content:center;border:1px solid #d8e1ef;background:#f8fafc;color:#334155;border-radius:999px;padding:2px 8px;font-size:12px;font-weight:700;line-height:1.1}.order-tag.is-info{border-color:#bfdbfe;background:#eff6ff;color:#1d4ed8}.order-tag.is-success{border-color:#bbf7d0;background:#f0fdf4;color:#166534}.order-tag.is-danger{border-color:#fecaca;background:#fef2f2;color:#b91c1c}.order-tag.is-warn{border-color:#fde68a;background:#fffbeb;color:#92400e}.order-tag.is-cod{border-color:#f9a8d4;background:#fdf2f8;color:#9d174d}.order-tag.is-unpaid{border-color:#fca5a5;background:#fef2f2;color:#b91c1c}.orders-mini{font-size:14px;color:#223247;line-height:1.25}.orders-mini__delivery{font-size:12px;color:#64748b;margin-bottom:2px;max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.orders-products{display:grid;gap:4px;min-width:240px}.orders-products__meta,.orders-products__more{font-size:12px;color:#64748b}.orders-product{display:grid;grid-template-columns:48px 1fr;gap:6px;align-items:center}.orders-product__thumb{width:48px;height:48px;border-radius:4px;border:1px solid #dbe3ef;object-fit:cover;background:#fff}.orders-product__thumb--empty{display:inline-block;background:#eef2f7;border-style:dashed}.orders-product__txt{min-width:0;display:grid;gap:1px}.orders-product__name{font-size:14px;color:#0f172a;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.orders-product__qty{font-size:12px;color:#64748b}.orders-image-hover-wrap{position:relative;display:inline-flex;align-items:center;justify-content:center;cursor:zoom-in}.orders-image-hover-popup{display:none;position:fixed;left:auto;top:auto;width:350px;max-height:350px;object-fit:contain;border-radius:8px;background:#fff;box-shadow:0 8px 24px rgba(0,0,0,.18);border:1px solid #dfe3ea;z-index:100;pointer-events:none}.orders-image-hover-wrap:hover .orders-image-hover-popup{display:block}.activity-type-badge{display:inline-block;padding:2px 8px;border-radius:4px;font-size:12px;font-weight:500;white-space:nowrap;background:#e2e8f0;color:#334155}.activity-type-badge--status_change{background:#dbeafe;color:#1e40af}.activity-type-badge--payment{background:#dcfce7;color:#166534}.activity-type-badge--invoice{background:#fef3c7;color:#92400e}.activity-type-badge--shipment{background:#e0e7ff;color:#3730a3}.activity-type-badge--message{background:#f3e8ff;color:#6b21a8}.activity-type-badge--document{background:#fce7f3;color:#9d174d}.activity-type-badge--import{background:#f1f5f9;color:#475569}.activity-type-badge--note{background:#ecfdf5;color:#065f46}.text-nowrap{white-space:nowrap}.orders-money{display:grid;gap:2px}.orders-money__main{color:#0f172a;font-weight:700;font-size:14px}.orders-money__meta{color:#64748b;font-size:12px}.table-list[data-table-list-id=orders]{gap:8px}.table-list[data-table-list-id=orders] .table-list__header{padding:2px 0 0}.table-list[data-table-list-id=orders] .table-list-filters{gap:8px;grid-template-columns:repeat(auto-fit, minmax(150px, 1fr))}.table-list[data-table-list-id=orders] .table th,.table-list[data-table-list-id=orders] .table td{padding:6px 8px}.table-list[data-table-list-id=orders] .table thead th{font-size:12px;text-transform:uppercase;letter-spacing:.02em;white-space:nowrap}.table-list[data-table-list-id=orders] .table tbody td{vertical-align:top;font-size:14px;line-height:1.25}.order-show-layout{display:grid;grid-template-columns:220px minmax(0, 1fr);gap:12px;align-items:start}.order-statuses-side{position:sticky;top:60px;padding:10px}.order-statuses-side__title{font-size:13px;font-weight:700;color:#0f172a;margin-bottom:8px}.order-status-group{margin-bottom:10px}.order-status-group__name{font-size:12px;color:#475569;font-weight:700;margin-bottom:5px}.order-status-row{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:4px 6px;border-radius:6px;color:#334155;font-size:12px;text-decoration:none}.order-status-row__count{min-width:24px;text-align:center;border-radius:999px;background:var(--status-color, #64748b);padding:1px 6px;font-weight:700;font-size:11px;color:#fff}.order-status-row:hover{background:#f1f5f9}.order-status-row.is-active{background:rgba(15,23,42,.06);color:#0f172a;font-weight:700}.order-show-main{min-width:0}.order-details-actions{display:inline-flex;flex-wrap:wrap;justify-content:flex-end;gap:6px}.order-details-page{padding:12px}.order-details-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap}.order-back-link{color:#475569;text-decoration:none;font-weight:600}.order-back-link:hover{color:#1d4ed8}.order-details-sub{display:inline-flex;gap:10px;flex-wrap:wrap;color:#64748b;font-size:12px}.order-details-pill{border-radius:999px;padding:5px 10px;background:#eef6ff;border:1px solid #cfe2ff;color:#1d4ed8;font-size:12px;font-weight:700}.order-status-change{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.order-status-change__form{display:flex;align-items:center;gap:6px}.order-status-change__select{min-width:180px}.order-details-tabs{display:flex;gap:6px;flex-wrap:wrap}.order-details-tab{border:1px solid #d6deea;border-radius:8px;padding:5px 10px;color:#475569;font-size:12px;background:#f8fafc;cursor:pointer}.order-details-tab.is-active{border-color:#bfdbfe;color:#1d4ed8;background:#eff6ff;font-weight:700}.order-item-cell{display:grid;grid-template-columns:44px 1fr;gap:8px;align-items:center;min-width:260px}.order-item-thumb{width:44px;height:44px;border-radius:6px;border:1px solid #dbe3ef;object-fit:cover}.order-item-thumb--empty{display:inline-block;background:#eef2f7;border-style:dashed}.order-item-name{font-weight:600;color:#0f172a}.order-grid-2{display:grid;grid-template-columns:repeat(2, minmax(0, 1fr));gap:12px}.order-grid-3{display:grid;grid-template-columns:repeat(3, minmax(0, 1fr));gap:12px}.order-kv{margin:0;display:grid;grid-template-columns:150px 1fr;gap:6px 10px;font-size:12px}.payment-summary{display:grid;gap:6px;max-width:420px}.payment-summary__row{display:flex;align-items:center;gap:10px;font-size:12px}.payment-summary__label{width:150px;flex-shrink:0;color:#64748b}.payment-summary__value{font-weight:600;color:#0f172a}.order-kv dt{color:#64748b}.order-kv dd{margin:0;color:#0f172a;font-weight:600}.order-address{display:grid;gap:3px;font-size:12px;color:#0f172a}.order-events{display:grid;gap:8px}.order-event{border:1px solid #e2e8f0;border-radius:8px;padding:8px;background:#fbfdff}.order-event__head{color:#64748b;font-size:11px}.order-event__body{margin-top:4px;color:#0f172a;font-size:12px}.order-tab-panel{display:none}.order-tab-panel.is-active{display:block}.order-empty-placeholder{border:1px dashed #cbd5e1;border-radius:8px;min-height:180px;background:#f8fafc}.order-status-badge{display:inline-flex;align-items:center;justify-content:center;padding:4px 10px;border-radius:999px;font-size:12px;font-weight:700;border:1px solid #cbd5e1;color:#334155;background:#f8fafc}.order-status-badge.is-info{border-color:#bfdbfe;background:#eff6ff;color:#1d4ed8}.order-status-badge.is-success{border-color:#bbf7d0;background:#f0fdf4;color:#166534}.order-status-badge.is-danger{border-color:#fecaca;background:#fef2f2;color:#b91c1c}.order-status-badge.is-warn{border-color:#fde68a;background:#fffbeb;color:#92400e}.order-status-badge.is-empty{color:#94a3b8}.order-buyer{display:grid;gap:2px}.order-buyer__name{color:#0f172a;font-weight:600}.order-buyer__email{color:#64748b;font-size:12px}.table-inline-action{display:inline-block;margin-right:6px}.product-name-cell{display:inline-flex;align-items:center;gap:10px}.product-name-thumb{width:60px;height:60px;border-radius:6px;object-fit:cover;border:1px solid var(--c-border);background:#f8fafc}.product-name-thumb--empty{display:inline-block;width:60px;height:60px;border-radius:6px;border:1px dashed #cbd5e0;background:#f8fafc}.product-name-thumb-btn{border:0;padding:0;background:rgba(0,0,0,0);cursor:pointer;display:inline-flex;align-items:center;justify-content:center}.product-name-thumb-btn:focus-visible{outline:none;box-shadow:var(--focus-ring);border-radius:8px}.modal--image-preview{width:min(760px,100%)}.product-image-preview__img{display:block;width:100%;max-height:70vh;object-fit:contain;border-radius:8px;background:#f8fafc}.product-images-grid{display:grid;grid-template-columns:repeat(auto-fill, minmax(220px, 1fr));gap:12px}.product-image-card{border:1px solid #dfe3ea;border-radius:10px;padding:10px;background:#fff}.product-image-card__thumb-wrap{position:relative;border-radius:8px;overflow:hidden;background:#f2f5f8}.product-image-card__thumb{width:100%;height:160px;object-fit:cover;display:block}.product-image-card__thumb.is-empty{height:160px;display:grid;place-items:center;color:#6b7785;font-size:12px}.product-image-card__badge{display:none;position:absolute;top:8px;left:8px;background:#1f7a43;color:#fff;padding:3px 8px;border-radius:999px;font-size:11px}.product-image-card.is-main .product-image-card__badge{display:inline-block}.product-image-card__meta{margin-top:8px;font-size:11px;line-height:1.25;color:#5f6b79;overflow-wrap:anywhere}.product-image-card__actions{margin-top:10px;display:grid;grid-template-columns:1fr;gap:8px}.product-image-card__actions .btn{min-height:34px;font-size:12px;line-height:1.2;padding:6px 10px}.product-links-search-form{display:grid;gap:12px;grid-template-columns:minmax(220px, 320px) minmax(220px, 1fr) auto;align-items:end}.product-links-head{display:grid;gap:8px;grid-template-columns:repeat(3, minmax(0, 1fr))}.product-tabs-nav{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.product-links-inline-form{display:grid;gap:8px;grid-template-columns:minmax(140px, 1fr) minmax(140px, 1fr) auto;align-items:center}.product-links-actions-row{display:flex;align-items:center;gap:8px;flex-wrap:nowrap}.product-links-actions-row .product-links-relink-form{flex:1 1 auto}.product-links-unlink-form{margin:0;flex:0 0 auto}.product-link-status-cell{display:inline-flex;align-items:center;gap:6px}.product-link-alert-indicator{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:999px;border:1px solid #f59e0b;background:#fffbeb;color:#b45309;font-size:12px;font-weight:700;cursor:help}.product-link-events-list{margin:0;padding:0;list-style:none;display:grid;gap:4px}.product-link-events-list li{display:grid;gap:2px}.product-link-events-type{font-weight:600;color:var(--c-text-strong)}.product-link-events-date{color:var(--c-muted);font-size:12px}.product-show-images-grid{display:grid;grid-template-columns:repeat(auto-fill, minmax(220px, 1fr));gap:12px}.product-show-image-card{border:1px solid var(--c-border);border-radius:10px;background:#fff;padding:10px;overflow:hidden}.product-show-image-card__meta{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;min-width:0}.product-show-image-path{font-size:12px;min-width:0;overflow:hidden}.product-show-image-path summary{cursor:pointer;color:var(--c-muted, #888);list-style:none;user-select:none;white-space:nowrap}.product-show-image-path summary::-webkit-details-marker{display:none}.product-show-image-path summary::after{content:" ▾"}.product-show-image-path[open] summary::after{content:" ▴"}.product-show-image-path__url{margin-top:4px;word-break:break-all;overflow-wrap:break-word;font-size:11px}.product-show-image{width:100%;max-height:260px;object-fit:cover;border-radius:8px;border:1px solid #d9e0ea}.shipment-grid{display:grid;grid-template-columns:repeat(2, minmax(0, 1fr));gap:12px}.searchable-select{position:relative}.searchable-select__trigger{display:flex;align-items:center;justify-content:space-between;cursor:pointer;user-select:none;min-height:34px}.searchable-select__trigger::after{content:"";width:0;height:0;border-left:4px solid rgba(0,0,0,0);border-right:4px solid rgba(0,0,0,0);border-top:5px solid var(--c-text-muted, #6b7280);margin-left:8px;flex-shrink:0}.searchable-select__trigger--placeholder{color:var(--c-text-muted, #6b7280)}.searchable-select__dropdown{display:none;position:absolute;left:0;right:0;top:100%;z-index:50;max-height:280px;overflow:auto;background:#fff;border:1px solid var(--c-border);border-top:0;border-radius:0 0 8px 8px;box-shadow:0 8px 20px rgba(15,23,42,.12)}.searchable-select__dropdown.is-open{display:block}.searchable-select__search{position:sticky;top:0;border:none !important;border-bottom:1px solid var(--c-border) !important;border-radius:0 !important;box-shadow:none !important;font-size:13px;background:#fff;z-index:1}.searchable-select__option{padding:7px 10px;font-size:13px;cursor:pointer;color:var(--c-text-strong)}.searchable-select__option:hover{background:#f1f5f9}.searchable-select__option.is-selected{background:#edf2ff;font-weight:600}.flash{padding:10px 14px;border-radius:8px;font-size:13px;font-weight:500}.flash--success{background:#f0fdf4;border:1px solid #bbf7d0;color:#166534}.flash--error{background:#fef2f2;border:1px solid #fecaca;color:#b91c1c}.content-tabs-card{margin-top:0}.content-tabs-nav{display:flex;gap:4px;border-bottom:2px solid var(--c-border);margin-bottom:16px;flex-wrap:wrap}.content-tab-btn{padding:8px 16px;border:none;background:none;cursor:pointer;font-size:14px;font-weight:500;color:var(--c-text-muted, #6b7280);border-bottom:2px solid rgba(0,0,0,0);margin-bottom:-2px;border-radius:4px 4px 0 0;transition:color .15s,border-color .15s}.content-tab-btn:hover{color:var(--c-text-strong, #111827)}.content-tab-btn.is-active{color:var(--c-primary, #2563eb);border-bottom-color:var(--c-primary, #2563eb)}.content-tab-panel{display:none}.content-tab-panel.is-active{display:block}.shoppro-tabs-toolbar{display:flex;align-items:flex-end;justify-content:space-between;gap:10px;margin-bottom:10px;flex-wrap:wrap}.shoppro-tabs-toolbar__field{margin:0;min-width:260px;max-width:420px;flex:1 1 320px}.shoppro-tabs-toolbar__field .form-control{width:100%}.shoppro-tabs-toolbar__actions{display:inline-flex;align-items:center;gap:8px}.dm-carrier-select{min-width:140px}.dm-service-wrap{min-width:200px}.dm-service-wrap .dm-inpost-panel .form-control,.dm-service-wrap .dm-apaczka-panel .form-control{width:100%}.integration-settings-group{grid-column:1/-1;border:1px solid var(--c-border);border-radius:10px;background:#f8fbff;padding:10px}.integration-settings-group__head{margin-bottom:8px;padding:4px 0;border-bottom:1px solid #e2e8f0}.integration-settings-group__title{margin:0;font-size:14px;font-weight:600;letter-spacing:.01em;color:var(--c-text-strong, #1e293b)}.integration-settings-group__desc{margin:4px 0 0;color:#4b5563}.integration-settings-group__grid{display:grid;grid-template-columns:repeat(2, minmax(0, 1fr));gap:10px 12px;align-items:start}.integration-settings-group__full{grid-column:1/-1}.integration-settings-group__grid .form-field{margin:0;align-self:start}.integration-settings-group__grid .form-control{min-height:34px;height:34px}.integration-settings-group__grid input[type=date].form-control{line-height:1.2}.integration-settings-checkboxes{border:0;padding:0;margin:0}.integration-settings-checkboxes .field-label{display:block;margin-bottom:2px}.integration-settings-checkboxes__list{display:grid;grid-template-columns:repeat(2, minmax(0, 1fr));gap:6px 12px}.integration-settings-checkboxes__item{display:inline-flex;align-items:center;gap:6px;font-size:13px;color:#334155}@media(max-width: 768px){.app-shell{flex-direction:column}.sidebar{width:100% !important;min-width:0 !important;border-right:0;border-bottom:1px solid #243041;padding:14px;overflow-x:auto}.sidebar__brand{margin:0 0 10px;font-size:22px}.sidebar__collapse-btn{display:none}.sidebar__nav{display:flex;gap:8px;overflow-x:auto}.sidebar__link{white-space:nowrap}.topbar{padding:0 14px}.container{margin-top:16px;width:calc(100% - 16px);margin-left:8px;margin-right:8px;padding:0 3px 12px}.settings-grid{grid-template-columns:1fr}.page-head{flex-direction:column;align-items:flex-start}.orders-stats{grid-template-columns:1fr;width:100%}.order-show-layout{grid-template-columns:1fr}.order-statuses-side{position:static;top:auto}.order-details-actions{justify-content:flex-start}.order-grid-2,.order-grid-3{grid-template-columns:1fr}.order-kv{grid-template-columns:1fr;gap:2px}.filters-grid,.form-grid,.form-grid-2,.form-grid-3,.form-grid-4,.shipment-grid,.statuses-form,.statuses-inline-form,.table-list-filters,.product-links-search-form,.product-links-inline-form{grid-template-columns:1fr}.statuses-dnd-item__content{display:block}.statuses-inline-delete{margin-top:6px}.filters-actions{align-items:center}.table-list__header,.table-list__footer{align-items:flex-start}.product-links-head{grid-template-columns:1fr}.integration-settings-group__grid{grid-template-columns:1fr}.integration-settings-checkboxes__list{grid-template-columns:1fr}.card{padding:12px}.modal--image-preview{width:min(92vw,100%)}} +:root{--c-primary: #6690f4;--c-primary-dark: #3164db;--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);--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-primary)}.btn--primary:hover{background:var(--c-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);border-color:var(--c-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{padding:12px 14px;border-radius:8px;border:1px solid rgba(0,0,0,0);font-size:13px;min-height:44px}.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}.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%}}*{box-sizing:border-box}html,body{min-height:100%}body{margin:0;font-family:"Roboto","Segoe UI",sans-serif;font-size:13px;color:var(--c-text);background:var(--c-bg)}a{color:var(--c-primary)}.app-shell{min-height:100vh;display:flex}.sidebar{width:260px;min-width:260px;flex-shrink:0;overflow:hidden;transition:width .22s ease,min-width .22s ease;border-right:1px solid #243041;background:#111a28;padding:18px 10px;display:flex;flex-direction:column}.sidebar.is-collapsed{width:52px;min-width:52px}.sidebar__brand{display:flex;align-items:center;justify-content:space-between;margin:4px 4px 16px;gap:6px;min-width:0}.sidebar__brand-text{color:#e9f0ff;font-size:24px;font-weight:300;letter-spacing:-0.02em;white-space:nowrap;overflow:hidden;flex:1;min-width:0}.sidebar__brand-text strong{font-weight:700}.sidebar__collapse-btn{flex-shrink:0;width:28px;height:28px;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0);border:1px solid #2a3a54;border-radius:6px;color:#64748b;cursor:pointer;padding:0;transition:background .15s,color .15s}.sidebar__collapse-btn:hover{background:#1b2a3f;color:#cbd5e1}.sidebar__collapse-icon{display:block;transition:transform .22s ease;flex-shrink:0}.sidebar.is-collapsed .sidebar__collapse-icon{transform:rotate(180deg)}.sidebar__nav{display:grid;gap:4px}.sidebar__link{display:flex;align-items:center;gap:9px;white-space:nowrap;border-radius:8px;padding:9px 10px;text-decoration:none;color:#cbd5e1;font-weight:600}.sidebar__link:hover{color:#f8fafc;background:#1b2a3f}.sidebar__link.is-active{color:#fff;background:#2e4f93}.sidebar__group{display:grid;gap:2px}.sidebar__group-toggle{list-style:none;border-radius:8px;padding:9px 10px;color:#cbd5e1;font-weight:600;cursor:pointer;display:flex;align-items:center;gap:9px;white-space:nowrap;user-select:none}.sidebar__group-toggle::-webkit-details-marker{display:none}.sidebar__group:hover .sidebar__group-toggle,.sidebar__group-toggle:hover{color:#f8fafc;background:#1b2a3f}.sidebar__group.is-active .sidebar__group-toggle{color:#fff;background:#2e4f93}.sidebar__icon{flex-shrink:0;width:18px;height:18px;display:flex;align-items:center;justify-content:center;opacity:.85}.sidebar__label{flex:1;min-width:0;overflow:hidden}.sidebar__toggle-arrow{flex-shrink:0;margin-left:auto;opacity:.5;transition:transform .18s ease}details[open]>.sidebar__group-toggle .sidebar__toggle-arrow{transform:rotate(180deg)}.sidebar__group-links{display:grid;gap:2px;padding-left:12px;overflow:hidden}.sidebar__sublink{border-radius:6px;padding:7px 10px 7px 8px;text-decoration:none;color:#94a3b8;font-size:12.5px;font-weight:500;display:flex;align-items:center;gap:8px;white-space:nowrap}.sidebar__sublink::before{content:"";flex-shrink:0;width:5px;height:5px;border-radius:50%;background:rgba(148,163,184,.3);transition:background .15s}.sidebar__sublink:hover{color:#e2e8f0;background:#1b2a3f}.sidebar__sublink:hover::before{background:rgba(148,163,184,.65)}.sidebar__sublink.is-active{color:#fff;background:rgba(46,79,147,.55)}.sidebar__sublink.is-active::before{background:#93c5fd}.app-main{flex:1;min-width:0}.topbar{height:50px;border-bottom:1px solid var(--c-border);background:var(--c-surface);display:flex;align-items:center;justify-content:space-between;padding:0 20px;position:sticky;top:0;z-index:100}.brand{font-size:22px;font-weight:300;letter-spacing:-0.02em;color:var(--c-text-strong)}.brand strong{font-weight:700}.container{max-width:none;width:calc(100% - 20px);margin:12px 10px;padding:0 4px 14px}.card{background:var(--c-surface);border-radius:10px;box-shadow:var(--shadow-card);padding:14px}.card h1{margin:0 0 10px;color:var(--c-text-strong);font-size:24px;font-weight:700}.muted{color:var(--c-muted)}.accent{color:var(--c-primary);font-weight:600}.users-form{display:grid;gap:14px;max-width:460px}.form-field{margin-bottom:12px}.section-title{margin:0;color:var(--c-text-strong);font-size:18px;font-weight:700}h2.section-title,h3.section-title,h4.section-title{display:flex;align-items:center;gap:6px;font-weight:600;padding:6px 0;border-bottom:1px solid #e2e8f0;color:var(--c-primary, #2563eb)}h2.section-title::before,h3.section-title::before,h4.section-title::before{content:"■";font-size:.55em;opacity:.5}h3.section-title,h4.section-title{font-size:15px}h3.section-title::before,h4.section-title::before{content:"◆";font-size:.5em}.mt-0{margin-top:0}.mt-4{margin-top:4px}.mt-12{margin-top:8px}.mt-16{margin-top:12px}.settings-grid{display:grid;grid-template-columns:repeat(3, minmax(0, 1fr));gap:12px}.settings-nav{display:flex;gap:8px;flex-wrap:wrap}.settings-nav__link{text-decoration:none;border:1px solid var(--c-border);border-radius:8px;padding:8px 12px;color:var(--c-text-strong);font-weight:600}.settings-nav__link:hover{background:#f8fafc}.settings-nav__link.is-active{border-color:var(--c-primary);color:var(--c-primary);background:#edf2ff}.settings-stat{border:1px solid var(--c-border);border-radius:8px;padding:12px;background:#f8fafc}.settings-stat__label{display:block;color:var(--c-muted);font-size:12px;margin-bottom:4px}.settings-stat__value{color:var(--c-text-strong);font-size:20px}.settings-logs{margin:0;padding:12px;border-radius:8px;border:1px solid var(--c-border);background:#0b1220;color:#d1d5db;font-size:12px;line-height:1.5;overflow:auto}.settings-allegro-callback{display:block;width:100%;padding:8px 10px;border:1px solid var(--c-border);border-radius:8px;background:#f8fafc;color:var(--c-text-strong);font-size:12px;line-height:1.45;word-break:break-all}.page-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.filters-grid{display:grid;grid-template-columns:repeat(3, minmax(0, 1fr));gap:12px}.filters-actions{display:flex;align-items:center;gap:8px}.product-form .form-control{width:100%}.form-grid{display:grid;grid-template-columns:repeat(2, minmax(0, 1fr));gap:12px}.form-grid-2{display:grid;grid-template-columns:repeat(2, minmax(0, 1fr));gap:12px;align-items:start}.form-grid-3{display:grid;grid-template-columns:repeat(3, minmax(0, 1fr));gap:12px;align-items:start}.form-grid-4{display:grid;grid-template-columns:repeat(4, minmax(0, 1fr));gap:12px;align-items:start}.form-actions{display:flex;gap:8px;flex-wrap:wrap;align-items:flex-start}.form-actions .btn{align-self:flex-start}.statuses-form{display:grid;gap:8px;grid-template-columns:repeat(2, minmax(0, 1fr))}.statuses-form .form-actions{grid-column:1/-1}.statuses-color-input{min-height:32px;padding:2px}.statuses-hint{grid-column:1/-1;margin:0}.statuses-group-block{border:1px solid var(--c-border);border-radius:10px;padding:8px;background:#fbfdff}.statuses-group-block__head{display:flex;align-items:center;justify-content:space-between;gap:6px;flex-wrap:wrap}.statuses-group-block__title{margin:0;display:inline-flex;align-items:center;gap:6px;color:var(--c-text-strong);font-size:14px}.statuses-color-dot{width:12px;height:12px;border-radius:999px;border:1px solid rgba(15,23,42,.15)}.statuses-dnd-list{margin:6px 0 0;padding:0;list-style:none;display:grid;gap:6px}.statuses-dnd-item{display:grid;grid-template-columns:24px 1fr;gap:6px;border:1px solid #dce4f0;border-radius:8px;background:#fff;padding:6px}.statuses-dnd-item__content{display:flex;align-items:center;gap:6px;min-width:0}.statuses-dnd-item.is-dragging{opacity:.6}.statuses-dnd-item__drag{display:inline-flex;align-items:center;justify-content:center;border:1px dashed #cbd5e1;border-radius:6px;color:#64748b;cursor:grab;user-select:none;font-weight:700;font-size:12px}.statuses-dnd-item__drag:active{cursor:grabbing}.statuses-inline-form{display:grid;gap:6px}.statuses-inline-form--row{grid-template-columns:minmax(180px, 1.4fr) minmax(150px, 1fr) auto auto auto;align-items:center;flex:1 1 auto;min-width:0}.statuses-inline-form--row-group{grid-template-columns:minmax(180px, 1.5fr) 56px auto auto auto;align-items:center;flex:1 1 auto;min-width:0}.statuses-inline-form--row .form-control,.statuses-inline-form--row-group .form-control{min-height:30px;padding:4px 8px}.statuses-inline-form--row .btn,.statuses-inline-form--row-group .btn,.statuses-inline-delete .btn{min-height:30px;padding:4px 10px;font-size:12px}.statuses-inline-check{margin-top:0;white-space:nowrap;font-size:12px}.statuses-inline-delete{margin:0;flex:0 0 auto}.statuses-code-label{font-size:12px;color:var(--c-muted)}.statuses-code-readonly{display:inline-flex;align-items:center;gap:6px;white-space:nowrap;font-size:12px}.statuses-code-readonly code{background:#eef2f7;border-radius:6px;padding:1px 6px;color:#1f2937;font-size:12px}.field-inline{display:flex;align-items:center;gap:8px;margin-top:2px}.modal-backdrop{position:fixed;inset:0;background:rgba(15,23,42,.5);display:flex;align-items:center;justify-content:center;padding:16px;z-index:200}.modal-backdrop[hidden]{display:none}.modal{width:min(560px,100%);background:#fff;border-radius:10px;box-shadow:0 20px 40px rgba(15,23,42,.35);overflow:hidden}.modal__header{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:16px 18px;border-bottom:1px solid var(--c-border)}.modal__header h3{margin:0;font-size:18px;color:var(--c-text-strong)}.modal__body{padding:16px 18px 18px}.status-pill{display:inline-flex;align-items:center;justify-content:center;border:1px solid #fed7d7;background:#fff5f5;color:#9b2c2c;padding:2px 8px;border-radius:999px;font-size:12px;font-weight:600}.status-pill.is-active{border-color:#b7ebcf;background:#f0fff6;color:#0f6b39}.table-row-actions{display:inline-flex;align-items:center;gap:6px;flex-wrap:wrap}.table-row-actions form{margin:0}.table-list{display:grid;gap:14px}.table-list__header{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap}.table-list__left{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}.table-list-header-actions{display:inline-flex;align-items:center;gap:10px;flex-wrap:wrap}.js-filter-toggle-btn.is-active{border-color:#cbd5e0;background:#edf2ff;color:var(--c-primary-dark)}.table-filter-badge{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;font-size:11px;font-weight:700;color:#fff;background:var(--c-primary);border-radius:999px}.table-filters-wrapper{display:none}.table-filters-wrapper.is-open{display:block}.table-list-filters{display:grid;gap:12px;grid-template-columns:repeat(auto-fit, minmax(170px, 1fr));align-items:end}.table-col-toggle-wrapper{position:relative}.table-col-toggle-dropdown{display:none;position:absolute;right:0;top:calc(100% + 6px);z-index:30;width:260px;max-height:360px;overflow:auto;border:1px solid var(--c-border);border-radius:10px;background:#fff;box-shadow:0 10px 25px rgba(15,23,42,.12)}.table-col-toggle-dropdown.is-open{display:block}.table-col-toggle-header{padding:10px 12px;border-bottom:1px solid var(--c-border);font-size:12px;font-weight:700;color:var(--c-muted)}.table-col-toggle-item{display:flex;align-items:center;gap:10px;padding:8px 12px;font-size:13px;color:var(--c-text-strong)}.table-col-toggle-item:hover{background:#f8fafc}.table-col-toggle-footer{border-top:1px solid var(--c-border);padding:8px 12px}.table-col-hidden{display:none}.table-col-switch{position:relative;display:inline-block;width:34px;min-width:34px;height:18px}.table-col-switch input{opacity:0;width:0;height:0;position:absolute}.table-col-switch-slider{position:absolute;top:0;left:0;right:0;bottom:0;background:#cbd5e1;border-radius:999px;transition:background-color .2s ease}.table-col-switch-slider::before{content:"";position:absolute;height:14px;width:14px;left:2px;bottom:2px;background:#fff;border-radius:50%;transition:transform .2s ease}.table-col-switch input:checked+.table-col-switch-slider{background:#16a34a}.table-col-switch input:checked+.table-col-switch-slider::before{transform:translateX(16px)}.table-sort-link{display:inline-flex;align-items:center;gap:6px;color:var(--c-text-strong);text-decoration:none}.table-sort-link:hover{color:var(--c-primary-dark)}.table-sort-icon.is-muted{color:#a0aec0}.table-list__footer{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}.table-list-per-page-form{display:inline-flex;align-items:center;gap:8px}.table-list-per-page-form .form-control{min-width:90px}.table-select-col{width:44px;text-align:center}.table-select-toggle{display:inline-flex;align-items:center;justify-content:center}.table-select-toggle input[type=checkbox]{width:16px;height:16px}.orders-page .orders-head{background:linear-gradient(120deg, #f8fbff 0%, #eef5ff 100%);border:1px solid #dbe7fb}.orders-page .table-list{border:1px solid #dde5f2;border-radius:12px;box-shadow:0 6px 16px rgba(20,44,86,.08)}.orders-page .table-list__header{padding:10px 6px 2px}.orders-page .table-list-filters{padding:6px 6px 2px;border-top:1px solid #ebf0f7;border-bottom:1px solid #ebf0f7;background:#f9fbff}.orders-page .table-wrap{border-radius:10px;overflow:hidden;border:1px solid #e7edf6}.orders-page .table thead th{background:#f3f7fd;color:#30435f;font-size:12px;text-transform:uppercase;letter-spacing:.03em}.orders-page .table tbody td{vertical-align:middle;padding-top:10px;padding-bottom:10px;border-bottom-color:#edf2f8}.orders-page .table tbody tr:hover td{background:#f9fcff}.orders-list-page{padding:10px;margin-bottom:10px}.orders-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap}.orders-stats{display:inline-grid;grid-template-columns:repeat(3, minmax(86px, auto));gap:8px}.orders-stat{border:1px solid #d8e2f0;background:#f8fbff;border-radius:8px;padding:6px 8px;line-height:1.15}.orders-stat__label{display:block;color:#5f6f83;font-size:11px;margin-bottom:2px}.orders-stat__value{color:#12233a;font-size:16px;font-weight:700}.orders-ref{display:grid;gap:2px;min-width:170px}.orders-ref__main{font-weight:700;color:#0f1f35;font-size:14px}.orders-ref__meta{display:inline-flex;flex-wrap:wrap;gap:4px 10px;color:#64748b;font-size:12px}.orders-buyer{display:grid;gap:2px}.orders-buyer__name{color:#0f172a;font-weight:600;font-size:14px}.orders-buyer__meta{display:inline-flex;flex-wrap:wrap;gap:4px 10px;color:#64748b;font-size:12px}.orders-status-wrap{display:inline-flex;align-items:center;gap:5px;flex-wrap:wrap}.order-tag{display:inline-flex;align-items:center;justify-content:center;border:1px solid #d8e1ef;background:#f8fafc;color:#334155;border-radius:999px;padding:2px 8px;font-size:12px;font-weight:700;line-height:1.1}.order-tag.is-info{border-color:#bfdbfe;background:#eff6ff;color:#1d4ed8}.order-tag.is-success{border-color:#bbf7d0;background:#f0fdf4;color:#166534}.order-tag.is-danger{border-color:#fecaca;background:#fef2f2;color:#b91c1c}.order-tag.is-warn{border-color:#fde68a;background:#fffbeb;color:#92400e}.order-tag.is-cod{border-color:#f9a8d4;background:#fdf2f8;color:#9d174d}.order-tag.is-unpaid{border-color:#fca5a5;background:#fef2f2;color:#b91c1c}.orders-mini{font-size:14px;color:#223247;line-height:1.25}.orders-mini__delivery{font-size:12px;color:#64748b;margin-bottom:2px;max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.orders-products{display:grid;gap:4px;min-width:240px}.orders-products__meta,.orders-products__more{font-size:12px;color:#64748b}.orders-product{display:grid;grid-template-columns:48px 1fr;gap:6px;align-items:center}.orders-product__thumb{width:48px;height:48px;border-radius:4px;border:1px solid #dbe3ef;object-fit:cover;background:#fff}.orders-product__thumb--empty{display:inline-block;background:#eef2f7;border-style:dashed}.orders-product__txt{min-width:0;display:grid;gap:1px}.orders-product__name{font-size:14px;color:#0f172a;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.orders-product__qty{font-size:12px;color:#64748b}.orders-image-hover-wrap{position:relative;display:inline-flex;align-items:center;justify-content:center;cursor:zoom-in}.orders-image-hover-popup{display:none;position:fixed;left:auto;top:auto;width:350px;max-height:350px;object-fit:contain;border-radius:8px;background:#fff;box-shadow:0 8px 24px rgba(0,0,0,.18);border:1px solid #dfe3ea;z-index:100;pointer-events:none}.orders-image-hover-wrap:hover .orders-image-hover-popup{display:block}.activity-type-badge{display:inline-block;padding:2px 8px;border-radius:4px;font-size:12px;font-weight:500;white-space:nowrap;background:#e2e8f0;color:#334155}.activity-type-badge--status_change{background:#dbeafe;color:#1e40af}.activity-type-badge--payment{background:#dcfce7;color:#166534}.activity-type-badge--invoice{background:#fef3c7;color:#92400e}.activity-type-badge--shipment{background:#e0e7ff;color:#3730a3}.activity-type-badge--message{background:#f3e8ff;color:#6b21a8}.activity-type-badge--document{background:#fce7f3;color:#9d174d}.activity-type-badge--import{background:#f1f5f9;color:#475569}.activity-type-badge--note{background:#ecfdf5;color:#065f46}.text-nowrap{white-space:nowrap}.orders-money{display:grid;gap:2px}.orders-money__main{color:#0f172a;font-weight:700;font-size:14px}.orders-money__meta{color:#64748b;font-size:12px}.table-list[data-table-list-id=orders]{gap:8px}.table-list[data-table-list-id=orders] .table-list__header{padding:2px 0 0}.table-list[data-table-list-id=orders] .table-list-filters{gap:8px;grid-template-columns:repeat(auto-fit, minmax(150px, 1fr))}.table-list[data-table-list-id=orders] .table th,.table-list[data-table-list-id=orders] .table td{padding:6px 8px}.table-list[data-table-list-id=orders] .table thead th{font-size:12px;text-transform:uppercase;letter-spacing:.02em;white-space:nowrap}.table-list[data-table-list-id=orders] .table tbody td{vertical-align:top;font-size:14px;line-height:1.25}.order-show-layout{display:grid;grid-template-columns:220px minmax(0, 1fr);gap:12px;align-items:start}.order-statuses-side{position:sticky;top:60px;padding:10px}.order-statuses-side__title{font-size:13px;font-weight:700;color:#0f172a;margin-bottom:8px}.order-status-group{margin-bottom:10px}.order-status-group__name{font-size:12px;color:#475569;font-weight:700;margin-bottom:5px}.order-status-row{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:4px 6px;border-radius:6px;color:#334155;font-size:12px;text-decoration:none}.order-status-row__count{min-width:24px;text-align:center;border-radius:999px;background:var(--status-color, #64748b);padding:1px 6px;font-weight:700;font-size:11px;color:#fff}.order-status-row:hover{background:#f1f5f9}.order-status-row.is-active{background:rgba(15,23,42,.06);color:#0f172a;font-weight:700}.order-show-main{min-width:0}.order-details-actions{display:inline-flex;flex-wrap:wrap;justify-content:flex-end;gap:6px}.order-details-page{padding:12px}.order-details-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap}.order-back-link{color:#475569;text-decoration:none;font-weight:600}.order-back-link:hover{color:#1d4ed8}.order-details-sub{display:inline-flex;gap:10px;flex-wrap:wrap;color:#64748b;font-size:12px}.order-details-pill{border-radius:999px;padding:5px 10px;background:#eef6ff;border:1px solid #cfe2ff;color:#1d4ed8;font-size:12px;font-weight:700}.order-status-change{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.order-status-change__form{display:flex;align-items:center;gap:6px}.order-status-change__select{min-width:180px}.order-details-tabs{display:flex;gap:6px;flex-wrap:wrap}.order-details-tab{border:1px solid #d6deea;border-radius:8px;padding:5px 10px;color:#475569;font-size:12px;background:#f8fafc;cursor:pointer}.order-details-tab.is-active{border-color:#bfdbfe;color:#1d4ed8;background:#eff6ff;font-weight:700}.order-item-cell{display:grid;grid-template-columns:44px 1fr;gap:8px;align-items:center;min-width:260px}.order-item-thumb{width:44px;height:44px;border-radius:6px;border:1px solid #dbe3ef;object-fit:cover}.order-item-thumb--empty{display:inline-block;background:#eef2f7;border-style:dashed}.order-item-name{font-weight:600;color:#0f172a}.order-grid-2{display:grid;grid-template-columns:repeat(2, minmax(0, 1fr));gap:12px}.order-grid-3{display:grid;grid-template-columns:repeat(3, minmax(0, 1fr));gap:12px}.order-kv{margin:0;display:grid;grid-template-columns:150px 1fr;gap:6px 10px;font-size:12px}.payment-summary{display:grid;gap:6px;max-width:420px}.payment-summary__row{display:flex;align-items:center;gap:10px;font-size:12px}.payment-summary__label{width:150px;flex-shrink:0;color:#64748b}.payment-summary__value{font-weight:600;color:#0f172a}.order-kv dt{color:#64748b}.order-kv dd{margin:0;color:#0f172a;font-weight:600}.order-address{display:grid;gap:3px;font-size:12px;color:#0f172a}.order-events{display:grid;gap:8px}.order-event{border:1px solid #e2e8f0;border-radius:8px;padding:8px;background:#fbfdff}.order-event__head{color:#64748b;font-size:11px}.order-event__body{margin-top:4px;color:#0f172a;font-size:12px}.order-tab-panel{display:none}.order-tab-panel.is-active{display:block}.order-empty-placeholder{border:1px dashed #cbd5e1;border-radius:8px;min-height:180px;background:#f8fafc}.order-status-badge{display:inline-flex;align-items:center;justify-content:center;padding:4px 10px;border-radius:999px;font-size:12px;font-weight:700;border:1px solid #cbd5e1;color:#334155;background:#f8fafc}.order-status-badge.is-info{border-color:#bfdbfe;background:#eff6ff;color:#1d4ed8}.order-status-badge.is-success{border-color:#bbf7d0;background:#f0fdf4;color:#166534}.order-status-badge.is-danger{border-color:#fecaca;background:#fef2f2;color:#b91c1c}.order-status-badge.is-warn{border-color:#fde68a;background:#fffbeb;color:#92400e}.order-status-badge.is-empty{color:#94a3b8}.order-buyer{display:grid;gap:2px}.order-buyer__name{color:#0f172a;font-weight:600}.order-buyer__email{color:#64748b;font-size:12px}.table-inline-action{display:inline-block;margin-right:6px}.product-name-cell{display:inline-flex;align-items:center;gap:10px}.product-name-thumb{width:60px;height:60px;border-radius:6px;object-fit:cover;border:1px solid var(--c-border);background:#f8fafc}.product-name-thumb--empty{display:inline-block;width:60px;height:60px;border-radius:6px;border:1px dashed #cbd5e0;background:#f8fafc}.product-name-thumb-btn{border:0;padding:0;background:rgba(0,0,0,0);cursor:pointer;display:inline-flex;align-items:center;justify-content:center}.product-name-thumb-btn:focus-visible{outline:none;box-shadow:var(--focus-ring);border-radius:8px}.modal--image-preview{width:min(760px,100%)}.product-image-preview__img{display:block;width:100%;max-height:70vh;object-fit:contain;border-radius:8px;background:#f8fafc}.product-images-grid{display:grid;grid-template-columns:repeat(auto-fill, minmax(220px, 1fr));gap:12px}.product-image-card{border:1px solid #dfe3ea;border-radius:10px;padding:10px;background:#fff}.product-image-card__thumb-wrap{position:relative;border-radius:8px;overflow:hidden;background:#f2f5f8}.product-image-card__thumb{width:100%;height:160px;object-fit:cover;display:block}.product-image-card__thumb.is-empty{height:160px;display:grid;place-items:center;color:#6b7785;font-size:12px}.product-image-card__badge{display:none;position:absolute;top:8px;left:8px;background:#1f7a43;color:#fff;padding:3px 8px;border-radius:999px;font-size:11px}.product-image-card.is-main .product-image-card__badge{display:inline-block}.product-image-card__meta{margin-top:8px;font-size:11px;line-height:1.25;color:#5f6b79;overflow-wrap:anywhere}.product-image-card__actions{margin-top:10px;display:grid;grid-template-columns:1fr;gap:8px}.product-image-card__actions .btn{min-height:34px;font-size:12px;line-height:1.2;padding:6px 10px}.product-links-search-form{display:grid;gap:12px;grid-template-columns:minmax(220px, 320px) minmax(220px, 1fr) auto;align-items:end}.product-links-head{display:grid;gap:8px;grid-template-columns:repeat(3, minmax(0, 1fr))}.product-tabs-nav{display:flex;align-items:center;gap:8px;flex-wrap:wrap}.product-links-inline-form{display:grid;gap:8px;grid-template-columns:minmax(140px, 1fr) minmax(140px, 1fr) auto;align-items:center}.product-links-actions-row{display:flex;align-items:center;gap:8px;flex-wrap:nowrap}.product-links-actions-row .product-links-relink-form{flex:1 1 auto}.product-links-unlink-form{margin:0;flex:0 0 auto}.product-link-status-cell{display:inline-flex;align-items:center;gap:6px}.product-link-alert-indicator{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:999px;border:1px solid #f59e0b;background:#fffbeb;color:#b45309;font-size:12px;font-weight:700;cursor:help}.product-link-events-list{margin:0;padding:0;list-style:none;display:grid;gap:4px}.product-link-events-list li{display:grid;gap:2px}.product-link-events-type{font-weight:600;color:var(--c-text-strong)}.product-link-events-date{color:var(--c-muted);font-size:12px}.product-show-images-grid{display:grid;grid-template-columns:repeat(auto-fill, minmax(220px, 1fr));gap:12px}.product-show-image-card{border:1px solid var(--c-border);border-radius:10px;background:#fff;padding:10px;overflow:hidden}.product-show-image-card__meta{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;min-width:0}.product-show-image-path{font-size:12px;min-width:0;overflow:hidden}.product-show-image-path summary{cursor:pointer;color:var(--c-muted, #888);list-style:none;user-select:none;white-space:nowrap}.product-show-image-path summary::-webkit-details-marker{display:none}.product-show-image-path summary::after{content:" ▾"}.product-show-image-path[open] summary::after{content:" ▴"}.product-show-image-path__url{margin-top:4px;word-break:break-all;overflow-wrap:break-word;font-size:11px}.product-show-image{width:100%;max-height:260px;object-fit:cover;border-radius:8px;border:1px solid #d9e0ea}.shipment-grid{display:grid;grid-template-columns:repeat(2, minmax(0, 1fr));gap:12px}.searchable-select{position:relative}.searchable-select__trigger{display:flex;align-items:center;justify-content:space-between;cursor:pointer;user-select:none;min-height:34px}.searchable-select__trigger::after{content:"";width:0;height:0;border-left:4px solid rgba(0,0,0,0);border-right:4px solid rgba(0,0,0,0);border-top:5px solid var(--c-text-muted, #6b7280);margin-left:8px;flex-shrink:0}.searchable-select__trigger--placeholder{color:var(--c-text-muted, #6b7280)}.searchable-select__dropdown{display:none;position:absolute;left:0;right:0;top:100%;z-index:50;max-height:280px;overflow:auto;background:#fff;border:1px solid var(--c-border);border-top:0;border-radius:0 0 8px 8px;box-shadow:0 8px 20px rgba(15,23,42,.12)}.searchable-select__dropdown.is-open{display:block}.searchable-select__search{position:sticky;top:0;border:none !important;border-bottom:1px solid var(--c-border) !important;border-radius:0 !important;box-shadow:none !important;font-size:13px;background:#fff;z-index:1}.searchable-select__option{padding:7px 10px;font-size:13px;cursor:pointer;color:var(--c-text-strong)}.searchable-select__option:hover{background:#f1f5f9}.searchable-select__option.is-selected{background:#edf2ff;font-weight:600}.flash{padding:10px 14px;border-radius:8px;font-size:13px;font-weight:500}.flash--success{background:#f0fdf4;border:1px solid #bbf7d0;color:#166534}.flash--error{background:#fef2f2;border:1px solid #fecaca;color:#b91c1c}.content-tabs-card{margin-top:0}.content-tabs-nav{display:flex;gap:4px;border-bottom:2px solid var(--c-border);margin-bottom:16px;flex-wrap:wrap}.content-tab-btn{padding:8px 16px;border:none;background:none;cursor:pointer;font-size:14px;font-weight:500;color:var(--c-text-muted, #6b7280);border-bottom:2px solid rgba(0,0,0,0);margin-bottom:-2px;border-radius:4px 4px 0 0;transition:color .15s,border-color .15s}.content-tab-btn:hover{color:var(--c-text-strong, #111827)}.content-tab-btn.is-active{color:var(--c-primary, #2563eb);border-bottom-color:var(--c-primary, #2563eb)}.content-tab-panel{display:none}.content-tab-panel.is-active{display:block}.shoppro-tabs-toolbar{display:flex;align-items:flex-end;justify-content:space-between;gap:10px;margin-bottom:10px;flex-wrap:wrap}.shoppro-tabs-toolbar__field{margin:0;min-width:260px;max-width:420px;flex:1 1 320px}.shoppro-tabs-toolbar__field .form-control{width:100%}.shoppro-tabs-toolbar__actions{display:inline-flex;align-items:center;gap:8px}.dm-carrier-select{min-width:140px}.dm-service-wrap{min-width:200px}.dm-service-wrap .dm-inpost-panel .form-control,.dm-service-wrap .dm-apaczka-panel .form-control{width:100%}.integration-settings-group{grid-column:1/-1;border:1px solid var(--c-border);border-radius:10px;background:#f8fbff;padding:10px}.integration-settings-group__head{margin-bottom:8px;padding:4px 0;border-bottom:1px solid #e2e8f0}.integration-settings-group__title{margin:0;font-size:14px;font-weight:600;letter-spacing:.01em;color:var(--c-text-strong, #1e293b)}.integration-settings-group__desc{margin:4px 0 0;color:#4b5563}.integration-settings-group__grid{display:grid;grid-template-columns:repeat(2, minmax(0, 1fr));gap:10px 12px;align-items:start}.integration-settings-group__full{grid-column:1/-1}.integration-settings-group__grid .form-field{margin:0;align-self:start}.integration-settings-group__grid .form-control{min-height:34px;height:34px}.integration-settings-group__grid input[type=date].form-control{line-height:1.2}.integration-settings-checkboxes{border:0;padding:0;margin:0}.integration-settings-checkboxes .field-label{display:block;margin-bottom:2px}.integration-settings-checkboxes__list{display:grid;grid-template-columns:repeat(2, minmax(0, 1fr));gap:6px 12px}.integration-settings-checkboxes__item{display:inline-flex;align-items:center;gap:6px;font-size:13px;color:#334155}@media(max-width: 768px){.app-shell{flex-direction:column}.sidebar{width:100% !important;min-width:0 !important;border-right:0;border-bottom:1px solid #243041;padding:14px;overflow-x:auto}.sidebar__brand{margin:0 0 10px;font-size:22px}.sidebar__collapse-btn{display:none}.sidebar__nav{display:flex;gap:8px;overflow-x:auto}.sidebar__link{white-space:nowrap}.topbar{padding:0 14px}.container{margin-top:16px;width:calc(100% - 16px);margin-left:8px;margin-right:8px;padding:0 3px 12px}.settings-grid{grid-template-columns:1fr}.page-head{flex-direction:column;align-items:flex-start}.orders-stats{grid-template-columns:1fr;width:100%}.order-show-layout{grid-template-columns:1fr}.order-statuses-side{position:static;top:auto}.order-details-actions{justify-content:flex-start}.order-grid-2,.order-grid-3{grid-template-columns:1fr}.order-kv{grid-template-columns:1fr;gap:2px}.filters-grid,.form-grid,.form-grid-2,.form-grid-3,.form-grid-4,.shipment-grid,.statuses-form,.statuses-inline-form,.table-list-filters,.product-links-search-form,.product-links-inline-form{grid-template-columns:1fr}.statuses-dnd-item__content{display:block}.statuses-inline-delete{margin-top:6px}.filters-actions{align-items:center}.table-list__header,.table-list__footer{align-items:flex-start}.product-links-head{grid-template-columns:1fr}.integration-settings-group__grid{grid-template-columns:1fr}.integration-settings-checkboxes__list{grid-template-columns:1fr}.card{padding:12px}.modal--image-preview{width:min(92vw,100%)}.email-tpl-editor-wrap{flex-direction:column}.email-tpl-var-panel{min-width:200px}.modal-box{width:95vw;max-height:90vh}}.email-tpl-editor-wrap{display:flex;flex-direction:column;border:1px solid var(--c-border);border-radius:6px;overflow:hidden}.email-tpl-toolbar{display:flex;align-items:center;gap:6px;padding:6px 8px;background:var(--c-bg-subtle, #f8f9fa);border-bottom:1px solid var(--c-border)}.email-tpl-var-dropdown{position:relative}.email-tpl-var-panel{position:absolute;top:100%;left:0;z-index:50;min-width:260px;max-height:320px;overflow-y:auto;background:var(--c-bg);border:1px solid var(--c-border);border-radius:6px;box-shadow:0 4px 12px rgba(0,0,0,.12);padding:6px;margin-top:4px}.email-var-group:not(:first-child){margin-top:6px;padding-top:6px;border-top:1px solid var(--c-border)}.email-var-group__label{font-size:11px;font-weight:600;text-transform:uppercase;color:var(--c-text-muted);padding:2px 4px;letter-spacing:.03em}.email-var-item{display:block;width:100%;text-align:left;padding:3px 6px;margin:1px 0;border:none;background:none;font-size:12px;font-family:"Roboto Mono",monospace;color:var(--c-text);border-radius:3px;cursor:pointer}.email-var-item:hover{background:var(--c-primary);color:#fff}#js-quill-editor{min-height:200px}#js-quill-editor .ql-editor{min-height:200px;font-size:13px}.modal-overlay{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.45)}.modal-box{width:min(680px,90vw);max-height:80vh;background:var(--c-bg);border-radius:8px;box-shadow:0 8px 30px rgba(0,0,0,.2);display:flex;flex-direction:column;overflow:hidden}.modal-box__header{display:flex;align-items:center;justify-content:space-between;padding:10px 16px;border-bottom:1px solid var(--c-border)}.modal-box__title{margin:0;font-size:15px;font-weight:600}.modal-box__close{background:none;border:none;font-size:22px;line-height:1;cursor:pointer;color:var(--c-text-muted);padding:0 4px}.modal-box__close:hover{color:var(--c-text)}.modal-box__body{padding:12px 16px;overflow-y:auto;flex:1} diff --git a/resources/scss/app.scss b/resources/scss/app.scss index 9b2f7ff..53e9cd3 100644 --- a/resources/scss/app.scss +++ b/resources/scss/app.scss @@ -105,8 +105,12 @@ a { } .sidebar__link { + display: flex; + align-items: center; + gap: 9px; + white-space: nowrap; border-radius: 8px; - padding: 10px 12px; + padding: 9px 10px; text-decoration: none; color: #cbd5e1; font-weight: 600; @@ -2303,5 +2307,158 @@ h4.section-title { .modal--image-preview { width: min(92vw, 100%); } + + .email-tpl-editor-wrap { + flex-direction: column; + } + + .email-tpl-var-panel { + min-width: 200px; + } + + .modal-box { + width: 95vw; + max-height: 90vh; + } +} + +// Email template editor +.email-tpl-editor-wrap { + display: flex; + flex-direction: column; + border: 1px solid var(--c-border); + border-radius: 6px; + overflow: hidden; +} + +.email-tpl-toolbar { + display: flex; + align-items: center; + gap: 6px; + padding: 6px 8px; + background: var(--c-bg-subtle, #f8f9fa); + border-bottom: 1px solid var(--c-border); +} + +.email-tpl-var-dropdown { + position: relative; +} + +.email-tpl-var-panel { + position: absolute; + top: 100%; + left: 0; + z-index: 50; + min-width: 260px; + max-height: 320px; + overflow-y: auto; + background: var(--c-bg); + border: 1px solid var(--c-border); + border-radius: 6px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12); + padding: 6px; + margin-top: 4px; +} + +.email-var-group { + &:not(:first-child) { + margin-top: 6px; + padding-top: 6px; + border-top: 1px solid var(--c-border); + } + + &__label { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + color: var(--c-text-muted); + padding: 2px 4px; + letter-spacing: 0.03em; + } +} + +.email-var-item { + display: block; + width: 100%; + text-align: left; + padding: 3px 6px; + margin: 1px 0; + border: none; + background: none; + font-size: 12px; + font-family: "Roboto Mono", monospace; + color: var(--c-text); + border-radius: 3px; + cursor: pointer; + + &:hover { + background: var(--c-primary); + color: #fff; + } +} + +#js-quill-editor { + min-height: 200px; + + .ql-editor { + min-height: 200px; + font-size: 13px; + } +} + +// Modal overlay (reusable) +.modal-overlay { + position: fixed; + inset: 0; + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; + background: rgba(0, 0, 0, 0.45); +} + +.modal-box { + width: min(680px, 90vw); + max-height: 80vh; + background: var(--c-bg); + border-radius: 8px; + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2); + display: flex; + flex-direction: column; + overflow: hidden; + + &__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 16px; + border-bottom: 1px solid var(--c-border); + } + + &__title { + margin: 0; + font-size: 15px; + font-weight: 600; + } + + &__close { + background: none; + border: none; + font-size: 22px; + line-height: 1; + cursor: pointer; + color: var(--c-text-muted); + padding: 0 4px; + + &:hover { + color: var(--c-text); + } + } + + &__body { + padding: 12px 16px; + overflow-y: auto; + flex: 1; + } } diff --git a/resources/views/layouts/app.php b/resources/views/layouts/app.php index 1884572..ef0cfa7 100644 --- a/resources/views/layouts/app.php +++ b/resources/views/layouts/app.php @@ -98,6 +98,9 @@ Skrzynki pocztowe + + Szablony e-mail + diff --git a/resources/views/settings/email-templates.php b/resources/views/settings/email-templates.php new file mode 100644 index 0000000..3460020 --- /dev/null +++ b/resources/views/settings/email-templates.php @@ -0,0 +1,311 @@ + + +
+

Szablony e-mail

+

Szablony wiadomosci e-mail z edytorem i systemem zmiennych.

+ + + + + +
+ +
+ +
+

Lista szablonow

+ + +

Brak szablonow. Dodaj pierwszy szablon ponizej.

+ +
+ + + + + + + + + + + + + + + + + + + + + +
NazwaTematSkrzynkaStatusAkcje
+ + Aktywny + + Nieaktywny + + + Edytuj + +
+ + + +
+
+
+ +
+ +
+

+ +
+ + + + + + +
+ + +
+ +
+ +
+ +
+
+ +
+ Tresc wiadomosci * + +
+ +
+ + + Anuluj + +
+
+
+ + + + + + diff --git a/routes/web.php b/routes/web.php index dd38679..a97ed8c 100644 --- a/routes/web.php +++ b/routes/web.php @@ -38,6 +38,8 @@ use App\Modules\Settings\ReceiptConfigController; use App\Modules\Settings\ReceiptConfigRepository; use App\Modules\Settings\EmailMailboxController; use App\Modules\Settings\EmailMailboxRepository; +use App\Modules\Settings\EmailTemplateController; +use App\Modules\Settings\EmailTemplateRepository; use App\Modules\Settings\IntegrationSecretCipher; use App\Modules\Accounting\AccountingController; use App\Modules\Accounting\ReceiptController; @@ -197,6 +199,14 @@ return static function (Application $app): void { $auth, $emailMailboxRepository ); + $emailTemplateRepository = new EmailTemplateRepository($app->db()); + $emailTemplateController = new EmailTemplateController( + $template, + $translator, + $auth, + $emailTemplateRepository, + $emailMailboxRepository + ); $receiptController = new ReceiptController( $template, $translator, @@ -325,6 +335,12 @@ return static function (Application $app): void { $router->post('/settings/email-mailboxes/delete', [$emailMailboxController, 'delete'], [$authMiddleware]); $router->post('/settings/email-mailboxes/toggle', [$emailMailboxController, 'toggleStatus'], [$authMiddleware]); $router->post('/settings/email-mailboxes/test', [$emailMailboxController, 'testConnection'], [$authMiddleware]); + $router->get('/settings/email-templates', [$emailTemplateController, 'index'], [$authMiddleware]); + $router->post('/settings/email-templates/save', [$emailTemplateController, 'save'], [$authMiddleware]); + $router->post('/settings/email-templates/delete', [$emailTemplateController, 'delete'], [$authMiddleware]); + $router->post('/settings/email-templates/toggle', [$emailTemplateController, 'toggleStatus'], [$authMiddleware]); + $router->post('/settings/email-templates/preview', [$emailTemplateController, 'preview'], [$authMiddleware]); + $router->get('/settings/email-templates/variables', [$emailTemplateController, 'getVariables'], [$authMiddleware]); $router->get('/accounting', [$accountingController, 'index'], [$authMiddleware]); $router->post('/accounting/export', [$accountingController, 'export'], [$authMiddleware]); $router->get('/orders/{id}/receipt/create', [$receiptController, 'create'], [$authMiddleware]); diff --git a/src/Modules/Settings/EmailTemplateController.php b/src/Modules/Settings/EmailTemplateController.php new file mode 100644 index 0000000..27601ff --- /dev/null +++ b/src/Modules/Settings/EmailTemplateController.php @@ -0,0 +1,228 @@ + [ + 'label' => 'Zamowienie', + 'vars' => [ + 'numer' => 'Numer wewnetrzny (OP...)', + 'numer_zewnetrzny' => 'Numer z platformy', + 'zrodlo' => 'Zrodlo (Allegro/shopPRO/...)', + 'kwota' => 'Kwota brutto', + 'waluta' => 'Waluta (PLN/EUR/...)', + 'data' => 'Data zamowienia', + ], + ], + 'kupujacy' => [ + 'label' => 'Kupujacy', + 'vars' => [ + 'imie_nazwisko' => 'Imie i nazwisko', + 'email' => 'Adres e-mail', + 'telefon' => 'Telefon', + 'login' => 'Login platformy', + ], + ], + 'adres' => [ + 'label' => 'Adres dostawy', + 'vars' => [ + 'ulica' => 'Ulica z numerem', + 'miasto' => 'Miasto', + 'kod_pocztowy' => 'Kod pocztowy', + 'kraj' => 'Kraj', + ], + ], + 'firma' => [ + 'label' => 'Firma', + 'vars' => [ + 'nazwa' => 'Nazwa firmy', + 'nip' => 'NIP', + ], + ], + ]; + + private const SAMPLE_DATA = [ + 'zamowienie.numer' => 'OP000001234', + 'zamowienie.numer_zewnetrzny' => 'ALG-98765432', + 'zamowienie.zrodlo' => 'Allegro', + 'zamowienie.kwota' => '149,99', + 'zamowienie.waluta' => 'PLN', + 'zamowienie.data' => '2026-03-16', + 'kupujacy.imie_nazwisko' => 'Jan Kowalski', + 'kupujacy.email' => 'jan.kowalski@example.com', + 'kupujacy.telefon' => '+48 600 123 456', + 'kupujacy.login' => 'jankowalski82', + 'adres.ulica' => 'ul. Dluga 15/3', + 'adres.miasto' => 'Warszawa', + 'adres.kod_pocztowy' => '00-238', + 'adres.kraj' => 'PL', + 'firma.nazwa' => 'Przykladowa Firma Sp. z o.o.', + 'firma.nip' => '5271234567', + ]; + + public function __construct( + private readonly Template $template, + private readonly Translator $translator, + private readonly AuthService $auth, + private readonly EmailTemplateRepository $repository, + private readonly EmailMailboxRepository $mailboxRepository + ) { + } + + public function index(Request $request): Response + { + $t = $this->translator; + $templates = $this->repository->listAll(); + $mailboxes = $this->mailboxRepository->listActive(); + + $editTemplate = null; + $editId = (int) $request->input('edit', '0'); + if ($editId > 0) { + $editTemplate = $this->repository->findById($editId); + } + + $html = $this->template->render('settings/email-templates', [ + 'title' => 'Szablony e-mail', + 'activeMenu' => 'settings', + 'activeSettings' => 'email-templates', + 'user' => $this->auth->user(), + 'csrfToken' => Csrf::token(), + 'templates' => $templates, + 'mailboxes' => $mailboxes, + 'editTemplate' => $editTemplate, + 'variableGroups' => self::VARIABLE_GROUPS, + 'successMessage' => Flash::get('settings.email_templates.success', ''), + 'errorMessage' => Flash::get('settings.email_templates.error', ''), + ], 'layouts/app'); + + return Response::html($html); + } + + public function save(Request $request): Response + { + if (!Csrf::validate((string) $request->input('_token', ''))) { + Flash::set('settings.email_templates.error', 'Nieprawidlowy token CSRF'); + return Response::redirect('/settings/email-templates'); + } + + $name = trim((string) $request->input('name', '')); + $subject = trim((string) $request->input('subject', '')); + $bodyHtml = (string) $request->input('body_html', ''); + + if ($name === '' || $subject === '' || $bodyHtml === '') { + Flash::set('settings.email_templates.error', 'Nazwa, temat i tresc sa wymagane'); + return Response::redirect('/settings/email-templates'); + } + + try { + $this->repository->save([ + 'id' => $request->input('id', ''), + 'name' => $name, + 'subject' => $subject, + 'body_html' => $bodyHtml, + 'mailbox_id' => $request->input('mailbox_id', ''), + 'is_active' => $request->input('is_active', null), + ]); + + Flash::set('settings.email_templates.success', 'Szablon zostal zapisany'); + } catch (Throwable) { + Flash::set('settings.email_templates.error', 'Blad zapisu szablonu'); + } + + return Response::redirect('/settings/email-templates'); + } + + public function delete(Request $request): Response + { + if (!Csrf::validate((string) $request->input('_token', ''))) { + Flash::set('settings.email_templates.error', 'Nieprawidlowy token CSRF'); + return Response::redirect('/settings/email-templates'); + } + + $id = (int) $request->input('id', '0'); + if ($id <= 0) { + Flash::set('settings.email_templates.error', 'Nieprawidlowy identyfikator szablonu'); + return Response::redirect('/settings/email-templates'); + } + + try { + $this->repository->delete($id); + Flash::set('settings.email_templates.success', 'Szablon zostal usuniety'); + } catch (Throwable) { + Flash::set('settings.email_templates.error', 'Blad usuwania szablonu'); + } + + return Response::redirect('/settings/email-templates'); + } + + public function toggleStatus(Request $request): Response + { + if (!Csrf::validate((string) $request->input('_token', ''))) { + return Response::json(['success' => false, 'message' => 'Nieprawidlowy token CSRF'], 403); + } + + $id = (int) $request->input('id', '0'); + if ($id <= 0) { + return Response::json(['success' => false, 'message' => 'Nieprawidlowy identyfikator'], 400); + } + + try { + $this->repository->toggleStatus($id); + return Response::json(['success' => true]); + } catch (Throwable) { + return Response::json(['success' => false, 'message' => 'Blad zmiany statusu'], 500); + } + } + + public function preview(Request $request): Response + { + if (!Csrf::validate((string) $request->input('_token', ''))) { + return Response::json(['success' => false, 'message' => 'Nieprawidlowy token CSRF'], 403); + } + + $subject = (string) $request->input('subject', ''); + $bodyHtml = (string) $request->input('body_html', ''); + + return Response::json([ + 'success' => true, + 'subject' => self::resolveVariables($subject, self::SAMPLE_DATA), + 'body_html' => self::resolveVariables($bodyHtml, self::SAMPLE_DATA), + ]); + } + + public function getVariables(Request $request): Response + { + return Response::json([ + 'success' => true, + 'groups' => self::VARIABLE_GROUPS, + ]); + } + + /** + * @param array $data + */ + public static function resolveVariables(string $text, array $data): string + { + return (string) preg_replace_callback( + '/\{\{([a-z_]+)\.([a-z_]+)\}\}/', + static function (array $matches) use ($data): string { + $key = $matches[1] . '.' . $matches[2]; + return $data[$key] ?? $matches[0]; + }, + $text + ); + } +} diff --git a/src/Modules/Settings/EmailTemplateRepository.php b/src/Modules/Settings/EmailTemplateRepository.php new file mode 100644 index 0000000..f2d02f8 --- /dev/null +++ b/src/Modules/Settings/EmailTemplateRepository.php @@ -0,0 +1,117 @@ +> + */ + public function listAll(): array + { + $statement = $this->pdo->prepare( + 'SELECT t.id, t.name, t.subject, t.mailbox_id, t.is_active, t.created_at, t.updated_at, + m.name AS mailbox_name + FROM email_templates t + LEFT JOIN email_mailboxes m ON m.id = t.mailbox_id + ORDER BY t.name ASC' + ); + $statement->execute(); + $rows = $statement->fetchAll(PDO::FETCH_ASSOC); + + return is_array($rows) ? $rows : []; + } + + /** + * @return array|null + */ + public function findById(int $id): ?array + { + $statement = $this->pdo->prepare( + 'SELECT id, name, subject, body_html, mailbox_id, is_active, created_at, updated_at + FROM email_templates + WHERE id = :id' + ); + $statement->execute(['id' => $id]); + $row = $statement->fetch(PDO::FETCH_ASSOC); + + return is_array($row) ? $row : null; + } + + /** + * @return list> + */ + public function listActive(): array + { + $statement = $this->pdo->prepare( + 'SELECT id, name, subject, mailbox_id + FROM email_templates + WHERE is_active = 1 + ORDER BY name ASC' + ); + $statement->execute(); + $rows = $statement->fetchAll(PDO::FETCH_ASSOC); + + return is_array($rows) ? $rows : []; + } + + /** + * @param array $data + */ + public function save(array $data): void + { + $id = isset($data['id']) && $data['id'] !== '' ? (int) $data['id'] : null; + + $mailboxId = isset($data['mailbox_id']) && $data['mailbox_id'] !== '' && $data['mailbox_id'] !== '0' + ? (int) $data['mailbox_id'] + : null; + + $params = [ + 'name' => trim((string) ($data['name'] ?? '')), + 'subject' => trim((string) ($data['subject'] ?? '')), + 'body_html' => (string) ($data['body_html'] ?? ''), + 'mailbox_id' => $mailboxId, + 'is_active' => isset($data['is_active']) ? 1 : 0, + ]; + + if ($id !== null) { + $params['id'] = $id; + $statement = $this->pdo->prepare( + 'UPDATE email_templates + SET name = :name, subject = :subject, body_html = :body_html, + mailbox_id = :mailbox_id, is_active = :is_active + WHERE id = :id' + ); + } else { + $statement = $this->pdo->prepare( + 'INSERT INTO email_templates (name, subject, body_html, mailbox_id, is_active) + VALUES (:name, :subject, :body_html, :mailbox_id, :is_active)' + ); + } + + $statement->execute($params); + } + + public function delete(int $id): void + { + $statement = $this->pdo->prepare('DELETE FROM email_templates WHERE id = :id'); + $statement->execute(['id' => $id]); + } + + public function toggleStatus(int $id): void + { + $statement = $this->pdo->prepare( + 'UPDATE email_templates SET is_active = NOT is_active WHERE id = :id' + ); + $statement->execute(['id' => $id]); + } +}