feat(124): sms templates CRUD + order picker
- Nowa tabela sms_templates (name + body + is_active) + minimalny CRUD.
- /settings/sms-templates: lista + formularz z paleta zmiennych (pill chips).
- Wydzielono Sms\SmsVariableResolver ze wspolna logika placeholderow;
Email\VariableResolver staje sie cienka fasada — EmailSendingService bez zmian.
- Dropdown "Wybierz szablon" w zakladce SMS na /orders/{id} z fetch
GET /orders/{id}/sms/template + OrderProAlerts.confirm przy nadpisaniu.
- Stopka SMSPLANET dalej doklejana wylacznie przez SmsConversationService
(Phase 122 contract preserved).
- Sidebar Ustawien: nowy link "Szablony SMS".
Migration: 20260512_000112_create_sms_templates.sql (CREATE TABLE).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -414,3 +414,61 @@ tests/
|
||||
- Edycja przez `<a href=".../edit?id=N">`, toggle/delete przez `<form>` z `_token` i `js-confirm-delete`.
|
||||
- Wspolny pattern miedzy `accounting-receipts.php` i `accounting-invoices.php` (faktury maja dodatkowe kolumny: Tryb, Konto Fakturowni).
|
||||
|
||||
|
||||
## Phase 124 — SMS Templates
|
||||
|
||||
### SmsTemplateRepository (`src/Modules/Sms/SmsTemplateRepository.php`)
|
||||
- CRUD na `sms_templates` (PDO prepared statements, ToggleableRepositoryTrait).
|
||||
- `listAll()` (cala lista alfabetycznie po `name`), `listActive()` (tylko is_active=1, kolumny `id|name|body` do dropdownu w UI).
|
||||
- `save(array): int` waliduje wymagane `name` + `body` (rzuca `RuntimeException` gdy puste); wykonuje INSERT albo UPDATE wg obecnosci `id` w payloadzie; zwraca id rekordu.
|
||||
- `delete(int)`, `toggleStatus(int)` przez `toggleActive('sms_templates', $id)`.
|
||||
|
||||
### SmsVariableResolver (`src/Modules/Sms/SmsVariableResolver.php`)
|
||||
- Wydzielony z `Email\VariableResolver` — wspolna logika zmiennych dla Email i SMS.
|
||||
- `buildVariableMap(order, addresses, companySettings)` zwraca mape placeholderow: `zamowienie.*`, `kupujacy.*`, `adres.*`, `firma.*`, `przesylka.*` (`przesylka.numer`/`przesylka.link_sledzenia` z najnowszej paczki przez `ShipmentPackageRepository::findLatestByOrderId` + `DeliveryStatus::trackingUrl`).
|
||||
- `resolve(template, variableMap)` zastepuje `{{group.var}}` wartoscia z mapy (puste gdy brak klucza).
|
||||
|
||||
### Email\VariableResolver (refaktor)
|
||||
- Pozostaje final class z tym samym API publicznym (`buildVariableMap`/`resolve`) — `EmailSendingService` niezmieniony.
|
||||
- Konstruktor: `(ShipmentPackageRepository $repo, ?SmsVariableResolver $inner = null)`. Gdy `$inner` nie podany, sam tworzy SmsVariableResolver — backward compat dla starego wiringu.
|
||||
- Metody publiczne deleguja do `$this->inner` — zero duplikacji logiki zmiennych.
|
||||
|
||||
### SmsTemplateController (`src/Modules/Settings/SmsTemplateController.php`)
|
||||
- Mirror `EmailTemplateController` bez Quill/skrzynki/zalacznika/duplikacji.
|
||||
- Akcje: `index` (lista), `create`/`edit`/`save` (form CRUD), `delete`, `toggleStatus` (AJAX JSON), `getVariables` (JSON paleta dla ewentualnego dynamic palette).
|
||||
- `VARIABLE_GROUPS` jako stala klasy — pelne 5 grup (zamowienie/kupujacy/adres/firma/przesylka) zgodnie ze wspolnym SmsVariableResolver.
|
||||
- Routy: `/settings/sms-templates`, `/create`, `/edit`, `/save`, `/delete`, `/toggle`, `/variables`. CSRF `_token` na POST. Flash `settings.sms_templates.success|error`.
|
||||
|
||||
### OrdersController (rozszerzenie)
|
||||
- Dodane optional params konstruktora: `?SmsTemplateRepository $smsTemplates`, `?SmsVariableResolver $smsVariableResolver`, `?CompanySettingsRepository $companySettingsRepo` (po istniejacych SMS params; default null = backward compat).
|
||||
- `show()` przekazuje `$smsTemplates` (list active) do widoku jako `smsTemplates`.
|
||||
- Nowa metoda `smsTemplate(Request)` -> `GET /orders/{id}/sms/template?template_id=N` -> JSON `{ok, body, name}` z rozwinietymi zmiennymi. 400/404/500 dla nieprawidlowych parametrow/braku rekordu.
|
||||
|
||||
### Widok `orders/show.php`
|
||||
- Nad textarea `name="message"` (`#js-sms-message`) dodany conditional `<select data-sms-template-picker data-order-id data-message-target="js-sms-message">` z opcja domyslna + aktywne szablony (renderowany tylko gdy `$smsTemplatesList !== []`).
|
||||
- Textarea ma teraz `id="js-sms-message"` — JS target.
|
||||
|
||||
### Frontend module `public/assets/js/modules/sms-template-picker.js`
|
||||
- Vanilla JS, idempotent guard `window.__smsTemplatePickerBound` + per-element `dataset.smsPickerBound`.
|
||||
- Na `change` selecta: fetch `/orders/{id}/sms/template?template_id=N`, podstaw body do textarea, fire `input` event.
|
||||
- Gdy textarea ma juz tresc -> `OrderProAlerts.confirm({...})` options-object API (Phase 114 pattern). Po zatwierdzeniu nadpisuje, po anulowaniu resetuje select. Fallback na natywny `confirm()`.
|
||||
- Ladowany globalnie z `layouts/app.php` (linia po `notifications.js`).
|
||||
|
||||
### Wspolny resolver — wiring DI (`routes/web.php`)
|
||||
- `$smsVariableResolver = new SmsVariableResolver($shipmentPackageRepositoryForOrders);`
|
||||
- `$variableResolver = new VariableResolver($shipmentPackageRepositoryForOrders, $smsVariableResolver);` (drugi argument opcjonalny dla BC).
|
||||
- `$smsTemplateRepository = new SmsTemplateRepository($app->db());`
|
||||
- `$smsTemplateController = new SmsTemplateController($template, $translator, $auth, $smsTemplateRepository);`
|
||||
- `$ordersController` rozszerzony o 3 trailing params (smsTemplateRepository, smsVariableResolver, companySettingsRepository).
|
||||
|
||||
### SCSS — `_sms-templates.scss`
|
||||
- Nowy partial `resources/scss/modules/_sms-templates.scss` z klasami `.sms-template-*` (active label, counter, body grid) oraz `.sms-var-panel/.sms-var-group/.sms-var-item` dla palety zmiennych.
|
||||
- Import w `app.scss` po `customer-risk-alert`.
|
||||
|
||||
### Stopka — preserved Phase 122 contract
|
||||
- Szablony SMS NIE zawieraja `default_footer` — operator wpisuje sama tresc.
|
||||
- `SmsConversationService::buildFinalOutboundBody()` dokleja stopke raz przy `sendFromOrder()` (po wstawieniu szablonu i ewentualnej edycji przez operatora). Walidacja `MAX_SMS_LENGTH = 918` obowiazuje na finalnej tresci.
|
||||
|
||||
### BREAKING / migration
|
||||
- Migracja `20260512_000112_create_sms_templates.sql` — `CREATE TABLE IF NOT EXISTS sms_templates` (DDL, brak SELECT no-op).
|
||||
- Brak innych zmian schematu. `OrdersController` ctor: 3 NEW optional params (default null) — backwards compatible.
|
||||
|
||||
Reference in New Issue
Block a user