272 lines
13 KiB
PHP
272 lines
13 KiB
PHP
|
|
<?php
|
||
|
|
$pageTitle = $title ?? 'Support Settings';
|
||
|
|
$saved = (string)($saved ?? '');
|
||
|
|
$regenerated = (string)($regenerated ?? '');
|
||
|
|
$imapTest = (string)($imap_test ?? '');
|
||
|
|
$imapError = (string)($imap_error ?? '');
|
||
|
|
$syncResult = (string)($sync_result ?? '');
|
||
|
|
$supportTypeRows = is_array($support_type_rows ?? null) ? $support_type_rows : [];
|
||
|
|
ob_start();
|
||
|
|
?>
|
||
|
|
<section class="admin-card">
|
||
|
|
<div class="badge">Support</div>
|
||
|
|
<div style="display:flex; align-items:center; justify-content:space-between; gap:16px; margin-top:16px;">
|
||
|
|
<div>
|
||
|
|
<h1 style="font-size:28px; margin:0;">Support Settings</h1>
|
||
|
|
<p style="color: var(--muted); margin-top:6px;">Configure ticket behavior, IMAP inbox sync, and request categories.</p>
|
||
|
|
</div>
|
||
|
|
<a href="/admin/support" class="btn outline">Back</a>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<?php if ($saved !== ''): ?><div style="margin-top:12px; color:#9be7c6; font-size:13px;">Settings saved.</div><?php endif; ?>
|
||
|
|
<?php if ($imapTest === 'ok'): ?><div style="margin-top:12px; color:#9be7c6; font-size:13px;">IMAP connection successful.</div><?php endif; ?>
|
||
|
|
<?php if ($imapError !== ''): ?><div style="margin-top:12px; color:#f3b0b0; font-size:13px;"><?= htmlspecialchars($imapError, ENT_QUOTES, 'UTF-8') ?></div><?php endif; ?>
|
||
|
|
<?php if ($syncResult !== ''): ?>
|
||
|
|
<div style="margin-top:12px; color:<?= str_starts_with($syncResult, 'ok') ? '#9be7c6' : '#f3b0b0' ?>; font-size:13px;">
|
||
|
|
Sync result: <?= htmlspecialchars($syncResult, ENT_QUOTES, 'UTF-8') ?>
|
||
|
|
</div>
|
||
|
|
<?php endif; ?>
|
||
|
|
|
||
|
|
<div style="display:flex; gap:8px; margin-top:16px; flex-wrap:wrap;">
|
||
|
|
<button type="button" class="btn outline support-tab-btn active" data-tab="general">General</button>
|
||
|
|
<button type="button" class="btn outline support-tab-btn" data-tab="email">Email</button>
|
||
|
|
<button type="button" class="btn outline support-tab-btn" data-tab="categories">Categories</button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<form method="post" action="/admin/support/settings" style="margin-top:14px; display:grid; gap:12px;">
|
||
|
|
<div class="support-tab-panel" data-panel="general">
|
||
|
|
<div class="admin-card" style="padding:16px; display:grid; gap:10px;">
|
||
|
|
<div>
|
||
|
|
<div class="label">Ticket Prefix</div>
|
||
|
|
<input class="input" name="support_ticket_prefix" value="<?= htmlspecialchars((string)($support_ticket_prefix ?? 'TCK'), ENT_QUOTES, 'UTF-8') ?>" placeholder="TCK">
|
||
|
|
<div style="margin-top:6px; color:var(--muted); font-size:12px;">Example: TCK-20260221-ABC123</div>
|
||
|
|
</div>
|
||
|
|
<div style="margin-top:2px; color:var(--muted); font-size:12px;">Cron job keys and commands are now managed from <a href="/admin/crons" style="color:#9ff8d8;">Admin > Cron Jobs</a>.</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="support-tab-panel" data-panel="email style="display:none;">
|
||
|
|
<div class="admin-card" style="padding:16px;">
|
||
|
|
<div style="display:grid; grid-template-columns:1fr 130px 130px; gap:10px;">
|
||
|
|
<div>
|
||
|
|
<div class="label">IMAP Host</div>
|
||
|
|
<input class="input" name="support_imap_host" value="<?= htmlspecialchars((string)($imap_host ?? ''), ENT_QUOTES, 'UTF-8') ?>" placeholder="mail.example.com">
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<div class="label">IMAP Port</div>
|
||
|
|
<input class="input" name="support_imap_port" value="<?= htmlspecialchars((string)($imap_port ?? '993'), ENT_QUOTES, 'UTF-8') ?>" placeholder="993">
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<div class="label">Encryption</div>
|
||
|
|
<select class="input" name="support_imap_encryption">
|
||
|
|
<?php $enc = (string)($imap_encryption ?? 'ssl'); ?>
|
||
|
|
<option value="ssl" <?= $enc === 'ssl' ? 'selected' : '' ?>>SSL</option>
|
||
|
|
<option value="tls" <?= $enc === 'tls' ? 'selected' : '' ?>>TLS</option>
|
||
|
|
<option value="none" <?= $enc === 'none' ? 'selected' : '' ?>>None</option>
|
||
|
|
</select>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div style="display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-top:10px;">
|
||
|
|
<div>
|
||
|
|
<div class="label">IMAP Username</div>
|
||
|
|
<input class="input" name="support_imap_user" value="<?= htmlspecialchars((string)($imap_user ?? ''), ENT_QUOTES, 'UTF-8') ?>" placeholder="support@example.com">
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<div class="label">IMAP Password</div>
|
||
|
|
<input class="input" type="password" name="support_imap_pass" value="<?= htmlspecialchars((string)($imap_pass ?? ''), ENT_QUOTES, 'UTF-8') ?>">
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div style="display:grid; grid-template-columns:1fr 1fr; gap:10px; margin-top:10px;">
|
||
|
|
<div>
|
||
|
|
<div class="label">Inbox Folder</div>
|
||
|
|
<input class="input" name="support_imap_folder" value="<?= htmlspecialchars((string)($imap_folder ?? 'INBOX'), ENT_QUOTES, 'UTF-8') ?>" placeholder="INBOX">
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<div class="label">Support From Email (optional)</div>
|
||
|
|
<input class="input" name="support_from_email" value="<?= htmlspecialchars((string)($support_from_email ?? ''), ENT_QUOTES, 'UTF-8') ?>" placeholder="support@example.com">
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div style="margin-top:12px; display:flex; flex-wrap:wrap; gap:8px;">
|
||
|
|
<button class="btn outline" type="submit" formaction="/admin/support/settings/test-imap" formmethod="post">Test IMAP</button>
|
||
|
|
<button class="btn outline" type="submit" formaction="/admin/support/settings/run-sync" formmethod="post">Run Sync Now</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="support-tab-panel" data-panel="categories" style="display:none;">
|
||
|
|
<div class="admin-card" style="padding:16px;">
|
||
|
|
<div style="display:flex; align-items:center; justify-content:space-between; gap:8px;">
|
||
|
|
<div class="label">Support Request Types</div>
|
||
|
|
<button type="button" class="btn outline small" id="addSupportTypeBtn">Add Type</button>
|
||
|
|
</div>
|
||
|
|
<div id="supportTypeRows" style="display:grid; gap:10px; margin-top:8px;">
|
||
|
|
<?php foreach ($supportTypeRows as $index => $row): ?>
|
||
|
|
<div class="admin-card support-type-row" style="padding:10px;">
|
||
|
|
<div style="display:grid; grid-template-columns:1fr auto; gap:10px; align-items:end;">
|
||
|
|
<div>
|
||
|
|
<div class="label" style="font-size:10px;">Title</div>
|
||
|
|
<input class="input" name="support_type_title[]" value="<?= htmlspecialchars((string)($row['label'] ?? ''), ENT_QUOTES, 'UTF-8') ?>" placeholder="Order Issue">
|
||
|
|
</div>
|
||
|
|
<button type="button" class="btn outline small removeSupportTypeBtn">Remove Type</button>
|
||
|
|
</div>
|
||
|
|
<div style="margin-top:10px;">
|
||
|
|
<div style="display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:6px;">
|
||
|
|
<div class="label" style="font-size:10px;">Additional Options</div>
|
||
|
|
<button type="button" class="btn outline small addFieldBtn">Add Option</button>
|
||
|
|
</div>
|
||
|
|
<div class="support-type-fields" style="display:grid; gap:8px;">
|
||
|
|
<?php
|
||
|
|
$labelsMap = [];
|
||
|
|
foreach ((array)($row['field_labels'] ?? []) as $opt) {
|
||
|
|
if (is_array($opt) && isset($opt['key'], $opt['label'])) {
|
||
|
|
$labelsMap[(string)$opt['key']] = (string)$opt['label'];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
foreach ((array)($row['fields'] ?? []) as $fieldKey):
|
||
|
|
$fieldKey = (string)$fieldKey;
|
||
|
|
$label = $labelsMap[$fieldKey] ?? $fieldKey;
|
||
|
|
?>
|
||
|
|
<div class="support-field-row" style="display:grid; grid-template-columns:1fr auto; gap:8px; align-items:center;">
|
||
|
|
<input class="input option-label" name="support_type_option_labels[<?= (int)$index ?>][]" value="<?= htmlspecialchars($label, ENT_QUOTES, 'UTF-8') ?>" placeholder="Option title">
|
||
|
|
<button type="button" class="btn outline small removeFieldBtn">Remove</button>
|
||
|
|
<input type="hidden" class="option-key" name="support_type_option_keys[<?= (int)$index ?>][]" value="<?= htmlspecialchars($fieldKey, ENT_QUOTES, 'UTF-8') ?>">
|
||
|
|
</div>
|
||
|
|
<?php endforeach; ?>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<?php endforeach; ?>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div style="display:flex; justify-content:flex-end;">
|
||
|
|
<button class="btn" type="submit">Save Settings</button>
|
||
|
|
</div>
|
||
|
|
</form>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<template id="supportTypeRowTemplate">
|
||
|
|
<div class="admin-card support-type-row" style="padding:10px;">
|
||
|
|
<div style="display:grid; grid-template-columns:1fr auto; gap:10px; align-items:end;">
|
||
|
|
<div>
|
||
|
|
<div class="label" style="font-size:10px;">Title</div>
|
||
|
|
<input class="input support-type-title" name="support_type_title[]" value="" placeholder="New Support Type">
|
||
|
|
</div>
|
||
|
|
<button type="button" class="btn outline small removeSupportTypeBtn">Remove Type</button>
|
||
|
|
</div>
|
||
|
|
<div style="margin-top:10px;">
|
||
|
|
<div style="display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:6px;">
|
||
|
|
<div class="label" style="font-size:10px;">Additional Options</div>
|
||
|
|
<button type="button" class="btn outline small addFieldBtn">Add Option</button>
|
||
|
|
</div>
|
||
|
|
<div class="support-type-fields" style="display:grid; gap:8px;"></div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<template id="supportFieldRowTemplate">
|
||
|
|
<div class="support-field-row" style="display:grid; grid-template-columns:1fr auto; gap:8px; align-items:center;">
|
||
|
|
<input class="input option-label" value="" placeholder="Option title">
|
||
|
|
<button type="button" class="btn outline small removeFieldBtn">Remove</button>
|
||
|
|
<input type="hidden" class="option-key" value="">
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
(function () {
|
||
|
|
const tabButtons = document.querySelectorAll('.support-tab-btn');
|
||
|
|
const tabPanels = document.querySelectorAll('.support-tab-panel');
|
||
|
|
tabButtons.forEach((btn) => {
|
||
|
|
btn.addEventListener('click', () => {
|
||
|
|
const tab = btn.getAttribute('data-tab');
|
||
|
|
tabButtons.forEach((b) => b.classList.toggle('active', b === btn));
|
||
|
|
tabPanels.forEach((panel) => {
|
||
|
|
panel.style.display = panel.getAttribute('data-panel') === tab ? '' : 'none';
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
const rowsWrap = document.getElementById('supportTypeRows');
|
||
|
|
const addBtn = document.getElementById('addSupportTypeBtn');
|
||
|
|
const rowTemplate = document.getElementById('supportTypeRowTemplate');
|
||
|
|
const fieldTemplate = document.getElementById('supportFieldRowTemplate');
|
||
|
|
if (!rowsWrap || !addBtn || !rowTemplate || !fieldTemplate) return;
|
||
|
|
|
||
|
|
const slugify = (value) => {
|
||
|
|
const raw = (value || '').toLowerCase().trim().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '');
|
||
|
|
return raw || ('field_' + Math.random().toString(16).slice(2, 6));
|
||
|
|
};
|
||
|
|
|
||
|
|
const normalizeIndexes = () => {
|
||
|
|
rowsWrap.querySelectorAll('.support-type-row').forEach((row, index) => {
|
||
|
|
row.querySelectorAll('.support-field-row').forEach((fieldRow) => {
|
||
|
|
const keyInput = fieldRow.querySelector('.option-key');
|
||
|
|
const labelInput = fieldRow.querySelector('.option-label');
|
||
|
|
if (!keyInput || !labelInput) return;
|
||
|
|
keyInput.name = `support_type_option_keys[${index}][]`;
|
||
|
|
labelInput.name = `support_type_option_labels[${index}][]`;
|
||
|
|
});
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
const wireButtons = () => {
|
||
|
|
rowsWrap.querySelectorAll('.removeSupportTypeBtn').forEach((btn) => {
|
||
|
|
btn.onclick = () => {
|
||
|
|
const row = btn.closest('.support-type-row');
|
||
|
|
if (!row) return;
|
||
|
|
row.remove();
|
||
|
|
normalizeIndexes();
|
||
|
|
};
|
||
|
|
});
|
||
|
|
rowsWrap.querySelectorAll('.addFieldBtn').forEach((btn) => {
|
||
|
|
btn.onclick = () => {
|
||
|
|
const row = btn.closest('.support-type-row');
|
||
|
|
const wrap = row ? row.querySelector('.support-type-fields') : null;
|
||
|
|
if (!wrap) return;
|
||
|
|
const node = fieldTemplate.content.firstElementChild.cloneNode(true);
|
||
|
|
const labelInput = node.querySelector('.option-label');
|
||
|
|
const keyInput = node.querySelector('.option-key');
|
||
|
|
keyInput.value = slugify('option');
|
||
|
|
labelInput.value = '';
|
||
|
|
wrap.appendChild(node);
|
||
|
|
wireButtons();
|
||
|
|
normalizeIndexes();
|
||
|
|
};
|
||
|
|
});
|
||
|
|
rowsWrap.querySelectorAll('.removeFieldBtn').forEach((btn) => {
|
||
|
|
btn.onclick = () => {
|
||
|
|
const row = btn.closest('.support-field-row');
|
||
|
|
if (!row) return;
|
||
|
|
row.remove();
|
||
|
|
normalizeIndexes();
|
||
|
|
};
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
addBtn.addEventListener('click', () => {
|
||
|
|
const node = rowTemplate.content.firstElementChild.cloneNode(true);
|
||
|
|
rowsWrap.appendChild(node);
|
||
|
|
normalizeIndexes();
|
||
|
|
wireButtons();
|
||
|
|
});
|
||
|
|
|
||
|
|
rowsWrap.querySelectorAll('.support-field-row').forEach((row, fieldIndex) => {
|
||
|
|
const keyInput = row.querySelector('.option-key');
|
||
|
|
if (!keyInput) return;
|
||
|
|
if (!keyInput.value) {
|
||
|
|
const labelInput = row.querySelector('.option-label');
|
||
|
|
keyInput.value = slugify(labelInput ? labelInput.value : ('option_' + fieldIndex));
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
normalizeIndexes();
|
||
|
|
wireButtons();
|
||
|
|
})();
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<?php
|
||
|
|
$content = ob_get_clean();
|
||
|
|
require __DIR__ . '/../../../../modules/admin/views/layout.php';
|