419 lines
14 KiB
PHP
419 lines
14 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../../includes/auth.php';
|
|
require_once __DIR__ . '/../../includes/config.php';
|
|
require_once __DIR__ . '/../../includes/header.php';
|
|
require_once __DIR__ . '/../../includes/sidebar.php';
|
|
?>
|
|
|
|
<div class="admin-content">
|
|
<style>
|
|
.admin-page-title {
|
|
font-size: 24px;
|
|
font-weight: 600;
|
|
color: #23282d;
|
|
margin-bottom: 20px;
|
|
padding-bottom: 10px;
|
|
border-bottom: 2px solid #0073aa;
|
|
}
|
|
|
|
.admin-content-box {
|
|
background: #fff;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
padding: 20px;
|
|
margin-top: 20px;
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.preorder-controls {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
|
|
gap: 10px;
|
|
margin-bottom: 14px;
|
|
}
|
|
|
|
.preorder-control {
|
|
display: grid;
|
|
gap: 6px;
|
|
}
|
|
|
|
.preorder-control label {
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
color: #555;
|
|
}
|
|
|
|
.preorder-controls input,
|
|
.preorder-controls select {
|
|
width: 100%;
|
|
min-height: 36px;
|
|
border: 1px solid #d0d7de;
|
|
border-radius: 6px;
|
|
padding: 8px 10px;
|
|
font-size: 13px;
|
|
outline: none;
|
|
background: #fff;
|
|
}
|
|
|
|
.preorder-controls input:focus,
|
|
.preorder-controls select:focus {
|
|
border-color: #0073aa;
|
|
box-shadow: 0 0 0 3px rgba(0,115,170,0.12);
|
|
}
|
|
|
|
.preorder-actions {
|
|
display: flex;
|
|
gap: 8px;
|
|
margin-bottom: 16px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.preorder-btn {
|
|
border: 0;
|
|
border-radius: 6px;
|
|
padding: 9px 14px;
|
|
font-size: 13px;
|
|
font-weight: 700;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.preorder-btn-primary {
|
|
background: #0073aa;
|
|
color: #fff;
|
|
}
|
|
|
|
.preorder-btn-primary:hover {
|
|
background: #005f8d;
|
|
}
|
|
|
|
.preorder-btn-secondary {
|
|
background: #6c757d;
|
|
color: #fff;
|
|
}
|
|
|
|
.preorder-btn-secondary:hover {
|
|
background: #596167;
|
|
}
|
|
|
|
.preorder-status {
|
|
font-size: 13px;
|
|
color: #666;
|
|
margin-bottom: 10px;
|
|
min-height: 18px;
|
|
}
|
|
|
|
.preorder-status.error {
|
|
color: #b32d2e;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.preorder-summary {
|
|
font-size: 13px;
|
|
color: #444;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.preorder-table-wrap {
|
|
width: 100%;
|
|
overflow-x: auto;
|
|
border: 1px solid #e6e6e6;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.preorder-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
background: #fff;
|
|
min-width: 620px;
|
|
}
|
|
|
|
.preorder-table th,
|
|
.preorder-table td {
|
|
padding: 10px 12px;
|
|
border-bottom: 1px solid #eee;
|
|
font-size: 13px;
|
|
text-align: left;
|
|
vertical-align: top;
|
|
}
|
|
|
|
.preorder-table th {
|
|
background: #f8f9fa;
|
|
color: #23282d;
|
|
font-weight: 700;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.preorder-table tbody tr:hover {
|
|
background: #fafcff;
|
|
}
|
|
|
|
.preorder-empty {
|
|
padding: 20px;
|
|
text-align: center;
|
|
color: #666;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.preorder-pagination {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 10px;
|
|
margin-top: 14px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.preorder-page-info {
|
|
font-size: 13px;
|
|
color: #444;
|
|
}
|
|
|
|
.preorder-page-actions {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
|
|
.preorder-page-btn {
|
|
border: 1px solid #d0d7de;
|
|
background: #fff;
|
|
border-radius: 6px;
|
|
padding: 8px 12px;
|
|
font-size: 13px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.preorder-page-btn:hover {
|
|
background: #f3f5f7;
|
|
}
|
|
|
|
.preorder-page-btn:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
background: #f7f7f7;
|
|
}
|
|
</style>
|
|
|
|
<h1 class="admin-page-title">📬 Preorder - zapisy newslettera</h1>
|
|
|
|
<div class="admin-content-box">
|
|
<div class="preorder-controls">
|
|
<div class="preorder-control">
|
|
<label for="preorderEmail">E-mail</label>
|
|
<input id="preorderEmail" type="text" placeholder="np. gmail.com" />
|
|
</div>
|
|
<div class="preorder-control">
|
|
<label for="preorderCreatedFrom">Data od</label>
|
|
<input id="preorderCreatedFrom" type="datetime-local" />
|
|
</div>
|
|
<div class="preorder-control">
|
|
<label for="preorderCreatedTo">Data do</label>
|
|
<input id="preorderCreatedTo" type="datetime-local" />
|
|
</div>
|
|
<div class="preorder-control">
|
|
<label for="preorderPerPage">Na stronę</label>
|
|
<select id="preorderPerPage">
|
|
<option value="10">10</option>
|
|
<option value="20" selected>20</option>
|
|
<option value="50">50</option>
|
|
<option value="100">100</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="preorder-actions">
|
|
<button id="preorderSearchBtn" class="preorder-btn preorder-btn-primary" type="button">Filtruj</button>
|
|
<button id="preorderResetBtn" class="preorder-btn preorder-btn-secondary" type="button">Wyczyść</button>
|
|
</div>
|
|
|
|
<div id="preorderStatus" class="preorder-status">Ładowanie...</div>
|
|
<div id="preorderSummary" class="preorder-summary"></div>
|
|
|
|
<div class="preorder-table-wrap">
|
|
<table class="preorder-table" aria-label="Lista zapisów PREOrder">
|
|
<thead>
|
|
<tr>
|
|
<th style="width: 80px;">ID</th>
|
|
<th>E-mail</th>
|
|
<th style="width: 170px;">IP</th>
|
|
<th style="width: 190px;">Data zapisu</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="preorderRows"></tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="preorder-pagination">
|
|
<div id="preorderPageInfo" class="preorder-page-info">Strona 1 z 1</div>
|
|
<div class="preorder-page-actions">
|
|
<button id="preorderPrevBtn" class="preorder-page-btn" type="button">← Poprzednia</button>
|
|
<button id="preorderNextBtn" class="preorder-page-btn" type="button">Następna →</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
(function () {
|
|
const API_URL = '/api/admin_preorder.php';
|
|
|
|
const inputEmail = document.getElementById('preorderEmail');
|
|
const inputCreatedFrom = document.getElementById('preorderCreatedFrom');
|
|
const inputCreatedTo = document.getElementById('preorderCreatedTo');
|
|
const inputPerPage = document.getElementById('preorderPerPage');
|
|
|
|
const searchBtn = document.getElementById('preorderSearchBtn');
|
|
const resetBtn = document.getElementById('preorderResetBtn');
|
|
const prevBtn = document.getElementById('preorderPrevBtn');
|
|
const nextBtn = document.getElementById('preorderNextBtn');
|
|
|
|
const statusEl = document.getElementById('preorderStatus');
|
|
const summaryEl = document.getElementById('preorderSummary');
|
|
const rowsEl = document.getElementById('preorderRows');
|
|
const pageInfoEl = document.getElementById('preorderPageInfo');
|
|
|
|
const state = {
|
|
page: 1,
|
|
perPage: 20,
|
|
totalPages: 1,
|
|
totalRecords: 0,
|
|
hasNextPage: false,
|
|
hasPreviousPage: false
|
|
};
|
|
|
|
function toSqlDateTime(localValue, isEnd) {
|
|
if (!localValue) return '';
|
|
const withSeconds = localValue.length === 16 ? (localValue + ':00') : localValue;
|
|
return withSeconds.replace('T', ' ') + (isEnd && withSeconds.length === 19 ? '' : '');
|
|
}
|
|
|
|
function escapeHtml(value) {
|
|
return String(value)
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''');
|
|
}
|
|
|
|
function setStatus(text, isError) {
|
|
statusEl.textContent = text || '';
|
|
statusEl.classList.toggle('error', !!isError);
|
|
}
|
|
|
|
function renderRows(data) {
|
|
if (!Array.isArray(data) || data.length === 0) {
|
|
rowsEl.innerHTML = '<tr><td colspan="4" class="preorder-empty">Brak zapisów dla wybranych filtrów</td></tr>';
|
|
return;
|
|
}
|
|
|
|
rowsEl.innerHTML = data.map(function (row) {
|
|
return '<tr>' +
|
|
'<td>' + escapeHtml(row.id ?? '') + '</td>' +
|
|
'<td>' + escapeHtml(row.email ?? '') + '</td>' +
|
|
'<td>' + escapeHtml(row.ip_address ?? '-') + '</td>' +
|
|
'<td>' + escapeHtml(row.created_at ?? '') + '</td>' +
|
|
'</tr>';
|
|
}).join('');
|
|
}
|
|
|
|
function updatePagination() {
|
|
pageInfoEl.textContent = 'Strona ' + String(state.page) + ' z ' + String(state.totalPages);
|
|
prevBtn.disabled = !state.hasPreviousPage;
|
|
nextBtn.disabled = !state.hasNextPage;
|
|
summaryEl.textContent = 'Wyniki: ' + String(state.totalRecords) + ' rekordów';
|
|
}
|
|
|
|
async function loadPreorders() {
|
|
setStatus('Ładowanie...', false);
|
|
|
|
const params = new URLSearchParams();
|
|
params.set('page', String(state.page));
|
|
params.set('perPage', String(state.perPage));
|
|
|
|
const email = (inputEmail.value || '').trim();
|
|
const createdFrom = toSqlDateTime((inputCreatedFrom.value || '').trim(), false);
|
|
const createdTo = toSqlDateTime((inputCreatedTo.value || '').trim(), true);
|
|
|
|
if (email !== '') params.set('email', email);
|
|
if (createdFrom !== '') params.set('createdFrom', createdFrom);
|
|
if (createdTo !== '') params.set('createdTo', createdTo);
|
|
|
|
try {
|
|
const response = await fetch(API_URL + '?' + params.toString(), {
|
|
method: 'GET',
|
|
credentials: 'same-origin'
|
|
});
|
|
const json = await response.json();
|
|
if (!json.success) {
|
|
throw new Error(json.error || 'Błąd API');
|
|
}
|
|
|
|
const pagination = json.pagination || {};
|
|
state.page = Number(pagination.currentPage || state.page || 1);
|
|
state.totalPages = Number(pagination.totalPages || 1);
|
|
state.totalRecords = Number(pagination.totalRecords || 0);
|
|
state.hasNextPage = !!pagination.hasNextPage;
|
|
state.hasPreviousPage = !!pagination.hasPreviousPage;
|
|
|
|
renderRows(json.data || []);
|
|
updatePagination();
|
|
setStatus('OK', false);
|
|
} catch (error) {
|
|
renderRows([]);
|
|
state.totalPages = 1;
|
|
state.totalRecords = 0;
|
|
state.hasNextPage = false;
|
|
state.hasPreviousPage = false;
|
|
updatePagination();
|
|
setStatus('Nie udało się pobrać danych: ' + (error && error.message ? error.message : 'nieznany błąd'), true);
|
|
}
|
|
}
|
|
|
|
function applyFilters() {
|
|
state.page = 1;
|
|
state.perPage = Math.max(1, Math.min(100, Number(inputPerPage.value || 20)));
|
|
loadPreorders();
|
|
}
|
|
|
|
function resetFilters() {
|
|
inputEmail.value = '';
|
|
inputCreatedFrom.value = '';
|
|
inputCreatedTo.value = '';
|
|
inputPerPage.value = '20';
|
|
state.page = 1;
|
|
state.perPage = 20;
|
|
loadPreorders();
|
|
}
|
|
|
|
searchBtn.addEventListener('click', applyFilters);
|
|
resetBtn.addEventListener('click', resetFilters);
|
|
|
|
inputEmail.addEventListener('keydown', function (e) {
|
|
if (e.key === 'Enter') {
|
|
e.preventDefault();
|
|
applyFilters();
|
|
}
|
|
});
|
|
|
|
inputPerPage.addEventListener('change', applyFilters);
|
|
|
|
prevBtn.addEventListener('click', function () {
|
|
if (state.page <= 1) return;
|
|
state.page -= 1;
|
|
loadPreorders();
|
|
});
|
|
|
|
nextBtn.addEventListener('click', function () {
|
|
if (!state.hasNextPage) return;
|
|
state.page += 1;
|
|
loadPreorders();
|
|
});
|
|
|
|
loadPreorders();
|
|
})();
|
|
</script>
|
|
</div>
|
|
|
|
<?php
|
|
require_once __DIR__ . '/../../includes/footer.php';
|
|
?>
|