feat(108): delivery status management
Phase 108 complete (v3.2 milestone):
Plan 108-01 — Delivery Status DB & CRUD:
- Tabela delivery_statuses z seedem 11 statusow systemowych
- DeliveryStatusRepository (CRUD + per-request static cache)
- DeliveryStatus::setRepository() — DB fallback dla static final class
- Panel /settings/delivery-statuses (zakladki Statusy + Mapowanie)
- Sidebar przebudowany: Statusy zamowien + Statusy przesylek
Plan 108-02 — Automation Dropdowns z DB + UI Refactor:
- Dropdowny automatyzacji ladowane z DB (warunek shipment_status + akcja update_shipment_status)
- Walidacja przez DeliveryStatus::getAllStatuses()
- Osobna podstrona formularza CRUD (delivery-status-form.php)
- Lista uproszczona: rename Terminal -> Koncowy, usunieta kolumna Typ
- BREAKING: drop backward compat dla starych grupowych kluczy automatyzacji
- Bug fix: path params w DeliveryStatusesController via \$request->input('id')
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -29,7 +29,8 @@ return [
|
||||
'cron' => 'Harmonogram',
|
||||
'dashboard' => 'Dashboard',
|
||||
'settings' => 'Ustawienia',
|
||||
'statuses' => 'Statusy',
|
||||
'statuses' => 'Statusy zamówień',
|
||||
'delivery_statuses' => 'Statusy przesyłek',
|
||||
'integrations' => 'Integracje',
|
||||
'allegro' => 'Integracje Allegro',
|
||||
'apaczka' => 'Integracja Apaczka',
|
||||
|
||||
@@ -24,3 +24,26 @@
|
||||
text-decoration: none;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.delivery-status-swatch {
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 2px;
|
||||
background: var(--status-color, #6c757d);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.delivery-status-system-badge {
|
||||
display: inline-block;
|
||||
padding: 1px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 0.75em;
|
||||
background: #e9ecef;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.delivery-badge--custom {
|
||||
background: var(--status-color, #6c757d);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
@@ -139,8 +139,8 @@
|
||||
$dsmUnmappedCount = 0;
|
||||
}
|
||||
?>
|
||||
<a class="sidebar__sublink<?= $currentMenu === 'settings' && $currentSettings === 'delivery-status-mappings' ? ' is-active' : '' ?>" href="/settings/delivery-status-mappings">
|
||||
Mapowanie statusów dostawy
|
||||
<a class="sidebar__sublink<?= $currentMenu === 'settings' && in_array($currentSettings, ['delivery-statuses', 'delivery-status-mappings'], true) ? ' is-active' : '' ?>" href="/settings/delivery-statuses">
|
||||
<?= $e($t('navigation.delivery_statuses')) ?>
|
||||
<?php if ($dsmUnmappedCount > 0): ?>
|
||||
<span class="sidebar__badge" title="Niezmapowane statusy"><?= (int) $dsmUnmappedCount ?></span>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -370,7 +370,11 @@ foreach ($addressesList as $address) {
|
||||
?>
|
||||
<dt>Status dostawy</dt>
|
||||
<dd>
|
||||
<span class="delivery-badge delivery-badge--<?= $e($ldStatus) ?>"><?= $e($ldLabel) ?></span>
|
||||
<?php
|
||||
$ldIsSystem = in_array($ldStatus, \App\Modules\Shipments\DeliveryStatus::ALL_STATUSES, true);
|
||||
$ldColor = $ldIsSystem ? '' : \App\Modules\Shipments\DeliveryStatus::getColor($ldStatus);
|
||||
?>
|
||||
<span class="delivery-badge<?= $ldIsSystem ? ' delivery-badge--' . $e($ldStatus) : ' delivery-badge--custom' ?>"<?= $ldColor !== '' ? ' style="--status-color: ' . $e($ldColor) . '"' : '' ?>><?= $e($ldLabel) ?></span>
|
||||
<?php if ($ldDate !== ''): ?><small class="muted" style="margin-left:4px"><?= $e($ldDate) ?></small><?php endif; ?>
|
||||
</dd>
|
||||
<?php endif; ?>
|
||||
@@ -603,7 +607,11 @@ foreach ($addressesList as $address) {
|
||||
$pkgDeliveryDesc = $pkgDeliveryRaw !== '' ? \App\Modules\Shipments\DeliveryStatus::description($pkgProvider, $pkgDeliveryRaw) : '';
|
||||
$pkgDeliveryTitle = $pkgDeliveryRaw !== '' ? ($pkgDeliveryRaw . ' — ' . $pkgDeliveryDesc) : '';
|
||||
?>
|
||||
<span class="delivery-badge delivery-badge--<?= $e($pkgDeliveryStatus) ?>" title="<?= $e($pkgDeliveryTitle) ?>"><?= $e($pkgDeliveryLabel) ?></span>
|
||||
<?php
|
||||
$pkgIsSystem = in_array($pkgDeliveryStatus, \App\Modules\Shipments\DeliveryStatus::ALL_STATUSES, true);
|
||||
$pkgColor = $pkgIsSystem ? '' : \App\Modules\Shipments\DeliveryStatus::getColor($pkgDeliveryStatus);
|
||||
?>
|
||||
<span class="delivery-badge<?= $pkgIsSystem ? ' delivery-badge--' . $e($pkgDeliveryStatus) : ' delivery-badge--custom' ?>" title="<?= $e($pkgDeliveryTitle) ?>"<?= $pkgColor !== '' ? ' style="--status-color: ' . $e($pkgColor) . '"' : '' ?>><?= $e($pkgDeliveryLabel) ?></span>
|
||||
</td>
|
||||
<td style="white-space:nowrap" data-pkg-tracking-cell="<?= $e((string) ($pkg['id'] ?? 0)) ?>">
|
||||
<?= $e($pkgTracking !== '' ? $pkgTracking : '-') ?><?php
|
||||
|
||||
211
resources/views/settings/_delivery-status-mappings-content.php
Normal file
211
resources/views/settings/_delivery-status-mappings-content.php
Normal file
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
$providersList = is_array($providers ?? null) ? $providers : [];
|
||||
$mappingsList = is_array($mappings ?? null) ? $mappings : [];
|
||||
$normalizedOptionsList = is_array($normalizedOptions ?? null) ? $normalizedOptions : [];
|
||||
$unmappedList = is_array($unmappedRawStatuses ?? null) ? $unmappedRawStatuses : [];
|
||||
$currentProvider = (string) ($provider ?? 'inpost');
|
||||
$mappingBaseUrl = isset($mappingBaseUrl) ? (string) $mappingBaseUrl : '/settings/delivery-status-mappings';
|
||||
?>
|
||||
|
||||
<?php if ($unmappedList !== []): ?>
|
||||
<section class="card mt-16 dsm-unmapped">
|
||||
<h2 class="section-title">Niezmapowane statusy wykryte w systemie (<?= count($unmappedList) ?>)</h2>
|
||||
<p class="muted mt-8">Statusy odebrane z API przewoźnika <strong><?= $e((string) ($providersList[$currentProvider] ?? $currentProvider)) ?></strong>, dla których nie ma jeszcze mapowania. Przypisz znormalizowany status, aby paczki przestały być oznaczane jako „Nieznany".</p>
|
||||
|
||||
<form action="/settings/delivery-status-mappings/save-bulk" method="post" class="mt-12">
|
||||
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
||||
<input type="hidden" name="provider" value="<?= $e($currentProvider) ?>">
|
||||
|
||||
<div class="table-wrapper">
|
||||
<table class="table table--compact">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Status surowy</th>
|
||||
<th>Liczba paczek</th>
|
||||
<th>Ostatnio widziany</th>
|
||||
<th>Opis (własny)</th>
|
||||
<th>Status znormalizowany</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($unmappedList as $unmapped): ?>
|
||||
<?php
|
||||
$rawStatus = (string) ($unmapped['raw_status'] ?? '');
|
||||
$count = (int) ($unmapped['count'] ?? 0);
|
||||
$lastSeen = (string) ($unmapped['last_seen'] ?? '');
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<code class="dsm-raw-status"><?= $e($rawStatus) ?></code>
|
||||
<input type="hidden" name="raw_status[]" value="<?= $e($rawStatus) ?>">
|
||||
</td>
|
||||
<td><?= $count ?></td>
|
||||
<td><?= $e($lastSeen) ?></td>
|
||||
<td>
|
||||
<input type="text" name="description[]" class="form-control form-control--sm"
|
||||
value="<?= $e($rawStatus) ?>" maxlength="255">
|
||||
</td>
|
||||
<td>
|
||||
<select name="normalized_status[]" class="form-control form-control--sm" required>
|
||||
<?php foreach ($normalizedOptionsList as $optValue => $optLabel): ?>
|
||||
<option value="<?= $e($optValue) ?>"<?= $optValue === 'unknown' ? ' selected' : '' ?>>
|
||||
<?= $e($optLabel) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="form-actions mt-12">
|
||||
<button type="submit" class="btn btn--primary">Dodaj do mapowania</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<section class="card mt-16">
|
||||
<nav class="content-tabs-nav" aria-label="Wybierz przewoźnika">
|
||||
<?php foreach ($providersList as $provKey => $provLabel): ?>
|
||||
<?php
|
||||
$mappingProviderUrl = $mappingBaseUrl . (str_contains($mappingBaseUrl, '?') ? '&' : '?') . 'provider=' . rawurlencode($provKey);
|
||||
?>
|
||||
<a class="content-tab-btn<?= $currentProvider === $provKey ? ' is-active' : '' ?>"
|
||||
href="<?= $e($mappingProviderUrl) ?>">
|
||||
<?= $e($provLabel) ?>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</nav>
|
||||
|
||||
<?php if ($mappingsList === []): ?>
|
||||
<p class="muted mt-16">Brak mapowań dla tego przewoźnika.</p>
|
||||
<?php else: ?>
|
||||
<form id="dsm-bulk-form" action="/settings/delivery-status-mappings/save-bulk" method="post" class="mt-16">
|
||||
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
||||
<input type="hidden" name="provider" value="<?= $e($currentProvider) ?>">
|
||||
|
||||
<div class="table-wrapper">
|
||||
<table class="table table--compact">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Status surowy</th>
|
||||
<th>Opis</th>
|
||||
<th>Status znormalizowany</th>
|
||||
<th>Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($mappingsList as $mapping): ?>
|
||||
<?php
|
||||
$rawStatus = (string) ($mapping['raw_status'] ?? '');
|
||||
$desc = (string) ($mapping['description'] ?? '');
|
||||
$normalized = (string) ($mapping['normalized_status'] ?? '');
|
||||
$isCustom = !empty($mapping['is_custom']);
|
||||
?>
|
||||
<tr class="<?= $isCustom ? 'dsm-row--custom' : '' ?>">
|
||||
<td>
|
||||
<code class="dsm-raw-status"><?= $e($rawStatus) ?></code>
|
||||
<input type="hidden" name="raw_status[]" value="<?= $e($rawStatus) ?>">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="description[]" class="form-control form-control--sm"
|
||||
value="<?= $e($desc) ?>" maxlength="255">
|
||||
</td>
|
||||
<td>
|
||||
<select name="normalized_status[]" class="form-control form-control--sm">
|
||||
<?php foreach ($normalizedOptionsList as $optValue => $optLabel): ?>
|
||||
<option value="<?= $e($optValue) ?>"<?= $optValue === $normalized ? ' selected' : '' ?>>
|
||||
<?= $e($optLabel) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($isCustom): ?>
|
||||
<button type="button" class="btn btn--danger btn--sm js-dsm-reset"
|
||||
data-raw-status="<?= $e($rawStatus) ?>"
|
||||
data-confirm-msg="Przywrócić domyślne mapowanie dla "<?= $e($rawStatus) ?>"?">Resetuj</button>
|
||||
<?php else: ?>
|
||||
<span class="muted">domyślne</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="form-actions mt-16">
|
||||
<button type="submit" class="btn btn--primary">Zapisz wszystkie</button>
|
||||
<button type="button" class="btn btn--danger" id="js-dsm-reset-all">Resetuj wszystkie</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form id="dsm-reset-all-form" action="/settings/delivery-status-mappings/reset-all" method="post" style="display:none;">
|
||||
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
||||
<input type="hidden" name="provider" value="<?= $e($currentProvider) ?>">
|
||||
</form>
|
||||
|
||||
<form id="dsm-reset-form" action="/settings/delivery-status-mappings/reset" method="post" style="display:none;">
|
||||
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
||||
<input type="hidden" name="provider" value="<?= $e($currentProvider) ?>">
|
||||
<input type="hidden" name="raw_status" id="dsm-reset-raw-status" value="">
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var resetForm = document.getElementById('dsm-reset-form');
|
||||
var resetInput = document.getElementById('dsm-reset-raw-status');
|
||||
if (!resetForm || !resetInput) return;
|
||||
|
||||
var resetAllBtn = document.getElementById('js-dsm-reset-all');
|
||||
var resetAllForm = document.getElementById('dsm-reset-all-form');
|
||||
if (resetAllBtn && resetAllForm) {
|
||||
resetAllBtn.addEventListener('click', function() {
|
||||
if (window.OrderProAlerts && typeof window.OrderProAlerts.confirm === 'function') {
|
||||
window.OrderProAlerts.confirm({
|
||||
title: 'Resetuj wszystkie mapowania',
|
||||
message: 'Przywrócić wszystkie mapowania do domyślnych dla tego przewoźnika?',
|
||||
confirmLabel: 'Resetuj wszystkie',
|
||||
cancelLabel: 'Anuluj',
|
||||
danger: true
|
||||
}).then(function(accepted) {
|
||||
if (!accepted) return;
|
||||
resetAllForm.submit();
|
||||
});
|
||||
} else {
|
||||
resetAllForm.submit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('.js-dsm-reset').forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
var rawStatus = btn.getAttribute('data-raw-status') || '';
|
||||
var message = btn.getAttribute('data-confirm-msg') || 'Przywrócić domyślne?';
|
||||
|
||||
if (window.OrderProAlerts && typeof window.OrderProAlerts.confirm === 'function') {
|
||||
window.OrderProAlerts.confirm({
|
||||
title: 'Resetuj mapowanie',
|
||||
message: message,
|
||||
confirmLabel: 'Resetuj',
|
||||
cancelLabel: 'Anuluj',
|
||||
danger: true
|
||||
}).then(function(accepted) {
|
||||
if (!accepted) return;
|
||||
resetInput.value = rawStatus;
|
||||
resetForm.submit();
|
||||
});
|
||||
} else {
|
||||
resetInput.value = rawStatus;
|
||||
resetForm.submit();
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
76
resources/views/settings/delivery-status-form.php
Normal file
76
resources/views/settings/delivery-status-form.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
$row = is_array($row ?? null) ? $row : null;
|
||||
$isEdit = (bool) ($isEdit ?? false);
|
||||
$errorMessage = (string) ($errorMessage ?? '');
|
||||
$csrfToken = (string) ($csrfToken ?? '');
|
||||
|
||||
$rowId = (int) ($row['id'] ?? 0);
|
||||
$rowKey = (string) ($row['key'] ?? '');
|
||||
$rowLabel = (string) ($row['label_pl'] ?? '');
|
||||
$rowColor = (string) ($row['color'] ?? '#6c757d');
|
||||
$rowSortOrder = (int) ($row['sort_order'] ?? 100);
|
||||
$rowIsTerminal = !empty($row['is_terminal']);
|
||||
|
||||
$action = $isEdit
|
||||
? '/settings/delivery-statuses/' . $rowId . '/update'
|
||||
: '/settings/delivery-statuses';
|
||||
?>
|
||||
|
||||
<section class="card">
|
||||
<h2 class="section-title"><?= $isEdit ? 'Edytuj status przesyłki' : 'Nowy status przesyłki' ?></h2>
|
||||
|
||||
<?php if ($errorMessage !== ''): ?>
|
||||
<div class="alert alert--danger mt-12" role="alert"><?= $e($errorMessage) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="<?= $e($action) ?>" method="post" novalidate class="mt-12">
|
||||
<input type="hidden" name="_token" value="<?= $e($csrfToken) ?>">
|
||||
|
||||
<div class="form-grid-2">
|
||||
<label class="form-field">
|
||||
<span class="field-label">Klucz *</span>
|
||||
<?php if ($isEdit): ?>
|
||||
<input class="form-control" type="text" value="<?= $e($rowKey) ?>" disabled>
|
||||
<small class="muted">Klucz nie może być zmieniany po utworzeniu.</small>
|
||||
<?php else: ?>
|
||||
<input class="form-control" type="text" name="key" maxlength="50" required
|
||||
pattern="[a-z][a-z0-9_]{0,49}"
|
||||
placeholder="np. custom_status"
|
||||
value="<?= $e($rowKey) ?>">
|
||||
<small class="muted">Małe litery, cyfry, _, max 50 znaków, zaczyna się literą.</small>
|
||||
<?php endif; ?>
|
||||
</label>
|
||||
|
||||
<label class="form-field">
|
||||
<span class="field-label">Etykieta *</span>
|
||||
<input class="form-control" type="text" name="label_pl" maxlength="100" required
|
||||
placeholder="np. Mój status" value="<?= $e($rowLabel) ?>">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-grid-2 mt-0">
|
||||
<label class="form-field">
|
||||
<span class="field-label">Kolor</span>
|
||||
<input class="form-control" type="color" name="color" value="<?= $e($rowColor) ?>">
|
||||
</label>
|
||||
|
||||
<label class="form-field">
|
||||
<span class="field-label">Kolejność</span>
|
||||
<input class="form-control" type="number" name="sort_order"
|
||||
min="0" max="9999" value="<?= $rowSortOrder ?>">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-grid-2 mt-0">
|
||||
<label class="form-field" style="display:flex;align-items:center;gap:6px;flex-direction:row">
|
||||
<input type="checkbox" name="is_terminal" value="1"<?= $rowIsTerminal ? ' checked' : '' ?>>
|
||||
<span class="field-label" style="margin:0">Końcowy</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-actions mt-16">
|
||||
<button type="submit" class="btn btn--primary"><?= $isEdit ? 'Zapisz zmiany' : 'Utwórz status' ?></button>
|
||||
<a href="/settings/delivery-statuses?tab=statuses" class="btn btn--secondary">Anuluj</a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
@@ -1,220 +1,3 @@
|
||||
<?php
|
||||
$providersList = is_array($providers ?? null) ? $providers : [];
|
||||
$mappingsList = is_array($mappings ?? null) ? $mappings : [];
|
||||
$normalizedOptionsList = is_array($normalizedOptions ?? null) ? $normalizedOptions : [];
|
||||
$unmappedList = is_array($unmappedRawStatuses ?? null) ? $unmappedRawStatuses : [];
|
||||
$currentProvider = (string) ($provider ?? 'inpost');
|
||||
?>
|
||||
|
||||
<section class="card">
|
||||
<h2 class="section-title">Mapowanie statusów dostawy</h2>
|
||||
<p class="muted mt-12">Konfiguracja przypisania surowych statusów z API przewoźników do znormalizowanych statusów w aplikacji.</p>
|
||||
|
||||
<?php if (!empty($errorMessage)): ?>
|
||||
<div class="alert alert--danger mt-12" role="alert"><?= $e((string) $errorMessage) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($successMessage)): ?>
|
||||
<div class="alert alert--success mt-12" role="status"><?= $e((string) $successMessage) ?></div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<?php if ($unmappedList !== []): ?>
|
||||
<section class="card mt-16 dsm-unmapped">
|
||||
<h2 class="section-title">Niezmapowane statusy wykryte w systemie (<?= count($unmappedList) ?>)</h2>
|
||||
<p class="muted mt-8">Statusy odebrane z API przewoźnika <strong><?= $e((string) ($providersList[$currentProvider] ?? $currentProvider)) ?></strong>, dla których nie ma jeszcze mapowania. Przypisz znormalizowany status, aby paczki przestały być oznaczane jako „Nieznany".</p>
|
||||
|
||||
<form action="/settings/delivery-status-mappings/save-bulk" method="post" class="mt-12">
|
||||
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
||||
<input type="hidden" name="provider" value="<?= $e($currentProvider) ?>">
|
||||
|
||||
<div class="table-wrapper">
|
||||
<table class="table table--compact">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Status surowy</th>
|
||||
<th>Liczba paczek</th>
|
||||
<th>Ostatnio widziany</th>
|
||||
<th>Opis (własny)</th>
|
||||
<th>Status znormalizowany</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($unmappedList as $unmapped): ?>
|
||||
<?php
|
||||
$rawStatus = (string) ($unmapped['raw_status'] ?? '');
|
||||
$count = (int) ($unmapped['count'] ?? 0);
|
||||
$lastSeen = (string) ($unmapped['last_seen'] ?? '');
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<code class="dsm-raw-status"><?= $e($rawStatus) ?></code>
|
||||
<input type="hidden" name="raw_status[]" value="<?= $e($rawStatus) ?>">
|
||||
</td>
|
||||
<td><?= $count ?></td>
|
||||
<td><?= $e($lastSeen) ?></td>
|
||||
<td>
|
||||
<input type="text" name="description[]" class="form-control form-control--sm"
|
||||
value="<?= $e($rawStatus) ?>" maxlength="255">
|
||||
</td>
|
||||
<td>
|
||||
<select name="normalized_status[]" class="form-control form-control--sm" required>
|
||||
<?php foreach ($normalizedOptionsList as $optValue => $optLabel): ?>
|
||||
<option value="<?= $e($optValue) ?>"<?= $optValue === 'unknown' ? ' selected' : '' ?>>
|
||||
<?= $e($optLabel) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="form-actions mt-12">
|
||||
<button type="submit" class="btn btn--primary">Dodaj do mapowania</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<section class="card mt-16">
|
||||
<nav class="content-tabs-nav" aria-label="Wybierz przewoźnika">
|
||||
<?php foreach ($providersList as $provKey => $provLabel): ?>
|
||||
<a class="content-tab-btn<?= $currentProvider === $provKey ? ' is-active' : '' ?>"
|
||||
href="/settings/delivery-status-mappings?provider=<?= $e(rawurlencode($provKey)) ?>">
|
||||
<?= $e($provLabel) ?>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</nav>
|
||||
|
||||
<?php if ($mappingsList === []): ?>
|
||||
<p class="muted mt-16">Brak mapowań dla tego przewoźnika.</p>
|
||||
<?php else: ?>
|
||||
<form id="dsm-bulk-form" action="/settings/delivery-status-mappings/save-bulk" method="post" class="mt-16">
|
||||
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
||||
<input type="hidden" name="provider" value="<?= $e($currentProvider) ?>">
|
||||
|
||||
<div class="table-wrapper">
|
||||
<table class="table table--compact">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Status surowy</th>
|
||||
<th>Opis</th>
|
||||
<th>Status znormalizowany</th>
|
||||
<th>Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($mappingsList as $mapping): ?>
|
||||
<?php
|
||||
$rawStatus = (string) ($mapping['raw_status'] ?? '');
|
||||
$desc = (string) ($mapping['description'] ?? '');
|
||||
$normalized = (string) ($mapping['normalized_status'] ?? '');
|
||||
$isCustom = !empty($mapping['is_custom']);
|
||||
?>
|
||||
<tr class="<?= $isCustom ? 'dsm-row--custom' : '' ?>">
|
||||
<td>
|
||||
<code class="dsm-raw-status"><?= $e($rawStatus) ?></code>
|
||||
<input type="hidden" name="raw_status[]" value="<?= $e($rawStatus) ?>">
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="description[]" class="form-control form-control--sm"
|
||||
value="<?= $e($desc) ?>" maxlength="255">
|
||||
</td>
|
||||
<td>
|
||||
<select name="normalized_status[]" class="form-control form-control--sm">
|
||||
<?php foreach ($normalizedOptionsList as $optValue => $optLabel): ?>
|
||||
<option value="<?= $e($optValue) ?>"<?= $optValue === $normalized ? ' selected' : '' ?>>
|
||||
<?= $e($optLabel) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($isCustom): ?>
|
||||
<button type="button" class="btn btn--danger btn--sm js-dsm-reset"
|
||||
data-raw-status="<?= $e($rawStatus) ?>"
|
||||
data-confirm-msg="Przywrócić domyślne mapowanie dla "<?= $e($rawStatus) ?>"?">Resetuj</button>
|
||||
<?php else: ?>
|
||||
<span class="muted">domyślne</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="form-actions mt-16">
|
||||
<button type="submit" class="btn btn--primary">Zapisz wszystkie</button>
|
||||
<button type="button" class="btn btn--danger" id="js-dsm-reset-all">Resetuj wszystkie</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form id="dsm-reset-all-form" action="/settings/delivery-status-mappings/reset-all" method="post" style="display:none;">
|
||||
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
||||
<input type="hidden" name="provider" value="<?= $e($currentProvider) ?>">
|
||||
</form>
|
||||
|
||||
<form id="dsm-reset-form" action="/settings/delivery-status-mappings/reset" method="post" style="display:none;">
|
||||
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
||||
<input type="hidden" name="provider" value="<?= $e($currentProvider) ?>">
|
||||
<input type="hidden" name="raw_status" id="dsm-reset-raw-status" value="">
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var resetForm = document.getElementById('dsm-reset-form');
|
||||
var resetInput = document.getElementById('dsm-reset-raw-status');
|
||||
if (!resetForm || !resetInput) return;
|
||||
|
||||
var resetAllBtn = document.getElementById('js-dsm-reset-all');
|
||||
var resetAllForm = document.getElementById('dsm-reset-all-form');
|
||||
if (resetAllBtn && resetAllForm) {
|
||||
resetAllBtn.addEventListener('click', function() {
|
||||
if (window.OrderProAlerts && typeof window.OrderProAlerts.confirm === 'function') {
|
||||
window.OrderProAlerts.confirm({
|
||||
title: 'Resetuj wszystkie mapowania',
|
||||
message: 'Przywrócić wszystkie mapowania do domyślnych dla tego przewoźnika?',
|
||||
confirmLabel: 'Resetuj wszystkie',
|
||||
cancelLabel: 'Anuluj',
|
||||
danger: true
|
||||
}).then(function(accepted) {
|
||||
if (!accepted) return;
|
||||
resetAllForm.submit();
|
||||
});
|
||||
} else {
|
||||
resetAllForm.submit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('.js-dsm-reset').forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
var rawStatus = btn.getAttribute('data-raw-status') || '';
|
||||
var message = btn.getAttribute('data-confirm-msg') || 'Przywrócić domyślne?';
|
||||
|
||||
if (window.OrderProAlerts && typeof window.OrderProAlerts.confirm === 'function') {
|
||||
window.OrderProAlerts.confirm({
|
||||
title: 'Resetuj mapowanie',
|
||||
message: message,
|
||||
confirmLabel: 'Resetuj',
|
||||
cancelLabel: 'Anuluj',
|
||||
danger: true
|
||||
}).then(function(accepted) {
|
||||
if (!accepted) return;
|
||||
resetInput.value = rawStatus;
|
||||
resetForm.submit();
|
||||
});
|
||||
} else {
|
||||
resetInput.value = rawStatus;
|
||||
resetForm.submit();
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
$mappingBaseUrl = '/settings/delivery-status-mappings';
|
||||
include __DIR__ . '/_delivery-status-mappings-content.php';
|
||||
|
||||
123
resources/views/settings/delivery-statuses.php
Normal file
123
resources/views/settings/delivery-statuses.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
$currentTab = (string) ($tab ?? 'statuses');
|
||||
$statusesList = is_array($statuses ?? null) ? $statuses : [];
|
||||
$errorMessage = (string) ($errorMessage ?? '');
|
||||
$successMessage = (string) ($successMessage ?? '');
|
||||
$csrfToken = (string) ($csrfToken ?? '');
|
||||
?>
|
||||
|
||||
<section class="card">
|
||||
<h2 class="section-title">Statusy przesyłek</h2>
|
||||
<?php if ($errorMessage !== ''): ?>
|
||||
<div class="alert alert--danger mt-12" role="alert"><?= $e($errorMessage) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($successMessage !== ''): ?>
|
||||
<div class="alert alert--success mt-12" role="status"><?= $e($successMessage) ?></div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="card mt-16">
|
||||
<nav class="content-tabs-nav" aria-label="Zakładki">
|
||||
<a class="content-tab-btn<?= $currentTab === 'statuses' ? ' is-active' : '' ?>"
|
||||
href="/settings/delivery-statuses?tab=statuses">Statusy</a>
|
||||
<a class="content-tab-btn<?= $currentTab === 'mapping' ? ' is-active' : '' ?>"
|
||||
href="/settings/delivery-statuses?tab=mapping">Mapowanie dostawy</a>
|
||||
</nav>
|
||||
|
||||
<?php if ($currentTab === 'statuses'): ?>
|
||||
|
||||
<div class="form-actions mt-16">
|
||||
<a href="/settings/delivery-statuses/new" class="btn btn--primary btn--sm">+ Dodaj status</a>
|
||||
</div>
|
||||
|
||||
<div class="table-wrapper mt-12">
|
||||
<table class="table table--compact">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Kolor</th>
|
||||
<th>Klucz</th>
|
||||
<th>Etykieta</th>
|
||||
<th>Kolejność</th>
|
||||
<th>Końcowy</th>
|
||||
<th>Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($statusesList as $row): ?>
|
||||
<?php
|
||||
$rowId = (int) ($row['id'] ?? 0);
|
||||
$rowKey = (string) ($row['key'] ?? '');
|
||||
$rowLabel = (string) ($row['label_pl'] ?? '');
|
||||
$rowColor = (string) ($row['color'] ?? '#6c757d');
|
||||
$rowSortOrder = (int) ($row['sort_order'] ?? 0);
|
||||
$rowIsTerminal = !empty($row['is_terminal']);
|
||||
$rowIsSystem = !empty($row['is_system']);
|
||||
?>
|
||||
<tr>
|
||||
<td>
|
||||
<span class="delivery-status-swatch" style="--status-color: <?= $e($rowColor) ?>"></span>
|
||||
</td>
|
||||
<td><code><?= $e($rowKey) ?></code></td>
|
||||
<td><?= $e($rowLabel) ?></td>
|
||||
<td><?= $rowSortOrder ?></td>
|
||||
<td><?= $rowIsTerminal ? '✓' : '' ?></td>
|
||||
<td>
|
||||
<?php if (!$rowIsSystem): ?>
|
||||
<a href="/settings/delivery-statuses/<?= $rowId ?>/edit"
|
||||
class="btn btn--sm btn--secondary">Edytuj</a>
|
||||
<button type="button"
|
||||
class="btn btn--sm btn--danger js-ds-delete"
|
||||
data-id="<?= $rowId ?>"
|
||||
data-label="<?= $e($rowLabel) ?>">Usuń</button>
|
||||
<form id="ds-delete-<?= $rowId ?>"
|
||||
action="/settings/delivery-statuses/<?= $rowId ?>/delete"
|
||||
method="post" style="display:none;">
|
||||
<input type="hidden" name="_token" value="<?= $e($csrfToken) ?>">
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<span class="muted">—</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<?php
|
||||
$mappingBaseUrl = '/settings/delivery-statuses?tab=mapping';
|
||||
include __DIR__ . '/_delivery-status-mappings-content.php';
|
||||
?>
|
||||
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
// Delete with confirm
|
||||
document.querySelectorAll('.js-ds-delete').forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
var id = btn.getAttribute('data-id');
|
||||
var label = btn.getAttribute('data-label') || '';
|
||||
var form = document.getElementById('ds-delete-' + id);
|
||||
if (!form) return;
|
||||
|
||||
if (window.OrderProAlerts && typeof window.OrderProAlerts.confirm === 'function') {
|
||||
window.OrderProAlerts.confirm({
|
||||
title: 'Usuń status',
|
||||
message: 'Usunąć status "' + label + '"? Operacja jest nieodwracalna.',
|
||||
confirmLabel: 'Usuń',
|
||||
cancelLabel: 'Anuluj',
|
||||
danger: true
|
||||
}).then(function(accepted) {
|
||||
if (accepted) form.submit();
|
||||
});
|
||||
} else {
|
||||
form.submit();
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
@@ -405,7 +405,11 @@ $defaultCodAmount = $isCod ? number_format($totalWithTax, 2, '.', '') : '0';
|
||||
$pkgDeliveryDesc = $pkgDeliveryRaw !== '' ? \App\Modules\Shipments\DeliveryStatus::description($pkgProvider, $pkgDeliveryRaw) : '';
|
||||
$pkgDeliveryTitle = $pkgDeliveryRaw !== '' ? ($pkgDeliveryRaw . ' — ' . $pkgDeliveryDesc) : '';
|
||||
?>
|
||||
<span class="delivery-badge delivery-badge--<?= $e($pkgDeliveryStatus) ?>" title="<?= $e($pkgDeliveryTitle) ?>"><?= $e($pkgDeliveryLabel) ?></span>
|
||||
<?php
|
||||
$pkgIsSystem = in_array($pkgDeliveryStatus, \App\Modules\Shipments\DeliveryStatus::ALL_STATUSES, true);
|
||||
$pkgColor = $pkgIsSystem ? '' : \App\Modules\Shipments\DeliveryStatus::getColor($pkgDeliveryStatus);
|
||||
?>
|
||||
<span class="delivery-badge<?= $pkgIsSystem ? ' delivery-badge--' . $e($pkgDeliveryStatus) : ' delivery-badge--custom' ?>" title="<?= $e($pkgDeliveryTitle) ?>"<?= $pkgColor !== '' ? ' style="--status-color: ' . $e($pkgColor) . '"' : '' ?>><?= $e($pkgDeliveryLabel) ?></span>
|
||||
</td>
|
||||
<td style="white-space:nowrap">
|
||||
<?= $e($pkgTracking !== '' ? $pkgTracking : '-') ?><?php
|
||||
|
||||
Reference in New Issue
Block a user