update
This commit is contained in:
@@ -1680,6 +1680,92 @@ details[open] > .order-statuses-side__title .order-statuses-side__arrow {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.order-payment-shipping {
|
||||
.section-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-edit-inline {
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
color: #6b7280;
|
||||
padding: 3px 5px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s, background-color 0.15s, color 0.15s;
|
||||
|
||||
&:hover {
|
||||
background: #f3f4f6;
|
||||
color: #111827;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .btn-edit-inline {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.order-details-edit-form {
|
||||
margin-top: 12px;
|
||||
padding: 10px;
|
||||
background: #f9fafb;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
|
||||
.form-row {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
color: #374151;
|
||||
font-weight: 500;
|
||||
|
||||
input[type="text"] {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-top: 3px;
|
||||
padding: 5px 7px;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
label.checkbox-inline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-weight: 400;
|
||||
|
||||
input {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #eef2ff;
|
||||
padding: 1px 4px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.payment-summary {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
@@ -2752,3 +2838,16 @@ body.no-scroll {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Aged orders row highlight (4-7+ days since ordered_at)
|
||||
.table-list-table tbody tr.order-row-aged > td {
|
||||
border-top: 2px solid transparent;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
.table-list-table tbody tr.order-row-aged > td:first-child { border-left: 2px solid transparent; }
|
||||
.table-list-table tbody tr.order-row-aged > td:last-child { border-right: 2px solid transparent; }
|
||||
|
||||
.table-list-table tbody tr.order-row-aged-4 > td { border-color: #f8b4b4; }
|
||||
.table-list-table tbody tr.order-row-aged-5 > td { border-color: #f28282; }
|
||||
.table-list-table tbody tr.order-row-aged-6 > td { border-color: #e74c3c; }
|
||||
.table-list-table tbody tr.order-row-aged-7 > td { border-color: #991b1b; }
|
||||
|
||||
@@ -217,7 +217,8 @@ $buildUrl = static function (array $params = []) use ($basePath, $query): string
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($rows as $row): ?>
|
||||
<tr>
|
||||
<?php $rowClass = trim((string) ($row['_row_class'] ?? '')); ?>
|
||||
<tr<?= $rowClass !== '' ? ' class="' . $e($rowClass) . '"' : '' ?>>
|
||||
<?php if ($selectable): ?>
|
||||
<?php $selectValue = $row[$selectValueKey] ?? ''; ?>
|
||||
<td class="table-select-col">
|
||||
|
||||
@@ -216,10 +216,73 @@ foreach ($addressesList as $address) {
|
||||
</dl>
|
||||
</article>
|
||||
|
||||
<article class="card">
|
||||
<h3 class="section-title"><?= $e($t('orders.details.payment_shipping')) ?></h3>
|
||||
<dl class="order-kv mt-12">
|
||||
<article class="card order-payment-shipping">
|
||||
<div class="section-title-row">
|
||||
<h3 class="section-title"><?= $e($t('orders.details.payment_shipping')) ?></h3>
|
||||
<button type="button" class="btn-edit-inline js-toggle-details-edit" title="Edytuj formę dostawy i płatności" aria-label="Edytuj">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<?php
|
||||
$deliveryMethodValue = trim((string) ($orderRow['delivery_method'] ?? ''));
|
||||
$paymentMethodValue = trim((string) ($orderRow['payment_method'] ?? ''));
|
||||
$paymentTypeCurrent = strtoupper(trim((string) ($orderRow['external_payment_type_id'] ?? '')));
|
||||
$isCodCurrent = \App\Core\Support\StringHelper::isCodPayment($paymentTypeCurrent);
|
||||
?>
|
||||
<form method="POST" action="/orders/<?= (int) $orderId ?>/details/update" class="order-details-edit-form js-details-edit-form" style="display:none">
|
||||
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
||||
<div class="form-row">
|
||||
<label>Forma dostawy
|
||||
<input type="text" name="delivery_method" value="<?= $e($deliveryMethodValue) ?>" maxlength="128" list="js-delivery-methods-list">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Forma płatności
|
||||
<input type="text" name="payment_method" value="<?= $e($paymentMethodValue) ?>" maxlength="128" list="js-payment-methods-list">
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label class="checkbox-inline">
|
||||
<input type="checkbox" name="is_cod" value="1" <?= $isCodCurrent ? 'checked' : '' ?>>
|
||||
<span>Pobranie (COD) — ustawi typ płatności na <code>CASH_ON_DELIVERY</code></span>
|
||||
</label>
|
||||
</div>
|
||||
<datalist id="js-delivery-methods-list">
|
||||
<?php foreach (($deliveryMethodSuggestions ?? []) as $sug): ?>
|
||||
<option value="<?= $e((string) $sug) ?>"></option>
|
||||
<?php endforeach; ?>
|
||||
</datalist>
|
||||
<datalist id="js-payment-methods-list">
|
||||
<?php foreach (($paymentMethodSuggestions ?? []) as $sug): ?>
|
||||
<option value="<?= $e((string) $sug) ?>"></option>
|
||||
<?php endforeach; ?>
|
||||
</datalist>
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary btn-sm">Zapisz</button>
|
||||
<button type="button" class="btn btn-sm js-cancel-details-edit">Anuluj</button>
|
||||
</div>
|
||||
</form>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function(){
|
||||
var toggleBtn = document.querySelector('.js-toggle-details-edit');
|
||||
var cancelBtn = document.querySelector('.js-cancel-details-edit');
|
||||
var form = document.querySelector('.js-details-edit-form');
|
||||
var view = document.querySelector('.js-details-view');
|
||||
if (!toggleBtn || !form || !view) return;
|
||||
toggleBtn.addEventListener('click', function(){
|
||||
form.style.display = 'block';
|
||||
view.style.display = 'none';
|
||||
});
|
||||
if (cancelBtn) cancelBtn.addEventListener('click', function(){
|
||||
form.style.display = 'none';
|
||||
view.style.display = '';
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<dl class="order-kv mt-12 js-details-view">
|
||||
<dt><?= $e($t('orders.details.fields.payment_status')) ?></dt><dd><?= $e((string) ($orderRow['payment_status'] ?? '-')) ?></dd>
|
||||
<dt>Forma dostawy</dt><dd><?= $e($deliveryMethodValue !== '' ? $deliveryMethodValue : '-') ?></dd>
|
||||
<dt>Forma płatności</dt><dd><?= $e($paymentMethodValue !== '' ? $paymentMethodValue : '-') ?></dd>
|
||||
<dt>Typ platnosci</dt>
|
||||
<dd>
|
||||
<?php
|
||||
@@ -514,12 +577,15 @@ foreach ($addressesList as $address) {
|
||||
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
|
||||
<button type="submit" class="btn btn--sm btn--secondary">Pobierz</button>
|
||||
</form>
|
||||
<?php elseif ($pkgStatus !== 'error'): ?>
|
||||
-
|
||||
<?php endif; ?>
|
||||
<?php if (in_array($pkgStatus, ['label_ready', 'created'], true)): ?>
|
||||
<?php if (in_array((int) ($pkg['id'] ?? 0), $pendingPrintIds, true)): ?>
|
||||
<button type="button" class="btn btn--sm btn--danger" disabled style="white-space:nowrap">W kolejce</button>
|
||||
<button type="button"
|
||||
class="btn btn--sm btn--danger js-print-queue-pending"
|
||||
data-package-id="<?= $e((string) ($pkg['id'] ?? 0)) ?>"
|
||||
data-order-id="<?= $e((string) ($orderId ?? 0)) ?>"
|
||||
disabled
|
||||
style="white-space:nowrap">W kolejce</button>
|
||||
<?php else: ?>
|
||||
<button type="button"
|
||||
class="btn btn--sm btn--secondary btn-print-label"
|
||||
@@ -831,6 +897,58 @@ foreach ($addressesList as $address) {
|
||||
});
|
||||
}
|
||||
|
||||
// --- Print queue polling: rewert przycisku "W kolejce" -> "Drukuj" po wydrukowaniu ---
|
||||
var printQueuePending = {}; // packageId -> btn element
|
||||
var printQueueTimer = null;
|
||||
var printQueueTicks = 0;
|
||||
var PRINT_QUEUE_MAX_TICKS = 120; // 120 * 3s = 6 min
|
||||
|
||||
function revertPrintButton(btn) {
|
||||
if (!btn) return;
|
||||
btn.innerHTML = 'Drukuj';
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('btn--danger');
|
||||
btn.classList.remove('js-print-queue-pending');
|
||||
btn.classList.add('btn--secondary');
|
||||
btn.classList.add('btn-print-label');
|
||||
btn.setAttribute('title', 'Wyslij do drukarki');
|
||||
}
|
||||
|
||||
function stopPrintQueuePoll() {
|
||||
if (printQueueTimer) { clearInterval(printQueueTimer); printQueueTimer = null; }
|
||||
printQueueTicks = 0;
|
||||
}
|
||||
|
||||
function pollPrintQueueTick() {
|
||||
printQueueTicks += 1;
|
||||
if (printQueueTicks > PRINT_QUEUE_MAX_TICKS) { stopPrintQueuePoll(); return; }
|
||||
var ids = Object.keys(printQueuePending);
|
||||
if (ids.length === 0) { stopPrintQueuePoll(); return; }
|
||||
fetch('/api/print/jobs/status?package_ids=' + encodeURIComponent(ids.join(',')), { credentials: 'same-origin' })
|
||||
.then(function (r) { return r.ok ? r.json() : { pending: ids.map(Number) }; })
|
||||
.then(function (data) {
|
||||
var pending = (data && Array.isArray(data.pending)) ? data.pending.map(String) : [];
|
||||
Object.keys(printQueuePending).forEach(function (pid) {
|
||||
if (pending.indexOf(String(pid)) === -1) {
|
||||
revertPrintButton(printQueuePending[pid]);
|
||||
delete printQueuePending[pid];
|
||||
}
|
||||
});
|
||||
if (Object.keys(printQueuePending).length === 0) stopPrintQueuePoll();
|
||||
})
|
||||
.catch(function () { /* ignore transient */ });
|
||||
}
|
||||
|
||||
function watchPrintQueueButton(btn) {
|
||||
var pid = btn.getAttribute('data-package-id');
|
||||
if (!pid) return;
|
||||
printQueuePending[pid] = btn;
|
||||
if (!printQueueTimer) {
|
||||
printQueueTicks = 0;
|
||||
printQueueTimer = setInterval(pollPrintQueueTick, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
// Print label button handler (delegated for dynamically added buttons)
|
||||
document.addEventListener('click', function (e) {
|
||||
var btn = e.target.closest('.btn-print-label');
|
||||
@@ -854,7 +972,10 @@ foreach ($addressesList as $address) {
|
||||
btn.innerHTML = 'W kolejce';
|
||||
btn.disabled = true;
|
||||
btn.classList.remove('btn--secondary');
|
||||
btn.classList.remove('btn-print-label');
|
||||
btn.classList.add('btn--danger');
|
||||
btn.classList.add('js-print-queue-pending');
|
||||
watchPrintQueueButton(btn);
|
||||
} else {
|
||||
var msg = (res.data && res.data.error) ? res.data.error : 'Nieznany blad';
|
||||
if (window.OrderProAlerts) { window.OrderProAlerts.show({ message: msg, type: 'error' }); }
|
||||
@@ -869,6 +990,41 @@ foreach ($addressesList as $address) {
|
||||
});
|
||||
});
|
||||
|
||||
// Przy zaladowaniu strony: uruchom polling dla przyciskow juz w kolejce
|
||||
document.querySelectorAll('.js-print-queue-pending').forEach(function (btn) {
|
||||
watchPrintQueueButton(btn);
|
||||
});
|
||||
|
||||
// Auto-click ostatniej etykiety po utworzeniu przesylki (?printLast=1)
|
||||
(function autoClickLastLabel() {
|
||||
if (!/[?&]printLast=1\b/.test(location.search)) return;
|
||||
var attempts = 0;
|
||||
var MAX_ATTEMPTS = 30; // 30 * 1s = 30s
|
||||
var timer = setInterval(function () {
|
||||
attempts += 1;
|
||||
if (attempts > MAX_ATTEMPTS) {
|
||||
clearInterval(timer);
|
||||
cleanupPrintLastParam();
|
||||
return;
|
||||
}
|
||||
var buttons = document.querySelectorAll('.btn-print-label');
|
||||
if (buttons.length > 0) {
|
||||
var last = buttons[buttons.length - 1];
|
||||
clearInterval(timer);
|
||||
cleanupPrintLastParam();
|
||||
last.click();
|
||||
}
|
||||
}, 1000);
|
||||
})();
|
||||
|
||||
function cleanupPrintLastParam() {
|
||||
try {
|
||||
var url = new URL(location.href);
|
||||
url.searchParams.delete('printLast');
|
||||
history.replaceState({}, '', url.toString());
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
@@ -412,12 +412,15 @@ $defaultCodAmount = $isCod ? number_format($totalWithTax, 2, '.', '') : '0';
|
||||
<?php endif; ?>
|
||||
<?php elseif ($pkgShipmentId !== '' && $pkgStatus === 'created'): ?>
|
||||
<span class="muted">Generowanie etykiety...</span>
|
||||
<?php else: ?>
|
||||
-
|
||||
<?php endif; ?>
|
||||
<?php if (in_array($pkgStatus, ['label_ready', 'created'], true)): ?>
|
||||
<?php if (in_array($pkgId, $pendingPrintIds, true)): ?>
|
||||
<button type="button" class="btn btn--sm btn--danger" disabled style="white-space:nowrap">W kolejce</button>
|
||||
<button type="button"
|
||||
class="btn btn--sm btn--danger js-print-queue-pending"
|
||||
data-package-id="<?= $e((string) $pkgId) ?>"
|
||||
data-order-id="<?= $e((string) ($orderId ?? 0)) ?>"
|
||||
disabled
|
||||
style="white-space:nowrap">W kolejce</button>
|
||||
<?php else: ?>
|
||||
<button type="button"
|
||||
class="btn btn--sm btn--secondary btn-print-label"
|
||||
@@ -765,6 +768,58 @@ $defaultCodAmount = $isCod ? number_format($totalWithTax, 2, '.', '') : '0';
|
||||
});
|
||||
});
|
||||
|
||||
// --- Print queue polling (shared mechanika: rewert "W kolejce" -> "Drukuj") ---
|
||||
var printQueuePending = {};
|
||||
var printQueueTimer = null;
|
||||
var printQueueTicks = 0;
|
||||
var PRINT_QUEUE_MAX_TICKS = 120;
|
||||
|
||||
function revertPrintButton(btn) {
|
||||
if (!btn) return;
|
||||
btn.innerHTML = 'Drukuj';
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('btn--danger');
|
||||
btn.classList.remove('js-print-queue-pending');
|
||||
btn.classList.add('btn--secondary');
|
||||
btn.classList.add('btn-print-label');
|
||||
btn.setAttribute('title', 'Wyslij do drukarki');
|
||||
}
|
||||
|
||||
function stopPrintQueuePoll() {
|
||||
if (printQueueTimer) { clearInterval(printQueueTimer); printQueueTimer = null; }
|
||||
printQueueTicks = 0;
|
||||
}
|
||||
|
||||
function pollPrintQueueTick() {
|
||||
printQueueTicks += 1;
|
||||
if (printQueueTicks > PRINT_QUEUE_MAX_TICKS) { stopPrintQueuePoll(); return; }
|
||||
var ids = Object.keys(printQueuePending);
|
||||
if (ids.length === 0) { stopPrintQueuePoll(); return; }
|
||||
fetch('/api/print/jobs/status?package_ids=' + encodeURIComponent(ids.join(',')), { credentials: 'same-origin' })
|
||||
.then(function (r) { return r.ok ? r.json() : { pending: ids.map(Number) }; })
|
||||
.then(function (data) {
|
||||
var pending = (data && Array.isArray(data.pending)) ? data.pending.map(String) : [];
|
||||
Object.keys(printQueuePending).forEach(function (pid) {
|
||||
if (pending.indexOf(String(pid)) === -1) {
|
||||
revertPrintButton(printQueuePending[pid]);
|
||||
delete printQueuePending[pid];
|
||||
}
|
||||
});
|
||||
if (Object.keys(printQueuePending).length === 0) stopPrintQueuePoll();
|
||||
})
|
||||
.catch(function () { /* ignore */ });
|
||||
}
|
||||
|
||||
function watchPrintQueueButton(btn) {
|
||||
var pid = btn.getAttribute('data-package-id');
|
||||
if (!pid) return;
|
||||
printQueuePending[pid] = btn;
|
||||
if (!printQueueTimer) {
|
||||
printQueueTicks = 0;
|
||||
printQueueTimer = setInterval(pollPrintQueueTick, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
// Print label button handler
|
||||
document.querySelectorAll('.btn-print-label').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
@@ -786,7 +841,10 @@ $defaultCodAmount = $isCod ? number_format($totalWithTax, 2, '.', '') : '0';
|
||||
btn.innerHTML = 'W kolejce';
|
||||
btn.disabled = true;
|
||||
btn.classList.remove('btn--secondary');
|
||||
btn.classList.remove('btn-print-label');
|
||||
btn.classList.add('btn--danger');
|
||||
btn.classList.add('js-print-queue-pending');
|
||||
watchPrintQueueButton(btn);
|
||||
} else {
|
||||
var msg = (res.data && res.data.error) ? res.data.error : 'Nieznany blad';
|
||||
if (window.OrderProAlerts) {
|
||||
@@ -806,6 +864,11 @@ $defaultCodAmount = $isCod ? number_format($totalWithTax, 2, '.', '') : '0';
|
||||
});
|
||||
});
|
||||
|
||||
// Przy zaladowaniu strony: uruchom polling dla przyciskow juz w kolejce
|
||||
document.querySelectorAll('.js-print-queue-pending').forEach(function (btn) {
|
||||
watchPrintQueueButton(btn);
|
||||
});
|
||||
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
var autoCheckId = params.get('check');
|
||||
|
||||
@@ -955,47 +1018,20 @@ $defaultCodAmount = $isCod ? number_format($totalWithTax, 2, '.', '') : '0';
|
||||
}
|
||||
|
||||
// --- Apply preset (autofill form) ---
|
||||
// Preset nadpisuje wylacznie rozmiary paczki i wage. Forma dostawy (carrier,
|
||||
// serwis, punkt nadania, format etykiety) pozostaje bez zmian.
|
||||
function applyPreset(preset) {
|
||||
var carrierSelect = document.getElementById('shipment-carrier-select');
|
||||
var hiddenInput = document.getElementById('shipment-delivery-service');
|
||||
var credentialsInput = document.getElementById('shipment-credentials-id');
|
||||
var carrierInput = document.getElementById('shipment-carrier-id');
|
||||
var providerInput = document.getElementById('shipment-provider-code');
|
||||
setFieldValue('package_type', preset.package_type || 'PACKAGE');
|
||||
setFieldValue('length_cm', preset.length_cm || '25');
|
||||
setFieldValue('width_cm', preset.width_cm || '20');
|
||||
setFieldValue('height_cm', preset.height_cm || '8');
|
||||
setFieldValue('weight_kg', preset.weight_kg || '1');
|
||||
|
||||
if (!carrierSelect) return;
|
||||
|
||||
// Set carrier — use native change so existing handler shows correct panel
|
||||
carrierSelect.value = preset.carrier || '';
|
||||
if (carrierSelect._syncTrigger) carrierSelect._syncTrigger();
|
||||
carrierSelect.dispatchEvent(new Event('change'));
|
||||
|
||||
// The existing change handler clears hidden fields and resets selects.
|
||||
// We must wait for that to finish, then override with preset values.
|
||||
// Auto-submit after autofill completes
|
||||
setTimeout(function () {
|
||||
// Hidden fields — set AFTER the change handler cleared them
|
||||
if (hiddenInput) hiddenInput.value = preset.delivery_method_id || '';
|
||||
if (credentialsInput) credentialsInput.value = preset.credentials_id || '';
|
||||
if (carrierInput) carrierInput.value = preset.carrier_id || '';
|
||||
if (providerInput) providerInput.value = preset.provider_code || '';
|
||||
|
||||
// Package fields
|
||||
setFieldValue('package_type', preset.package_type || 'PACKAGE');
|
||||
setFieldValue('length_cm', preset.length_cm || '25');
|
||||
setFieldValue('width_cm', preset.width_cm || '20');
|
||||
setFieldValue('height_cm', preset.height_cm || '8');
|
||||
setFieldValue('weight_kg', preset.weight_kg || '1');
|
||||
setFieldValue('sender_point_id', preset.sender_point_id || '');
|
||||
setFieldValue('label_format', preset.label_format || 'PDF');
|
||||
|
||||
// Select delivery service in the correct panel
|
||||
selectDeliveryService(preset);
|
||||
|
||||
// Auto-submit after autofill completes
|
||||
setTimeout(function () {
|
||||
var form = document.getElementById('shipment-form');
|
||||
if (form) form.submit();
|
||||
}, 300);
|
||||
}, 200);
|
||||
var form = document.getElementById('shipment-form');
|
||||
if (form) form.submit();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function setFieldValue(name, value) {
|
||||
|
||||
Reference in New Issue
Block a user