feat: cache-bust assets, fix XSS and filemtime error handling, clean up users filters
- Add ?ver=<filemtime> cache-busting to CSS/JS assets in app and auth layouts
- Use ?: 0 fallback on filemtime() to prevent E_WARNING when files are missing
- Escape security_information output with $e() to fix XSS vulnerability (show.php:91)
- Wrap product image metadata in __meta container, move storage path into <details>
- Add table--details CSS class and th { white-space: nowrap } rule
- Remove redundant sort, sort_dir, per_page filter dropdowns from users list
- Add 10 as a per-page option in users list
- Clean up completed items from TODO.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,4 @@
|
||||
1. Na podglądzie produktu zmień wyświetlanie zdjęć na siatkę (grid)
|
||||
2. W tabelach w filtrach filtr Na strone nie jest potrzebny bo ta opcja jest na dole w stronicowaniu.
|
||||
3. W tabelach w sortowanie nie jest potrzebny bo ta opcja jest dostępna w nagłowkach tabel.
|
||||
4. https://orderpro.projectpro.pl/products/8 rozszerzyć kolumnę z nazwami parametrów
|
||||
5. Rozbudować dane o producencie o pola z shopPRO
|
||||
6. ~~https://orderpro.projectpro.pl/products dodać kolumnę z EAN~~
|
||||
7. https://orderpro.projectpro.pl/products domyślnie sortowanie po dacie dodanie DESC
|
||||
8. Edytor opisów WYSWIG
|
||||
9. Opisy tytuly dla każdej z integracji osobno
|
||||
10. Dla integracji shopPRO możliwość przypisania do kategorii (pobierane w locie przez API)
|
||||
11. Nowa zakładka ze stanami magazynowyi z inputami do szybkiego wpisania aktualnego stanu magazynowego
|
||||
|
||||
@@ -1 +1 @@
|
||||
:root{--c-primary: #6690f4;--c-primary-dark: #3164db;--c-bg: #f4f6f9;--c-surface: #ffffff;--c-text: #4e5e6a;--c-text-strong: #2d3748;--c-muted: #718096;--c-border: #e2e8f0;--c-danger: #cc0000;--focus-ring: 0 0 0 3px rgba(102, 144, 244, 0.15);--shadow-card: 0 1px 4px rgba(0, 0, 0, 0.06)}.btn{display:inline-flex;align-items:center;justify-content:center;min-height:38px;padding:8px 16px;border:1px solid rgba(0,0,0,0);border-radius:8px;font:inherit;font-weight:600;text-decoration:none;cursor:pointer;transition:background-color .2s ease,border-color .2s ease,color .2s ease,transform .1s ease}.btn--primary{color:#fff;background:var(--c-primary)}.btn--primary:hover{background:var(--c-primary-dark)}.btn--secondary{color:var(--c-text-strong);border-color:var(--c-border);background:var(--c-surface)}.btn--secondary:hover{border-color:#cbd5e0;background:#f8fafc}.btn--danger{color:#fff;border-color:#b91c1c;background:#dc2626}.btn--danger:hover{border-color:#991b1b;background:#b91c1c}.btn--block{width:100%}.btn:active{transform:translateY(1px)}.btn:focus-visible{outline:none;box-shadow:var(--focus-ring);border-color:var(--c-primary)}.form-control{width:100%;min-height:38px;border:1px solid var(--c-border);border-radius:8px;padding:7px 12px;font:inherit;color:var(--c-text-strong);background:#fff;transition:border-color .2s ease,box-shadow .2s ease}.form-control:focus{outline:none;border-color:var(--c-primary);box-shadow:var(--focus-ring)}.alert{padding:12px 14px;border-radius:8px;border:1px solid rgba(0,0,0,0);font-size:13px;min-height:44px}.alert--danger{border-color:#fed7d7;background:#fff5f5;color:var(--c-danger)}.alert--success{border-color:#b7ebcf;background:#f0fff6;color:#0f6b39}.alert--warning{border-color:#f7dd8b;background:#fff8e8;color:#815500}.form-field{display:grid;gap:7px}.field-label{color:var(--c-text-strong);font-size:13px;font-weight:600}.table-wrap{width:100%;overflow-x:auto}.table{width:100%;border-collapse:collapse;background:var(--c-surface)}.table th,.table td{padding:10px 12px;border-bottom:1px solid var(--c-border);text-align:left}.table th{color:var(--c-text-strong);font-weight:700;background:#f8fafc}.pagination{display:flex;align-items:center;flex-wrap:wrap;gap:8px}.pagination__item{display:inline-flex;align-items:center;justify-content:center;min-width:36px;height:36px;padding:0 10px;border-radius:8px;border:1px solid var(--c-border);color:var(--c-text-strong);background:var(--c-surface);text-decoration:none;font-weight:600}.pagination__item:hover{border-color:#cbd5e0;background:#f8fafc}.pagination__item.is-active{border-color:var(--c-primary);color:var(--c-primary);background:#edf2ff}:root{--shadow-card: 0 20px 50px rgba(22, 34, 58, 0.14)}*{box-sizing:border-box}html,body{min-height:100%}body{margin:0;font-family:"Roboto","Segoe UI",sans-serif;color:var(--c-text);background:var(--c-bg);overflow-x:hidden}.bg-orb{position:fixed;width:460px;height:460px;border-radius:999px;filter:blur(28px);z-index:0;opacity:.45;pointer-events:none}.bg-orb-left{top:-200px;left:-180px;background:radial-gradient(circle, rgba(102, 144, 244, 0.48) 0%, rgba(102, 144, 244, 0) 70%)}.bg-orb-right{right:-200px;bottom:-220px;background:radial-gradient(circle, rgba(30, 42, 58, 0.36) 0%, rgba(30, 42, 58, 0) 70%)}.login-page{min-height:100vh;display:grid;place-items:center;padding:32px 20px;position:relative;z-index:1}.login-card{width:100%;max-width:430px;background:var(--c-surface);border:1px solid var(--c-border);border-radius:12px;box-shadow:var(--shadow-card);padding:34px 30px 28px;animation:card-enter 420ms ease-out}.login-header{margin-bottom:24px}.login-badge{display:inline-block;margin:0 0 14px;padding:5px 12px;border-radius:999px;border:1px solid #d9e2ff;background:#eef2ff;color:#3f5faf;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.06em}h1{margin:0;color:var(--c-text-strong);font-size:clamp(1.6rem,2.5vw,1.9rem);line-height:1.15;font-weight:700}.login-subtitle{margin:10px 0 0;font-size:15px;line-height:1.55;color:var(--c-muted)}.login-alert{margin-bottom:18px}.login-alert-placeholder{opacity:.56}.login-form{display:grid;gap:16px}.form-field{display:grid;gap:7px}.field-label{color:var(--c-text-strong);font-size:13px;font-weight:600}.login-form .form-control{min-height:46px;padding:0 14px;border-width:2px}.login-form .form-control::placeholder{color:#cbd5e0}.login-submit{margin-top:2px;font-size:15px;min-height:48px}@keyframes card-enter{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@media(max-width: 640px){.login-page{padding:18px 14px}.login-card{padding:24px 20px 20px}h1{font-size:1.55rem}}
|
||||
:root{--c-primary: #6690f4;--c-primary-dark: #3164db;--c-bg: #f4f6f9;--c-surface: #ffffff;--c-text: #4e5e6a;--c-text-strong: #2d3748;--c-muted: #718096;--c-border: #e2e8f0;--c-danger: #cc0000;--focus-ring: 0 0 0 3px rgba(102, 144, 244, 0.15);--shadow-card: 0 1px 4px rgba(0, 0, 0, 0.06)}.btn{display:inline-flex;align-items:center;justify-content:center;min-height:38px;padding:8px 16px;border:1px solid rgba(0,0,0,0);border-radius:8px;font:inherit;font-weight:600;text-decoration:none;cursor:pointer;transition:background-color .2s ease,border-color .2s ease,color .2s ease,transform .1s ease}.btn--primary{color:#fff;background:var(--c-primary)}.btn--primary:hover{background:var(--c-primary-dark)}.btn--secondary{color:var(--c-text-strong);border-color:var(--c-border);background:var(--c-surface)}.btn--secondary:hover{border-color:#cbd5e0;background:#f8fafc}.btn--danger{color:#fff;border-color:#b91c1c;background:#dc2626}.btn--danger:hover{border-color:#991b1b;background:#b91c1c}.btn--block{width:100%}.btn:active{transform:translateY(1px)}.btn:focus-visible{outline:none;box-shadow:var(--focus-ring);border-color:var(--c-primary)}.form-control{width:100%;min-height:38px;border:1px solid var(--c-border);border-radius:8px;padding:7px 12px;font:inherit;color:var(--c-text-strong);background:#fff;transition:border-color .2s ease,box-shadow .2s ease}.form-control:focus{outline:none;border-color:var(--c-primary);box-shadow:var(--focus-ring)}.alert{padding:12px 14px;border-radius:8px;border:1px solid rgba(0,0,0,0);font-size:13px;min-height:44px}.alert--danger{border-color:#fed7d7;background:#fff5f5;color:var(--c-danger)}.alert--success{border-color:#b7ebcf;background:#f0fff6;color:#0f6b39}.alert--warning{border-color:#f7dd8b;background:#fff8e8;color:#815500}.form-field{display:grid;gap:7px}.field-label{color:var(--c-text-strong);font-size:13px;font-weight:600}.table-wrap{width:100%;overflow-x:auto}.table{width:100%;border-collapse:collapse;background:var(--c-surface)}.table th,.table td{padding:10px 12px;border-bottom:1px solid var(--c-border);text-align:left}.table th{color:var(--c-text-strong);font-weight:700;background:#f8fafc}.table--details th{white-space:nowrap}.pagination{display:flex;align-items:center;flex-wrap:wrap;gap:8px}.pagination__item{display:inline-flex;align-items:center;justify-content:center;min-width:36px;height:36px;padding:0 10px;border-radius:8px;border:1px solid var(--c-border);color:var(--c-text-strong);background:var(--c-surface);text-decoration:none;font-weight:600}.pagination__item:hover{border-color:#cbd5e0;background:#f8fafc}.pagination__item.is-active{border-color:var(--c-primary);color:var(--c-primary);background:#edf2ff}:root{--shadow-card: 0 20px 50px rgba(22, 34, 58, 0.14)}*{box-sizing:border-box}html,body{min-height:100%}body{margin:0;font-family:"Roboto","Segoe UI",sans-serif;color:var(--c-text);background:var(--c-bg);overflow-x:hidden}.bg-orb{position:fixed;width:460px;height:460px;border-radius:999px;filter:blur(28px);z-index:0;opacity:.45;pointer-events:none}.bg-orb-left{top:-200px;left:-180px;background:radial-gradient(circle, rgba(102, 144, 244, 0.48) 0%, rgba(102, 144, 244, 0) 70%)}.bg-orb-right{right:-200px;bottom:-220px;background:radial-gradient(circle, rgba(30, 42, 58, 0.36) 0%, rgba(30, 42, 58, 0) 70%)}.login-page{min-height:100vh;display:grid;place-items:center;padding:32px 20px;position:relative;z-index:1}.login-card{width:100%;max-width:430px;background:var(--c-surface);border:1px solid var(--c-border);border-radius:12px;box-shadow:var(--shadow-card);padding:34px 30px 28px;animation:card-enter 420ms ease-out}.login-header{margin-bottom:24px}.login-badge{display:inline-block;margin:0 0 14px;padding:5px 12px;border-radius:999px;border:1px solid #d9e2ff;background:#eef2ff;color:#3f5faf;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.06em}h1{margin:0;color:var(--c-text-strong);font-size:clamp(1.6rem,2.5vw,1.9rem);line-height:1.15;font-weight:700}.login-subtitle{margin:10px 0 0;font-size:15px;line-height:1.55;color:var(--c-muted)}.login-alert{margin-bottom:18px}.login-alert-placeholder{opacity:.56}.login-form{display:grid;gap:16px}.form-field{display:grid;gap:7px}.field-label{color:var(--c-text-strong);font-size:13px;font-weight:600}.login-form .form-control{min-height:46px;padding:0 14px;border-width:2px}.login-form .form-control::placeholder{color:#cbd5e0}.login-submit{margin-top:2px;font-size:15px;min-height:48px}@keyframes card-enter{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@media(max-width: 640px){.login-page{padding:18px 14px}.login-card{padding:24px 20px 20px}h1{font-size:1.55rem}}
|
||||
|
||||
@@ -151,6 +151,10 @@
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.table--details th {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/assets/css/app.css">
|
||||
<link rel="stylesheet" href="/assets/css/modules/jquery-alerts.css">
|
||||
<link rel="stylesheet" href="/assets/css/app.css?ver=<?= filemtime(dirname(__DIR__, 3) . '/public/assets/css/app.css') ?: 0 ?>">
|
||||
<link rel="stylesheet" href="/assets/css/modules/jquery-alerts.css?ver=<?= filemtime(dirname(__DIR__, 3) . '/public/assets/css/modules/jquery-alerts.css') ?: 0 ?>">
|
||||
</head>
|
||||
<body>
|
||||
<?php $currentMenu = (string) ($activeMenu ?? ''); ?>
|
||||
@@ -68,6 +68,6 @@
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/assets/js/modules/jquery-alerts.js"></script>
|
||||
<script src="/assets/js/modules/jquery-alerts.js?ver=<?= filemtime(dirname(__DIR__, 3) . '/public/assets/js/modules/jquery-alerts.js') ?: 0 ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/assets/css/login.css">
|
||||
<link rel="stylesheet" href="/assets/css/login.css?ver=<?= filemtime(dirname(__DIR__, 3) . '/public/assets/css/login.css') ?: 0 ?>">
|
||||
</head>
|
||||
<body>
|
||||
<div class="bg-orb bg-orb-left" aria-hidden="true"></div>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<?php endif; ?>
|
||||
|
||||
<h3><?= $e($t('products.show.details')) ?></h3>
|
||||
<table class="table mt-12">
|
||||
<table class="table table--details mt-12">
|
||||
<tbody>
|
||||
<tr><th>ID</th><td><?= $e((string) ($item['id'] ?? '')) ?></td></tr>
|
||||
<tr><th><?= $e($t('products.fields.name')) ?></th><td><?= $e((string) ($item['name'] ?? '')) ?></td></tr>
|
||||
@@ -88,7 +88,7 @@
|
||||
<?php if (!empty($item['security_information'])): ?>
|
||||
<tr>
|
||||
<th>GPSR — informacje o bezpieczeństwie</th>
|
||||
<td><?= $item['security_information'] ?></td>
|
||||
<td><?= $e((string) ($item['security_information'] ?? '')) ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
@@ -170,10 +170,17 @@
|
||||
<div class="product-show-images-grid mt-12">
|
||||
<?php foreach ($images as $image): ?>
|
||||
<div class="product-show-image-card">
|
||||
<div><strong>ID:</strong> <?= $e((string) ($image['id'] ?? 0)) ?><?= ((int) ($image['is_main'] ?? 0) === 1) ? ' | <strong>' . $e($t('products.images.main')) . '</strong>' : '' ?></div>
|
||||
<div class="muted"><?= $e((string) ($image['storage_path'] ?? '')) ?></div>
|
||||
<div class="product-show-image-card__meta">
|
||||
<span><strong>ID:</strong> <?= $e((string) ($image['id'] ?? 0)) ?><?= ((int) ($image['is_main'] ?? 0) === 1) ? ' | <strong>' . $e($t('products.images.main')) . '</strong>' : '' ?></span>
|
||||
<?php if ((string) ($image['storage_path'] ?? '') !== ''): ?>
|
||||
<details class="product-show-image-path">
|
||||
<summary><?= $e($t('products.images.path')) ?></summary>
|
||||
<div class="product-show-image-path__url muted"><?= $e((string) ($image['storage_path'] ?? '')) ?></div>
|
||||
</details>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php if ((string) ($image['public_url'] ?? '') !== ''): ?>
|
||||
<div class="mt-12">
|
||||
<div class="mt-8">
|
||||
<img src="<?= $e((string) $image['public_url']) ?>" alt="<?= $e((string) ($image['alt'] ?? '')) ?>" class="product-show-image">
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -55,39 +55,6 @@ final class UsersController
|
||||
'type' => 'text',
|
||||
'value' => $filters['search'],
|
||||
],
|
||||
[
|
||||
'key' => 'sort',
|
||||
'label' => $this->translator->get('users.filters.sort'),
|
||||
'type' => 'select',
|
||||
'value' => $filters['sort'],
|
||||
'options' => [
|
||||
'id' => 'ID',
|
||||
'name' => $this->translator->get('users.fields.name'),
|
||||
'email' => $this->translator->get('users.fields.email'),
|
||||
'created_at' => $this->translator->get('users.fields.created_at'),
|
||||
],
|
||||
],
|
||||
[
|
||||
'key' => 'sort_dir',
|
||||
'label' => $this->translator->get('users.filters.direction'),
|
||||
'type' => 'select',
|
||||
'value' => $filters['sort_dir'],
|
||||
'options' => [
|
||||
'DESC' => 'DESC',
|
||||
'ASC' => 'ASC',
|
||||
],
|
||||
],
|
||||
[
|
||||
'key' => 'per_page',
|
||||
'label' => $this->translator->get('users.filters.per_page'),
|
||||
'type' => 'select',
|
||||
'value' => (string) $filters['per_page'],
|
||||
'options' => [
|
||||
'20' => '20',
|
||||
'50' => '50',
|
||||
'100' => '100',
|
||||
],
|
||||
],
|
||||
],
|
||||
'columns' => [
|
||||
['key' => 'id', 'label' => 'ID', 'sortable' => true, 'sort_key' => 'id'],
|
||||
@@ -102,7 +69,7 @@ final class UsersController
|
||||
'total' => (int) ($result['total'] ?? 0),
|
||||
'per_page' => (int) ($result['per_page'] ?? 20),
|
||||
],
|
||||
'per_page_options' => [20, 50, 100],
|
||||
'per_page_options' => [10, 20, 50, 100],
|
||||
'empty_message' => $this->translator->get('users.empty'),
|
||||
'show_actions' => false,
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user