Files
orderPRO/public/assets/js/modules/order-notes.js
Jacek Pyziak 48351b5f36 feat(129): order user notes module
CRUD notatek autorskich operatora per zamowienie z badge [N] na liscie
zamowien. Reuse istniejacej tabeli `order_notes` przez nowy
`note_type='user'` z `user_id` (FK->users SET NULL) i `author_name`
(snapshot). Sekcja `#notes` w "Wiadomosci i zalaczniki" w
`/orders/{id}` z inline edit form + delete przez
`OrderProAlerts.confirm`. Autoryzacja DB-level
(`WHERE user_id = :user_id`, rowCount=0 ⇒ 403) — bez admin override
(brak systemu rol w aplikacji).

- Migracja `20260514_000116_*.sql` (ADD COLUMN user_id + author_name +
  FK + indeks `idx_order_notes_type_order`); idempotentne z DDL
  no-op fallback.
- `OrderNotesService` (CRUD + walidacja body ≤ 2000 znakow); subquery
  `user_notes_count` w paginate; badge HTML w `toTableRow()`.
- 3 routy POST /orders/{id}/notes(/update|/delete).
- SCSS module `_order-notes.scss` + vanilla JS `order-notes.js`
  (inline edit toggle + delete confirm; idempotent guard).
- 9 kluczy i18n PL; PROJECT.md + ROADMAP.md + tech_changelog.md +
  db_schema.md zaktualizowane.

Follow-up: `php bin/migrate.php` + manualny smoke test (autor vs inny
user + badge na /orders/list).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 15:20:05 +02:00

90 lines
3.0 KiB
JavaScript

(function () {
'use strict';
if (window.__orderNotesInit) {
return;
}
window.__orderNotesInit = true;
function bindEdit(button) {
if (button.dataset.bound === '1') return;
button.dataset.bound = '1';
button.addEventListener('click', function () {
var noteEl = button.closest('[data-note-id]');
if (!noteEl) return;
var body = noteEl.querySelector('.js-order-note-body');
var form = noteEl.querySelector('.js-order-note-edit-form');
if (!body || !form) return;
body.style.display = 'none';
form.style.display = '';
var textarea = form.querySelector('textarea[name="body"]');
if (textarea) {
textarea.focus();
var len = textarea.value.length;
try { textarea.setSelectionRange(len, len); } catch (e) { /* ignore */ }
}
});
}
function bindCancel(button) {
if (button.dataset.bound === '1') return;
button.dataset.bound = '1';
button.addEventListener('click', function () {
var noteEl = button.closest('[data-note-id]');
if (!noteEl) return;
var body = noteEl.querySelector('.js-order-note-body');
var form = noteEl.querySelector('.js-order-note-edit-form');
if (!body || !form) return;
form.style.display = 'none';
body.style.display = '';
});
}
function bindDelete(form) {
if (form.dataset.bound === '1') return;
form.dataset.bound = '1';
form.addEventListener('submit', function (event) {
event.preventDefault();
if (window.OrderProAlerts && typeof window.OrderProAlerts.confirm === 'function') {
window.OrderProAlerts.confirm({
title: 'Usunac notatke?',
message: 'Tej operacji nie mozna cofnac.',
danger: true,
confirmLabel: 'Usun',
onConfirm: function () {
form.dataset.bound = '2';
form.submit();
}
});
} else {
form.dataset.bound = '2';
form.submit();
}
});
}
function init() {
var editButtons = document.querySelectorAll('.js-order-note-edit');
for (var i = 0; i < editButtons.length; i++) {
bindEdit(editButtons[i]);
}
var cancelButtons = document.querySelectorAll('.js-order-note-edit-cancel');
for (var j = 0; j < cancelButtons.length; j++) {
bindCancel(cancelButtons[j]);
}
var deleteForms = document.querySelectorAll('.js-order-note-delete');
for (var k = 0; k < deleteForms.length; k++) {
bindDelete(deleteForms[k]);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();