- Implemented OrdersRepository for handling order data with pagination, filtering, and sorting capabilities. - Added methods for retrieving order status options, quick stats, and detailed order information. - Created OrderStatusRepository for managing order status groups and statuses, including CRUD operations and sorting. - Introduced a bootstrap file for test environment setup and autoloading.
342 lines
17 KiB
PHP
342 lines
17 KiB
PHP
<?php
|
|
$groupsList = is_array($groups ?? null) ? $groups : [];
|
|
$statusesList = is_array($statuses ?? null) ? $statuses : [];
|
|
$statusesByGroup = [];
|
|
foreach ($groupsList as $groupRow) {
|
|
$groupId = max(0, (int) ($groupRow['id'] ?? 0));
|
|
if ($groupId <= 0) {
|
|
continue;
|
|
}
|
|
$statusesByGroup[$groupId] = [];
|
|
}
|
|
foreach ($statusesList as $statusRow) {
|
|
$groupId = max(0, (int) ($statusRow['group_id'] ?? 0));
|
|
if ($groupId <= 0) {
|
|
continue;
|
|
}
|
|
if (!isset($statusesByGroup[$groupId])) {
|
|
$statusesByGroup[$groupId] = [];
|
|
}
|
|
$statusesByGroup[$groupId][] = $statusRow;
|
|
}
|
|
?>
|
|
|
|
<section class="card">
|
|
<h2 class="section-title"><?= $e($t('settings.statuses.title')) ?></h2>
|
|
<p class="muted mt-12"><?= $e($t('settings.statuses.description')) ?></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>
|
|
|
|
<section class="card mt-16">
|
|
<nav class="content-tabs-nav" aria-label="<?= $e($t('settings.statuses.tabs.label')) ?>">
|
|
<button type="button" class="content-tab-btn is-active" data-tab-target="statuses-tab">
|
|
<?= $e($t('settings.statuses.tabs.statuses')) ?>
|
|
</button>
|
|
<button type="button" class="content-tab-btn" data-tab-target="groups-tab">
|
|
<?= $e($t('settings.statuses.tabs.groups')) ?>
|
|
</button>
|
|
</nav>
|
|
|
|
<div class="content-tab-panel is-active" data-tab-panel="statuses-tab">
|
|
<h2 class="section-title"><?= $e($t('settings.statuses.statuses.create_title')) ?></h2>
|
|
<form class="statuses-form mt-16" action="/settings/statuses/create" method="post">
|
|
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
|
<label class="form-field">
|
|
<span class="field-label"><?= $e($t('settings.statuses.fields.group')) ?></span>
|
|
<select name="group_id" class="form-control" required>
|
|
<option value=""><?= $e($t('settings.statuses.fields.group_placeholder')) ?></option>
|
|
<?php foreach ($groupsList as $group): ?>
|
|
<?php $groupId = max(0, (int) ($group['id'] ?? 0)); ?>
|
|
<?php if ($groupId <= 0) continue; ?>
|
|
<option value="<?= $e((string) $groupId) ?>"><?= $e((string) ($group['name'] ?? ('#' . $groupId))) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</label>
|
|
<label class="form-field">
|
|
<span class="field-label"><?= $e($t('settings.statuses.fields.name')) ?></span>
|
|
<input type="text" name="name" class="form-control" maxlength="120" required>
|
|
</label>
|
|
<label class="field-inline">
|
|
<input type="hidden" name="is_active" value="0">
|
|
<input type="checkbox" name="is_active" value="1" checked>
|
|
<span><?= $e($t('settings.statuses.fields.is_active')) ?></span>
|
|
</label>
|
|
<p class="muted statuses-hint"><?= $e($t('settings.statuses.hints.code_auto')) ?></p>
|
|
<div class="form-actions">
|
|
<button type="submit" class="btn btn--primary"><?= $e($t('settings.statuses.actions.add_status')) ?></button>
|
|
</div>
|
|
</form>
|
|
|
|
<h2 class="section-title mt-16"><?= $e($t('settings.statuses.statuses.list_title')) ?></h2>
|
|
<?php if ($groupsList === []): ?>
|
|
<div class="alert alert--warning mt-12"><?= $e($t('settings.statuses.groups.empty')) ?></div>
|
|
<?php endif; ?>
|
|
|
|
<?php foreach ($groupsList as $group): ?>
|
|
<?php
|
|
$groupId = max(0, (int) ($group['id'] ?? 0));
|
|
if ($groupId <= 0) {
|
|
continue;
|
|
}
|
|
$groupName = (string) ($group['name'] ?? ('#' . $groupId));
|
|
$groupColor = (string) ($group['color_hex'] ?? '#64748b');
|
|
$groupStatuses = is_array($statusesByGroup[$groupId] ?? null) ? $statusesByGroup[$groupId] : [];
|
|
?>
|
|
<article class="statuses-group-block mt-16">
|
|
<header class="statuses-group-block__head">
|
|
<h3 class="statuses-group-block__title">
|
|
<span class="statuses-color-dot" style="background: <?= $e($groupColor) ?>;"></span>
|
|
<?= $e($groupName) ?>
|
|
</h3>
|
|
<span class="muted"><?= $e($t('settings.statuses.hints.drag_statuses')) ?> <?= $e($t('settings.statuses.hints.auto_save_order')) ?></span>
|
|
</header>
|
|
|
|
<ul class="statuses-dnd-list js-dnd-list" data-order-scope="statuses-<?= $e((string) $groupId) ?>">
|
|
<?php foreach ($groupStatuses as $status): ?>
|
|
<?php $statusId = max(0, (int) ($status['id'] ?? 0)); ?>
|
|
<?php if ($statusId <= 0) continue; ?>
|
|
<li class="statuses-dnd-item" draggable="true" data-id="<?= $e((string) $statusId) ?>">
|
|
<div class="statuses-dnd-item__drag" title="<?= $e($t('settings.statuses.hints.drag_handle')) ?>">::</div>
|
|
<div class="statuses-dnd-item__content">
|
|
<form action="/settings/statuses/update" method="post" class="statuses-inline-form statuses-inline-form--row">
|
|
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
|
<input type="hidden" name="status_id" value="<?= $e((string) $statusId) ?>">
|
|
<input type="text" name="name" class="form-control" maxlength="120" required value="<?= $e((string) ($status['name'] ?? '')) ?>" aria-label="<?= $e($t('settings.statuses.fields.name')) ?>">
|
|
<select name="group_id" class="form-control" required aria-label="<?= $e($t('settings.statuses.fields.group')) ?>">
|
|
<?php foreach ($groupsList as $groupOption): ?>
|
|
<?php $groupOptionId = max(0, (int) ($groupOption['id'] ?? 0)); ?>
|
|
<?php if ($groupOptionId <= 0) continue; ?>
|
|
<option value="<?= $e((string) $groupOptionId) ?>"<?= $groupOptionId === (int) ($status['group_id'] ?? 0) ? ' selected' : '' ?>>
|
|
<?= $e((string) ($groupOption['name'] ?? ('#' . $groupOptionId))) ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<label class="field-inline statuses-inline-check">
|
|
<input type="hidden" name="is_active" value="0">
|
|
<input type="checkbox" name="is_active" value="1"<?= ((int) ($status['is_active'] ?? 0)) === 1 ? ' checked' : '' ?>>
|
|
<span><?= $e($t('settings.statuses.fields.is_active')) ?></span>
|
|
</label>
|
|
<div class="statuses-code-readonly">
|
|
<span class="statuses-code-label"><?= $e($t('settings.statuses.fields.code')) ?>:</span>
|
|
<code><?= $e((string) ($status['code'] ?? '')) ?></code>
|
|
</div>
|
|
<button type="submit" class="btn btn--secondary"><?= $e($t('settings.statuses.actions.save')) ?></button>
|
|
</form>
|
|
<form action="/settings/statuses/delete" method="post" class="table-inline-action statuses-inline-delete" data-alert-confirm="<?= $e($t('settings.statuses.confirm.delete_status')) ?>" data-alert-confirm-title="<?= $e($t('settings.statuses.confirm.title')) ?>" data-alert-confirm-yes="<?= $e($t('settings.statuses.confirm.confirm')) ?>" data-alert-confirm-no="<?= $e($t('settings.statuses.confirm.cancel')) ?>">
|
|
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
|
<input type="hidden" name="status_id" value="<?= $e((string) $statusId) ?>">
|
|
<button type="submit" class="btn btn--danger"><?= $e($t('settings.statuses.actions.delete')) ?></button>
|
|
</form>
|
|
</div>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<?php if ($groupStatuses === []): ?>
|
|
<p class="muted mt-12"><?= $e($t('settings.statuses.statuses.empty')) ?></p>
|
|
<?php endif; ?>
|
|
<?php if ($groupStatuses !== []): ?>
|
|
<form action="/settings/statuses/reorder" method="post" class="statuses-reorder-form js-reorder-form mt-12" data-order-scope="statuses-<?= $e((string) $groupId) ?>">
|
|
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
|
<input type="hidden" name="group_id" value="<?= $e((string) $groupId) ?>">
|
|
<div class="js-order-inputs"></div>
|
|
</form>
|
|
<?php endif; ?>
|
|
</article>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<div class="content-tab-panel" data-tab-panel="groups-tab">
|
|
<h2 class="section-title"><?= $e($t('settings.statuses.groups.create_title')) ?></h2>
|
|
<form class="statuses-form mt-16" action="/settings/status-groups" method="post">
|
|
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
|
<label class="form-field">
|
|
<span class="field-label"><?= $e($t('settings.statuses.fields.name')) ?></span>
|
|
<input type="text" name="name" class="form-control" maxlength="120" required>
|
|
</label>
|
|
<label class="form-field">
|
|
<span class="field-label"><?= $e($t('settings.statuses.fields.color')) ?></span>
|
|
<input type="color" name="color_hex" class="form-control statuses-color-input" value="#64748b">
|
|
</label>
|
|
<label class="field-inline">
|
|
<input type="hidden" name="is_active" value="0">
|
|
<input type="checkbox" name="is_active" value="1" checked>
|
|
<span><?= $e($t('settings.statuses.fields.is_active')) ?></span>
|
|
</label>
|
|
<p class="muted statuses-hint"><?= $e($t('settings.statuses.hints.code_auto')) ?></p>
|
|
<div class="form-actions">
|
|
<button type="submit" class="btn btn--primary"><?= $e($t('settings.statuses.actions.add_group')) ?></button>
|
|
</div>
|
|
</form>
|
|
|
|
<h2 class="section-title mt-16"><?= $e($t('settings.statuses.groups.list_title')) ?></h2>
|
|
<p class="muted mt-12"><?= $e($t('settings.statuses.hints.drag_groups')) ?> <?= $e($t('settings.statuses.hints.auto_save_order')) ?></p>
|
|
<ul class="statuses-dnd-list js-dnd-list mt-12" data-order-scope="groups">
|
|
<?php foreach ($groupsList as $group): ?>
|
|
<?php
|
|
$groupId = max(0, (int) ($group['id'] ?? 0));
|
|
if ($groupId <= 0) {
|
|
continue;
|
|
}
|
|
?>
|
|
<li class="statuses-dnd-item" draggable="true" data-id="<?= $e((string) $groupId) ?>">
|
|
<div class="statuses-dnd-item__drag" title="<?= $e($t('settings.statuses.hints.drag_handle')) ?>">::</div>
|
|
<div class="statuses-dnd-item__content">
|
|
<form action="/settings/status-groups/update" method="post" class="statuses-inline-form statuses-inline-form--row-group">
|
|
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
|
<input type="hidden" name="group_id" value="<?= $e((string) $groupId) ?>">
|
|
<input type="text" name="name" class="form-control" maxlength="120" required value="<?= $e((string) ($group['name'] ?? '')) ?>" aria-label="<?= $e($t('settings.statuses.fields.name')) ?>">
|
|
<input type="color" name="color_hex" class="form-control statuses-color-input" value="<?= $e((string) ($group['color_hex'] ?? '#64748b')) ?>" aria-label="<?= $e($t('settings.statuses.fields.color')) ?>">
|
|
<label class="field-inline statuses-inline-check">
|
|
<input type="hidden" name="is_active" value="0">
|
|
<input type="checkbox" name="is_active" value="1"<?= ((int) ($group['is_active'] ?? 0)) === 1 ? ' checked' : '' ?>>
|
|
<span><?= $e($t('settings.statuses.fields.is_active')) ?></span>
|
|
</label>
|
|
<div class="statuses-code-readonly">
|
|
<span class="statuses-code-label"><?= $e($t('settings.statuses.fields.code')) ?>:</span>
|
|
<code><?= $e((string) ($group['code'] ?? '')) ?></code>
|
|
</div>
|
|
<button type="submit" class="btn btn--secondary"><?= $e($t('settings.statuses.actions.save')) ?></button>
|
|
</form>
|
|
<form action="/settings/status-groups/delete" method="post" class="table-inline-action statuses-inline-delete" data-alert-confirm="<?= $e($t('settings.statuses.confirm.delete_group')) ?>" data-alert-confirm-title="<?= $e($t('settings.statuses.confirm.title')) ?>" data-alert-confirm-yes="<?= $e($t('settings.statuses.confirm.confirm')) ?>" data-alert-confirm-no="<?= $e($t('settings.statuses.confirm.cancel')) ?>">
|
|
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
|
<input type="hidden" name="group_id" value="<?= $e((string) $groupId) ?>">
|
|
<button type="submit" class="btn btn--danger"><?= $e($t('settings.statuses.actions.delete')) ?></button>
|
|
</form>
|
|
</div>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<?php if ($groupsList === []): ?>
|
|
<p class="muted mt-12"><?= $e($t('settings.statuses.groups.empty')) ?></p>
|
|
<?php endif; ?>
|
|
<?php if ($groupsList !== []): ?>
|
|
<form action="/settings/status-groups/reorder" method="post" class="statuses-reorder-form js-reorder-form mt-12" data-order-scope="groups">
|
|
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
|
<div class="js-order-inputs"></div>
|
|
</form>
|
|
<?php endif; ?>
|
|
</div>
|
|
</section>
|
|
|
|
<script>
|
|
(function() {
|
|
var tabStorageKey = 'settings.statuses.active_tab';
|
|
var defaultTab = 'statuses-tab';
|
|
|
|
function activateTab(target) {
|
|
var normalizedTarget = (target === 'groups-tab' || target === 'statuses-tab') ? target : defaultTab;
|
|
tabButtons.forEach(function(btn) { btn.classList.remove('is-active'); });
|
|
tabPanels.forEach(function(panel) { panel.classList.remove('is-active'); });
|
|
|
|
var activeButton = document.querySelector('[data-tab-target="' + normalizedTarget + '"]');
|
|
var activePanel = document.querySelector('[data-tab-panel="' + normalizedTarget + '"]');
|
|
if (activeButton) activeButton.classList.add('is-active');
|
|
if (activePanel) activePanel.classList.add('is-active');
|
|
}
|
|
|
|
var tabButtons = document.querySelectorAll('[data-tab-target]');
|
|
var tabPanels = document.querySelectorAll('[data-tab-panel]');
|
|
try {
|
|
activateTab(localStorage.getItem(tabStorageKey) || defaultTab);
|
|
} catch (e) {
|
|
activateTab(defaultTab);
|
|
}
|
|
|
|
tabButtons.forEach(function(button) {
|
|
button.addEventListener('click', function() {
|
|
var target = button.getAttribute('data-tab-target');
|
|
activateTab(target);
|
|
try {
|
|
localStorage.setItem(tabStorageKey, target || defaultTab);
|
|
} catch (e) {}
|
|
});
|
|
});
|
|
|
|
function initDndList(list) {
|
|
var dragged = null;
|
|
var startIndex = -1;
|
|
|
|
function submitReorderForScope(scope) {
|
|
if (scope === '') return;
|
|
var form = document.querySelector('.js-reorder-form[data-order-scope="' + scope + '"]');
|
|
if (!form || form.getAttribute('data-submitting') === '1') return;
|
|
var sourceList = document.querySelector('.js-dnd-list[data-order-scope="' + scope + '"]');
|
|
if (!sourceList) return;
|
|
var holder = form.querySelector('.js-order-inputs');
|
|
if (!holder) return;
|
|
holder.innerHTML = '';
|
|
sourceList.querySelectorAll('.statuses-dnd-item').forEach(function(item) {
|
|
var id = item.getAttribute('data-id') || '';
|
|
if (id === '') return;
|
|
var input = document.createElement('input');
|
|
input.type = 'hidden';
|
|
input.name = 'order[]';
|
|
input.value = id;
|
|
holder.appendChild(input);
|
|
});
|
|
form.setAttribute('data-submitting', '1');
|
|
form.submit();
|
|
}
|
|
|
|
list.querySelectorAll('.statuses-dnd-item').forEach(function(item) {
|
|
item.addEventListener('dragstart', function() {
|
|
dragged = item;
|
|
startIndex = Array.prototype.indexOf.call(list.children, item);
|
|
item.classList.add('is-dragging');
|
|
});
|
|
item.addEventListener('dragend', function() {
|
|
item.classList.remove('is-dragging');
|
|
var endIndex = Array.prototype.indexOf.call(list.children, item);
|
|
var moved = startIndex >= 0 && endIndex >= 0 && startIndex !== endIndex;
|
|
var scope = list.getAttribute('data-order-scope') || '';
|
|
dragged = null;
|
|
startIndex = -1;
|
|
if (moved) {
|
|
submitReorderForScope(scope);
|
|
}
|
|
});
|
|
item.addEventListener('dragover', function(event) {
|
|
event.preventDefault();
|
|
if (!dragged || dragged === item) return;
|
|
var rect = item.getBoundingClientRect();
|
|
var before = event.clientY < rect.top + rect.height / 2;
|
|
list.insertBefore(dragged, before ? item : item.nextSibling);
|
|
});
|
|
});
|
|
}
|
|
|
|
document.querySelectorAll('.js-dnd-list').forEach(initDndList);
|
|
|
|
document.addEventListener('submit', function(event) {
|
|
var form = event.target;
|
|
if (!form || !form.matches || !form.matches('form[data-alert-confirm]')) return;
|
|
if (form.getAttribute('data-confirmed') === '1') {
|
|
form.removeAttribute('data-confirmed');
|
|
return;
|
|
}
|
|
var message = form.getAttribute('data-alert-confirm') || '';
|
|
if (message === '') return;
|
|
if (!window.OrderProAlerts || typeof window.OrderProAlerts.confirm !== 'function') return;
|
|
event.preventDefault();
|
|
window.OrderProAlerts.confirm({
|
|
title: form.getAttribute('data-alert-confirm-title') || 'Potwierdzenie',
|
|
message: message,
|
|
confirmLabel: form.getAttribute('data-alert-confirm-yes') || 'Potwierdz',
|
|
cancelLabel: form.getAttribute('data-alert-confirm-no') || 'Anuluj',
|
|
danger: true
|
|
}).then(function(accepted) {
|
|
if (!accepted) return;
|
|
form.setAttribute('data-confirmed', '1');
|
|
form.submit();
|
|
});
|
|
});
|
|
})();
|
|
</script>
|