feat(16-automated-tasks): moduł zadań automatycznych — CRUD + watcher/executor
Reguły automatyzacji oparte na zdarzeniach (receipt.created) z warunkami (integracja/kanał sprzedaży, AND logic) i akcjami (wyślij e-mail z 3 trybami odbiorcy: klient / firma / klient+firma). Trigger w ReceiptController po utworzeniu paragonu — błąd automatyzacji nie blokuje sukcesu paragonu. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
132
public/assets/js/modules/automation-form.js
Normal file
132
public/assets/js/modules/automation-form.js
Normal file
@@ -0,0 +1,132 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var conditionsContainer = document.getElementById('js-conditions-container');
|
||||
var actionsContainer = document.getElementById('js-actions-container');
|
||||
var addConditionBtn = document.getElementById('js-add-condition');
|
||||
var addActionBtn = document.getElementById('js-add-action');
|
||||
var data = window.AutomationFormData || { integrations: [], emailTemplates: [], recipientLabels: {} };
|
||||
|
||||
function getNextIndex(container) {
|
||||
var rows = container.querySelectorAll('.automation-row');
|
||||
var maxIdx = -1;
|
||||
rows.forEach(function(row) {
|
||||
var idx = parseInt(row.getAttribute('data-index') || '0', 10);
|
||||
if (idx > maxIdx) maxIdx = idx;
|
||||
});
|
||||
return maxIdx + 1;
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
var div = document.createElement('div');
|
||||
div.textContent = str;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function buildIntegrationCheckboxes(namePrefix) {
|
||||
var html = '<div class="checkbox-group">';
|
||||
data.integrations.forEach(function(integ) {
|
||||
html += '<label class="checkbox-label">'
|
||||
+ '<input type="checkbox" name="' + namePrefix + '[integration_ids][]" value="' + integ.id + '"> '
|
||||
+ escapeHtml(integ.name) + ' <span class="muted">(' + escapeHtml(integ.type) + ')</span>'
|
||||
+ '</label>';
|
||||
});
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function buildEmailActionConfig(namePrefix) {
|
||||
var html = '<select class="form-control" name="' + namePrefix + '[template_id]">'
|
||||
+ '<option value="">-- Wybierz szablon --</option>';
|
||||
data.emailTemplates.forEach(function(tpl) {
|
||||
html += '<option value="' + tpl.id + '">' + escapeHtml(tpl.name) + '</option>';
|
||||
});
|
||||
html += '</select>';
|
||||
|
||||
html += '<select class="form-control" name="' + namePrefix + '[recipient]">';
|
||||
Object.keys(data.recipientLabels).forEach(function(key) {
|
||||
html += '<option value="' + escapeHtml(key) + '">' + escapeHtml(data.recipientLabels[key]) + '</option>';
|
||||
});
|
||||
html += '</select>';
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function addCondition() {
|
||||
var idx = getNextIndex(conditionsContainer);
|
||||
var namePrefix = 'conditions[' + idx + ']';
|
||||
|
||||
var row = document.createElement('div');
|
||||
row.className = 'automation-row mt-8';
|
||||
row.setAttribute('data-index', idx);
|
||||
|
||||
row.innerHTML = '<div class="automation-row__fields">'
|
||||
+ '<select class="form-control automation-row__type" name="' + namePrefix + '[type]" onchange="window.AutomationForm.onConditionTypeChange(this)">'
|
||||
+ '<option value="integration" selected>Integracja (kanal sprzedazy)</option>'
|
||||
+ '</select>'
|
||||
+ '<div class="automation-row__config">'
|
||||
+ buildIntegrationCheckboxes(namePrefix)
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
+ '<button type="button" class="btn btn--sm btn--danger automation-row__remove" onclick="window.AutomationForm.removeRow(this)">×</button>';
|
||||
|
||||
conditionsContainer.appendChild(row);
|
||||
}
|
||||
|
||||
function addAction() {
|
||||
var idx = getNextIndex(actionsContainer);
|
||||
var namePrefix = 'actions[' + idx + ']';
|
||||
|
||||
var row = document.createElement('div');
|
||||
row.className = 'automation-row mt-8';
|
||||
row.setAttribute('data-index', idx);
|
||||
|
||||
row.innerHTML = '<div class="automation-row__fields">'
|
||||
+ '<select class="form-control automation-row__type" name="' + namePrefix + '[type]" onchange="window.AutomationForm.onActionTypeChange(this)">'
|
||||
+ '<option value="send_email" selected>Wyslij e-mail</option>'
|
||||
+ '</select>'
|
||||
+ '<div class="automation-row__config">'
|
||||
+ buildEmailActionConfig(namePrefix)
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
+ '<button type="button" class="btn btn--sm btn--danger automation-row__remove" onclick="window.AutomationForm.removeRow(this)">×</button>';
|
||||
|
||||
actionsContainer.appendChild(row);
|
||||
}
|
||||
|
||||
function removeRow(btn) {
|
||||
var row = btn.closest('.automation-row');
|
||||
if (row) row.remove();
|
||||
}
|
||||
|
||||
function onConditionTypeChange(select) {
|
||||
var row = select.closest('.automation-row');
|
||||
var configDiv = row.querySelector('.automation-row__config');
|
||||
var idx = row.getAttribute('data-index');
|
||||
var namePrefix = 'conditions[' + idx + ']';
|
||||
|
||||
if (select.value === 'integration') {
|
||||
configDiv.innerHTML = buildIntegrationCheckboxes(namePrefix);
|
||||
}
|
||||
}
|
||||
|
||||
function onActionTypeChange(select) {
|
||||
var row = select.closest('.automation-row');
|
||||
var configDiv = row.querySelector('.automation-row__config');
|
||||
var idx = row.getAttribute('data-index');
|
||||
var namePrefix = 'actions[' + idx + ']';
|
||||
|
||||
if (select.value === 'send_email') {
|
||||
configDiv.innerHTML = buildEmailActionConfig(namePrefix);
|
||||
}
|
||||
}
|
||||
|
||||
if (addConditionBtn) addConditionBtn.addEventListener('click', addCondition);
|
||||
if (addActionBtn) addActionBtn.addEventListener('click', addAction);
|
||||
|
||||
window.AutomationForm = {
|
||||
removeRow: removeRow,
|
||||
onConditionTypeChange: onConditionTypeChange,
|
||||
onActionTypeChange: onActionTypeChange
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user