feat(24-shipment-presets-ui): kolorowe przyciski presetów, popup tworzenia, autofill formularza

Phase 24 complete:
- SCSS moduł _shipment-presets.scss (przyciski, popup, color picker)
- Sekcja presetów nad formularzem przesyłki z przyciskiem "Dodaj"
- Popup tworzenia presetu z nazwą i wyborem koloru (8 opcji)
- JS autofill: carrier, usługa dostawy, wymiary, waga, label format
- Obsługa 3 paneli: Allegro searchable, InPost select, Apaczka select

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-22 23:42:56 +01:00
parent 03a237e7d2
commit e379557533
8 changed files with 740 additions and 2874 deletions

View File

@@ -61,6 +61,28 @@ $defaultCodAmount = $isCod ? number_format($totalWithTax, 2, '.', '') : '0';
<?php endif; ?>
</section>
<section class="shipment-presets" id="shipment-presets">
<button type="button" class="shipment-presets__add" id="preset-add-btn">+ Dodaj przycisk dostawy</button>
</section>
<div class="preset-modal" id="preset-modal" style="display:none">
<div class="preset-modal__content">
<h3>Nowy przycisk dostawy</h3>
<label class="form-field mt-12">
<span class="field-label">Nazwa</span>
<input class="form-control" type="text" id="preset-name-input" maxlength="100" placeholder="np. InPost Paczkomat Standard">
</label>
<div class="form-field mt-12">
<span class="field-label">Kolor</span>
<div class="preset-modal__colors" id="preset-color-picker"></div>
</div>
<div class="form-actions mt-16">
<button type="button" class="btn btn--primary" id="preset-save-btn">Zapisz</button>
<button type="button" class="btn btn--secondary" id="preset-cancel-btn">Anuluj</button>
</div>
</div>
</div>
<form method="post" action="/orders/<?= $e((string) ($orderId ?? 0)) ?>/shipment/create" novalidate>
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
@@ -790,3 +812,235 @@ $defaultCodAmount = $isCod ? number_format($totalWithTax, 2, '.', '') : '0';
})();
</script>
<script>
(function () {
'use strict';
var PRESET_COLORS = ['#3b82f6','#ef4444','#10b981','#f59e0b','#8b5cf6','#ec4899','#06b6d4','#6b7280'];
var presetsContainer = document.getElementById('shipment-presets');
var addBtn = document.getElementById('preset-add-btn');
var modal = document.getElementById('preset-modal');
var nameInput = document.getElementById('preset-name-input');
var colorPicker = document.getElementById('preset-color-picker');
var saveBtn = document.getElementById('preset-save-btn');
var cancelBtn = document.getElementById('preset-cancel-btn');
if (!presetsContainer || !addBtn || !modal) return;
var selectedColor = PRESET_COLORS[0];
var presetsData = [];
// --- Color picker ---
PRESET_COLORS.forEach(function (color, idx) {
var swatch = document.createElement('div');
swatch.className = 'preset-modal__color-swatch' + (idx === 0 ? ' is-selected' : '');
swatch.style.background = color;
swatch.setAttribute('data-color', color);
swatch.addEventListener('click', function () {
colorPicker.querySelectorAll('.preset-modal__color-swatch').forEach(function (s) { s.classList.remove('is-selected'); });
swatch.classList.add('is-selected');
selectedColor = color;
});
colorPicker.appendChild(swatch);
});
// --- Load presets ---
function loadPresets() {
fetch('/api/shipment-presets', { credentials: 'same-origin' })
.then(function (r) { return r.json(); })
.then(function (data) {
presetsData = data.presets || [];
renderPresets();
})
.catch(function () {});
}
function renderPresets() {
var existing = presetsContainer.querySelectorAll('.shipment-presets__btn');
existing.forEach(function (btn) { btn.remove(); });
presetsData.forEach(function (preset) {
var btn = document.createElement('button');
btn.type = 'button';
btn.className = 'shipment-presets__btn';
btn.style.background = preset.color || '#3b82f6';
btn.textContent = preset.name;
btn.setAttribute('data-preset-id', preset.id);
btn.addEventListener('click', function () { applyPreset(preset); });
presetsContainer.insertBefore(btn, addBtn);
});
}
// --- Apply preset (autofill form) ---
function applyPreset(preset) {
var carrierSelect = document.getElementById('shipment-carrier-select');
var hiddenInput = document.getElementById('shipment-delivery-service');
var credentialsInput = document.getElementById('shipment-credentials-id');
var carrierInput = document.getElementById('shipment-carrier-id');
var providerInput = document.getElementById('shipment-provider-code');
if (!carrierSelect) return;
// Set carrier — use native change so existing handler shows correct panel
carrierSelect.value = preset.carrier || '';
if (carrierSelect._syncTrigger) carrierSelect._syncTrigger();
carrierSelect.dispatchEvent(new Event('change'));
// The existing change handler clears hidden fields and resets selects.
// We must wait for that to finish, then override with preset values.
setTimeout(function () {
// Hidden fields — set AFTER the change handler cleared them
if (hiddenInput) hiddenInput.value = preset.delivery_method_id || '';
if (credentialsInput) credentialsInput.value = preset.credentials_id || '';
if (carrierInput) carrierInput.value = preset.carrier_id || '';
if (providerInput) providerInput.value = preset.provider_code || '';
// Package fields
setFieldValue('package_type', preset.package_type || 'PACKAGE');
setFieldValue('length_cm', preset.length_cm || '25');
setFieldValue('width_cm', preset.width_cm || '20');
setFieldValue('height_cm', preset.height_cm || '8');
setFieldValue('weight_kg', preset.weight_kg || '1');
setFieldValue('sender_point_id', preset.sender_point_id || '');
setFieldValue('label_format', preset.label_format || 'PDF');
// Select delivery service in the correct panel
selectDeliveryService(preset);
}, 200);
}
function setFieldValue(name, value) {
var field = document.querySelector('[name="' + name + '"]');
if (!field) return;
field.value = value;
if (field.tagName === 'SELECT' && field._syncTrigger) {
field._syncTrigger();
}
}
function selectDeliveryService(preset) {
var carrier = preset.carrier || '';
var methodId = preset.delivery_method_id || '';
var credentialsId = preset.credentials_id || '';
var carrierId = preset.carrier_id || '';
var hiddenInput = document.getElementById('shipment-delivery-service');
var credentialsInput = document.getElementById('shipment-credentials-id');
var carrierInput = document.getElementById('shipment-carrier-id');
if (carrier === 'allegro') {
// Click the matching option in Allegro searchable dropdown
var dropdown = document.getElementById('shipment-service-dropdown');
if (dropdown) {
var opts = dropdown.querySelectorAll('.searchable-select__option');
opts.forEach(function (opt) {
opt.classList.remove('is-selected');
if (opt.getAttribute('data-value') === methodId) {
opt.classList.add('is-selected');
// Update search input to show selected service name
var searchInput = document.getElementById('shipment-service-search');
if (searchInput) searchInput.value = opt.getAttribute('data-label') || opt.textContent.trim();
// Set hidden fields from option data attributes
if (hiddenInput) hiddenInput.value = methodId;
if (credentialsInput) credentialsInput.value = opt.getAttribute('data-credentials-id') || credentialsId;
if (carrierInput) carrierInput.value = opt.getAttribute('data-carrier-id') || carrierId;
}
});
}
} else if (carrier === 'inpost') {
var inpostSelect = document.getElementById('shipment-inpost-select');
if (inpostSelect) {
inpostSelect.value = methodId;
if (inpostSelect._syncTrigger) inpostSelect._syncTrigger();
// Trigger change to set hidden fields via existing handler
inpostSelect.dispatchEvent(new Event('change'));
}
} else if (carrier === 'apaczka') {
var apaczkaSelect = document.getElementById('shipment-apaczka-select');
if (apaczkaSelect) {
apaczkaSelect.value = methodId;
if (apaczkaSelect._syncTrigger) apaczkaSelect._syncTrigger();
// Trigger change to set hidden fields via existing handler
apaczkaSelect.dispatchEvent(new Event('change'));
}
}
}
// --- Modal: add preset ---
addBtn.addEventListener('click', function () {
nameInput.value = '';
selectedColor = PRESET_COLORS[0];
colorPicker.querySelectorAll('.preset-modal__color-swatch').forEach(function (s, i) {
s.classList.toggle('is-selected', i === 0);
});
modal.style.display = '';
nameInput.focus();
});
cancelBtn.addEventListener('click', function () {
modal.style.display = 'none';
});
modal.addEventListener('click', function (e) {
if (e.target === modal) modal.style.display = 'none';
});
saveBtn.addEventListener('click', function () {
var name = nameInput.value.trim();
if (!name) {
nameInput.focus();
return;
}
var carrierSelect = document.getElementById('shipment-carrier-select');
var hiddenInput = document.getElementById('shipment-delivery-service');
var credentialsInput = document.getElementById('shipment-credentials-id');
var carrierInput = document.getElementById('shipment-carrier-id');
var providerInput = document.getElementById('shipment-provider-code');
var payload = {
name: name,
color: selectedColor,
carrier: carrierSelect ? carrierSelect.value : '',
provider_code: providerInput ? providerInput.value : '',
delivery_method_id: hiddenInput ? hiddenInput.value : '',
credentials_id: credentialsInput ? credentialsInput.value : '',
carrier_id: carrierInput ? carrierInput.value : '',
package_type: getFieldValue('package_type'),
length_cm: getFieldValue('length_cm'),
width_cm: getFieldValue('width_cm'),
height_cm: getFieldValue('height_cm'),
weight_kg: getFieldValue('weight_kg'),
sender_point_id: getFieldValue('sender_point_id'),
label_format: getFieldValue('label_format')
};
saveBtn.disabled = true;
var formBody = new URLSearchParams(payload);
fetch('/api/shipment-presets', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: formBody.toString()
})
.then(function (r) { return r.json(); })
.then(function (data) {
if (data.preset) {
modal.style.display = 'none';
loadPresets();
}
})
.catch(function () {})
.finally(function () { saveBtn.disabled = false; });
});
function getFieldValue(name) {
var field = document.querySelector('[name="' + name + '"]');
return field ? field.value : '';
}
// --- Init ---
loadPresets();
})();
</script>