Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f2b2629d49 |
229
admin/layout/style-css/order-details-mobile.css
Normal file
229
admin/layout/style-css/order-details-mobile.css
Normal file
@@ -0,0 +1,229 @@
|
||||
/* ==========================================================================
|
||||
Order Details — Mobile Responsive
|
||||
Scoped to .od-actions and .order-details
|
||||
All rules inside @media (max-width: 767px) — desktop layout untouched
|
||||
========================================================================== */
|
||||
|
||||
@media (max-width: 767px) {
|
||||
|
||||
/* --- 1. Action buttons bar --- */
|
||||
|
||||
.od-actions {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.od-actions .btn {
|
||||
flex-shrink: 0;
|
||||
font-size: 12px;
|
||||
padding: 6px 10px;
|
||||
margin-right: 0 !important;
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
/* Hide button labels on small screens — show only icons */
|
||||
.od-actions .od-actions-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Integrations dropdown — push to end, cancel float */
|
||||
.od-actions .pull-right,
|
||||
.od-actions .dropdown.pull-right {
|
||||
float: none !important;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* Dropdown menu must not clip inside scroll container */
|
||||
.od-actions .dropdown {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.od-actions + .order-details .dropdown-menu,
|
||||
#integrationsDropdownMenu.show {
|
||||
position: fixed;
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
border-radius: 12px 12px 0 0;
|
||||
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.15);
|
||||
z-index: 1060;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
#integrationsDropdownMenu.show .dropdown-item {
|
||||
padding: 12px 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* --- 2. Full-width columns stacking --- */
|
||||
|
||||
.order-details .col-sm-6,
|
||||
.order-details .col-lg-8,
|
||||
.order-details .col-lg-4,
|
||||
.order-details .col-xl-5,
|
||||
.order-details .col-xl-7 {
|
||||
width: 100%;
|
||||
flex: 0 0 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Spacing between stacked sections */
|
||||
.order-details .col-sm-6 {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
/* "Resend confirmation" button — allow text to wrap */
|
||||
.order-details .resend_order_confirmation_email .btn {
|
||||
font-size: 12px;
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* --- 3. Status section --- */
|
||||
|
||||
.order-details .status_select #order-status {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.order-details .buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.order-details .buttons .btn {
|
||||
width: 100%;
|
||||
white-space: normal;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.order-details .order-status {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.order-details .order-history {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* --- 4. Products table — compact list --- */
|
||||
|
||||
.order-details table thead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.order-details table {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.order-details table tbody {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.order-details table tr.order-product-details {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.order-details table tr.order-product-details:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* Image cell */
|
||||
.order-details table td.product-image {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
flex-shrink: 0;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin-right: 10px;
|
||||
float: none;
|
||||
}
|
||||
|
||||
.order-details table td.product-image img {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* Name cell — takes remaining space next to image */
|
||||
.order-details table tr.order-product-details td:nth-child(2) {
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
border: none;
|
||||
padding: 0;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.order-details table tr.order-product-details td:nth-child(2) a {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
display: block;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* Hide quantity, price, discounted-price, total columns on mobile */
|
||||
.order-details table tr.order-product-details td.tab-center,
|
||||
.order-details table tr.order-product-details td.tab-right {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Show mobile price summary line */
|
||||
.od-mobile-price-line {
|
||||
display: block !important;
|
||||
margin-top: 4px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #2a3042;
|
||||
}
|
||||
|
||||
/* --- 5. Notes --- */
|
||||
|
||||
.order-details #notes {
|
||||
font-size: 14px;
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
/* --- 6. Order summary panel --- */
|
||||
|
||||
.order-details .panel .panel-body {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* --- 7. Paid status --- */
|
||||
|
||||
.order-details .paid-status .panel-body a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* --- 8. Message section — hide empty spacer columns --- */
|
||||
|
||||
.order-details .d-none.d-lg-block {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Desktop: action bar spacing (replaces mr5 class) --- */
|
||||
.od-actions .btn + .btn,
|
||||
.od-actions .btn + .dropdown {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
/* Mobile price line — hidden on desktop */
|
||||
.od-mobile-price-line {
|
||||
display: none;
|
||||
}
|
||||
@@ -57,6 +57,36 @@
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* --- Filter toggle --- */
|
||||
|
||||
.table-filters-wrapper {
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table-filters-wrapper.open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.js-filter-toggle-btn.active {
|
||||
background-color: #e8e8e8;
|
||||
border-color: #adadad;
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
|
||||
.table-filter-badge {
|
||||
display: inline-block;
|
||||
min-width: 16px;
|
||||
padding: 2px 5px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
color: #fff;
|
||||
background-color: #337ab7;
|
||||
border-radius: 10px;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
/* --- Column visibility toggle --- */
|
||||
|
||||
.table-list-header-actions {
|
||||
|
||||
@@ -19,6 +19,14 @@ $totalPages = max(1, (int)($list->pagination['total_pages'] ?? 1));
|
||||
$total = (int)($list->pagination['total'] ?? 0);
|
||||
$perPage = (int)($list->pagination['per_page'] ?? 15);
|
||||
|
||||
$hasActiveFilters = false;
|
||||
foreach ($list->filters as $filter) {
|
||||
if (isset($filter['value']) && (string)$filter['value'] !== '') {
|
||||
$hasActiveFilters = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$isCompactColumn = function(array $column): bool {
|
||||
$key = strtolower(trim((string)($column['key'] ?? '')));
|
||||
$label = strtolower(trim((string)($column['label'] ?? '')));
|
||||
@@ -48,6 +56,14 @@ $isCompactColumn = function(array $column): bool {
|
||||
<div class="col-sm-4 text-right">
|
||||
<div class="table-list-header-actions">
|
||||
<span class="text-muted">Wyników: <?= $total; ?></span>
|
||||
<?php if (!empty($list->filters)): ?>
|
||||
<button type="button" class="btn btn-default btn-sm js-filter-toggle-btn<?= $hasActiveFilters ? ' active' : ''; ?>" title="Filtry">
|
||||
<i class="fa fa-filter"></i>
|
||||
<?php if ($hasActiveFilters): ?>
|
||||
<span class="badge badge-primary table-filter-badge"><?= count(array_filter($list->filters, function($f) { return isset($f['value']) && (string)$f['value'] !== ''; })); ?></span>
|
||||
<?php endif; ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<div class="table-col-toggle-wrapper">
|
||||
<button type="button" class="btn btn-default btn-sm js-col-toggle-btn" title="Widoczność kolumn">
|
||||
<i class="fa fa-columns"></i>
|
||||
@@ -75,6 +91,7 @@ $isCompactColumn = function(array $column): bool {
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<div class="js-table-filters-wrapper table-filters-wrapper<?= $hasActiveFilters ? ' open' : ''; ?>">
|
||||
<form method="get" action="<?= htmlspecialchars($list->basePath, ENT_QUOTES, 'UTF-8'); ?>" class="row mb15 js-table-filters-form">
|
||||
<?php foreach ($list->filters as $filter): ?>
|
||||
<?php
|
||||
@@ -148,6 +165,7 @@ $isCompactColumn = function(array $column): bool {
|
||||
<a href="<?= htmlspecialchars($list->basePath, ENT_QUOTES, 'UTF-8'); ?>" class="btn btn-default btn-sm">Wyczyść</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped table-bordered mbn table-list-table">
|
||||
@@ -469,5 +487,47 @@ $isCompactColumn = function(array $column): bool {
|
||||
saveHiddenCols([]);
|
||||
applyColumnVisibility([]);
|
||||
});
|
||||
|
||||
// --- Filter toggle ---
|
||||
var filterStorageKey = 'tableListFilters_' + <?= json_encode($list->basePath); ?>;
|
||||
|
||||
function isFilterVisible() {
|
||||
try {
|
||||
return localStorage.getItem(filterStorageKey) === '1';
|
||||
} catch (e) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
function saveFilterState(visible) {
|
||||
try {
|
||||
localStorage.setItem(filterStorageKey, visible ? '1' : '0');
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
var $filterWrapper = $('.js-table-filters-wrapper');
|
||||
var $filterBtn = $('.js-filter-toggle-btn');
|
||||
var hasActiveFilters = $filterWrapper.hasClass('open');
|
||||
|
||||
if (!hasActiveFilters && isFilterVisible()) {
|
||||
$filterWrapper.addClass('open');
|
||||
$filterBtn.addClass('active');
|
||||
}
|
||||
|
||||
$(document).off('click.filterToggle', '.js-filter-toggle-btn');
|
||||
$(document).on('click.filterToggle', '.js-filter-toggle-btn', function() {
|
||||
var $wrapper = $('.js-table-filters-wrapper');
|
||||
var $btn = $(this);
|
||||
var isOpen = $wrapper.hasClass('open');
|
||||
|
||||
if (isOpen) {
|
||||
$wrapper.removeClass('open');
|
||||
$btn.removeClass('active');
|
||||
saveFilterState(false);
|
||||
} else {
|
||||
$wrapper.addClass('open');
|
||||
$btn.addClass('active');
|
||||
saveFilterState(true);
|
||||
}
|
||||
});
|
||||
})(window.jQuery);
|
||||
</script>
|
||||
|
||||
@@ -4,23 +4,23 @@ $orderId = (int)($this -> order['id'] ?? 0);
|
||||
|
||||
<div class="site-title">Szczegóły zamówienia: <?= htmlspecialchars((string)($this -> order['number'] ?? ''), ENT_QUOTES, 'UTF-8');?></div>
|
||||
|
||||
<div class="mb15">
|
||||
<a href="/admin/shop_order/list/" class="btn btn-dark btn-sm mr5">
|
||||
<i class="fa fa-reply"></i> Wstecz
|
||||
<div class="od-actions mb15">
|
||||
<a href="/admin/shop_order/list/" class="btn btn-dark btn-sm">
|
||||
<i class="fa fa-reply"></i> <span class="od-actions-label">Wstecz</span>
|
||||
</a>
|
||||
<a href="/admin/shop_order/order_edit/order_id=<?= $orderId;?>" class="btn btn-danger btn-sm mr5">
|
||||
<i class="fa fa-pencil"></i> Edytuj zamówienie
|
||||
<a href="/admin/shop_order/order_edit/order_id=<?= $orderId;?>" class="btn btn-danger btn-sm">
|
||||
<i class="fa fa-pencil"></i> <span class="od-actions-label">Edytuj</span>
|
||||
</a>
|
||||
|
||||
<? if ( $this -> prev_order_id ):?>
|
||||
<a href="/admin/shop_order/order_details/order_id=<?= (int)$this -> prev_order_id;?>" class="btn btn-success btn-sm mr5">
|
||||
<i class="fa fa-arrow-left"></i> Poprzednie zamówienie
|
||||
<a href="/admin/shop_order/order_details/order_id=<?= (int)$this -> prev_order_id;?>" class="btn btn-success btn-sm">
|
||||
<i class="fa fa-arrow-left"></i> <span class="od-actions-label">Poprzednie</span>
|
||||
</a>
|
||||
<? endif;?>
|
||||
|
||||
<? if ( $this -> next_order_id ):?>
|
||||
<a href="/admin/shop_order/order_details/order_id=<?= (int)$this -> next_order_id;?>" class="btn btn-success btn-sm mr5">
|
||||
<i class="fa fa-arrow-right"></i> Następne zamówienie
|
||||
<a href="/admin/shop_order/order_details/order_id=<?= (int)$this -> next_order_id;?>" class="btn btn-success btn-sm">
|
||||
<i class="fa fa-arrow-right"></i> <span class="od-actions-label">Następne</span>
|
||||
</a>
|
||||
<? endif;?>
|
||||
|
||||
@@ -183,6 +183,9 @@ $orderId = (int)($this -> order['id'] ?? 0);
|
||||
<div class="product-message">
|
||||
<?= $product[ 'message' ] != '' ? '<strong>Wiadomość:</strong> ' . $product['message'] : '';?>
|
||||
</div>
|
||||
<div class="od-mobile-price-line">
|
||||
<?= (int)$product['quantity'];?> × <?= \Shared\Helpers\Helpers::decimal( $product['price_brutto_promo'] );?> = <?= \Shared\Helpers\Helpers::decimal( $product['price_brutto_promo'] * $product['quantity'] );?> zł
|
||||
</div>
|
||||
</td>
|
||||
<td class="tab-center"><?= $product[ 'quantity' ];?></td>
|
||||
<td class="tab-right"><?= \Shared\Helpers\Helpers::decimal( $product[ 'price_brutto' ] );?> zł</td>
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<script type="text/javascript" src="/admin/js/functions.js"></script>
|
||||
<link rel="stylesheet" href="/admin/layout/style-css/style.css" />
|
||||
<link rel="stylesheet" href="/admin/layout/style-css/table-list.css" />
|
||||
<link rel="stylesheet" href="/admin/layout/style-css/order-details-mobile.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="admin-page">
|
||||
|
||||
@@ -4,6 +4,18 @@ Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze.
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.301 (2026-02-22) - Mobile responsive: filtry tabel i szczegoly zamowienia
|
||||
|
||||
- **NEW**: Filtry w tabelach admina domyslnie ukryte — przycisk toggle z ikona filtra, badge z liczba aktywnych filtrow
|
||||
- **NEW**: Stan filtrow zapamietywany w `localStorage` per widok; auto-show gdy filtry aktywne
|
||||
- **NEW**: Mobilna wersja szczegolow zamowienia — responsywny layout (CSS-only, breakpoint 767px)
|
||||
- **NEW**: Pasek akcji zamowienia — ikony-only na mobile, flex row bez zawijania
|
||||
- **NEW**: Tabela produktow zamowienia — kompaktowa lista na mobile (obraz + nazwa + linia `qty x cena = suma`)
|
||||
- **NEW**: Sekcje informacyjne zamowienia — full-width stacking na mobile
|
||||
- **NEW**: Dropdown integracji jako bottom-sheet na mobile
|
||||
|
||||
---
|
||||
|
||||
## ver. 0.300 (2026-02-21) - System aktualizacji oparty na manifestach JSON
|
||||
|
||||
- **NEW**: Manifest JSON per wersja — zastępuje osobne pliki `_sql.txt` i `_files.txt`
|
||||
|
||||
@@ -101,13 +101,13 @@ Aktualizacje znajdują się w folderze `updates/0.XX/` gdzie XX oznacza dziesią
|
||||
|
||||
**WAŻNE:** W archiwum ZIP NIE powinno być folderu z nazwą wersji. Struktura ZIP zaczyna się bezpośrednio od katalogów projektu (admin/, autoload/, itp.).
|
||||
|
||||
## Status bieżącej aktualizacji (ver. 0.300)
|
||||
## Status bieżącej aktualizacji (ver. 0.301)
|
||||
|
||||
- Wersja udostępniona: `0.300` (data: 2026-02-21).
|
||||
- Wersja udostępniona: `0.301` (data: 2026-02-22).
|
||||
- Pliki publikacyjne:
|
||||
- `updates/0.30/ver_0.300.zip`
|
||||
- `updates/0.30/ver_0.301.zip`
|
||||
- Pliki metadanych aktualizacji:
|
||||
- `updates/changelog.php`
|
||||
- `updates/versions.php` (`$current_ver = 300`)
|
||||
- `updates/versions.php` (`$current_ver = 301`)
|
||||
- Weryfikacja testów przed publikacją:
|
||||
- `OK (692 tests, 1988 assertions)`
|
||||
|
||||
Reference in New Issue
Block a user