Asiakaspalvelu: alinavi-uudelleenjärjestely + tikettityyppien hallinta
Vastauspohjat, Säännöt ja Asetukset siirretty omiksi alinaveikseen tikettilistan overlay-napeista. Säännöt-välilehdelle lisätty tikettityyppien hallinta (lisää/poista). Tyypit tallennetaan tietokantaan yrityskohtaisesti ja populoidaan dynaamisesti kaikkiin dropdown-valikoihin. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
162
script.js
162
script.js
@@ -301,11 +301,8 @@ function switchToTab(target, subTab) {
|
||||
if (target === 'support') {
|
||||
loadTickets(); showTicketListView();
|
||||
if (document.getElementById('ticket-auto-refresh').checked) startTicketAutoRefresh();
|
||||
if (subTab === 'ohjeet') {
|
||||
switchSupportSubTab('support-ohjeet');
|
||||
} else {
|
||||
switchSupportSubTab('support-tickets');
|
||||
}
|
||||
const supportSubMap = { ohjeet: 'support-ohjeet', saannot: 'support-saannot', vastauspohjat: 'support-vastauspohjat', asetukset: 'support-asetukset' };
|
||||
switchSupportSubTab(supportSubMap[subTab] || 'support-tickets');
|
||||
}
|
||||
if (target === 'documents') {
|
||||
if (subTab && subTab !== 'kokoukset') {
|
||||
@@ -1344,7 +1341,7 @@ const ticketStatusLabels = {
|
||||
suljettu: 'Suljettu',
|
||||
};
|
||||
|
||||
const ticketTypeLabels = {
|
||||
let ticketTypeLabels = {
|
||||
laskutus: 'Laskutus',
|
||||
tekniikka: 'Tekniikka',
|
||||
vika: 'Vika',
|
||||
@@ -1520,10 +1517,9 @@ async function showTicketDetail(id, companyId = '') {
|
||||
</div>
|
||||
<div style="display:flex;gap:0.5rem;align-items:center;flex-wrap:wrap;">
|
||||
<select id="ticket-type-select" style="padding:6px 10px;border:2px solid #e0e0e0;border-radius:8px;font-size:0.85rem;">
|
||||
<option value="muu" ${(ticket.type || 'muu') === 'muu' ? 'selected' : ''}>Muu</option>
|
||||
<option value="laskutus" ${ticket.type === 'laskutus' ? 'selected' : ''}>Laskutus</option>
|
||||
<option value="tekniikka" ${ticket.type === 'tekniikka' ? 'selected' : ''}>Tekniikka</option>
|
||||
<option value="vika" ${ticket.type === 'vika' ? 'selected' : ''}>Vika</option>
|
||||
${Object.entries(ticketTypeLabels).map(([val, label]) =>
|
||||
`<option value="${val}" ${(ticket.type || 'muu') === val ? 'selected' : ''}>${esc(label)}</option>`
|
||||
).join('')}
|
||||
</select>
|
||||
<select id="ticket-status-select" style="padding:6px 10px;border:2px solid #e0e0e0;border-radius:8px;font-size:0.85rem;">
|
||||
<option value="uusi" ${ticket.status === 'uusi' ? 'selected' : ''}>Uusi</option>
|
||||
@@ -2022,20 +2018,6 @@ function renderRules() {
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function showRulesView() {
|
||||
document.getElementById('ticket-list-view').style.display = 'none';
|
||||
document.getElementById('ticket-detail-view').style.display = 'none';
|
||||
document.getElementById('ticket-templates-view').style.display = 'none';
|
||||
document.getElementById('ticket-settings-view').style.display = 'none';
|
||||
document.getElementById('ticket-rules-view').style.display = 'block';
|
||||
loadRules();
|
||||
}
|
||||
|
||||
function hideRulesView() {
|
||||
document.getElementById('ticket-rules-view').style.display = 'none';
|
||||
document.getElementById('ticket-list-view').style.display = 'block';
|
||||
}
|
||||
|
||||
function showRuleForm(rule) {
|
||||
document.getElementById('rule-form-container').style.display = '';
|
||||
document.getElementById('rule-form-title').textContent = rule ? 'Muokkaa sääntöä' : 'Uusi sääntö';
|
||||
@@ -2057,8 +2039,6 @@ function hideRuleForm() {
|
||||
editingRuleId = null;
|
||||
}
|
||||
|
||||
document.getElementById('btn-ticket-rules').addEventListener('click', () => showRulesView());
|
||||
document.getElementById('btn-rules-back').addEventListener('click', () => hideRulesView());
|
||||
document.getElementById('btn-add-rule').addEventListener('click', () => showRuleForm(null));
|
||||
document.getElementById('btn-cancel-rule').addEventListener('click', () => hideRuleForm());
|
||||
|
||||
@@ -2108,23 +2088,86 @@ async function toggleRule(id, enabled) {
|
||||
} catch (e) { alert(e.message); }
|
||||
}
|
||||
|
||||
// ==================== TIKETTITYYPIT ====================
|
||||
|
||||
async function loadTicketTypes() {
|
||||
try {
|
||||
const types = await apiCall('ticket_types');
|
||||
ticketTypeLabels = {};
|
||||
types.forEach(t => { ticketTypeLabels[t.value] = t.label; });
|
||||
renderTicketTypes(types);
|
||||
populateTypeDropdowns();
|
||||
} catch (e) { console.error('loadTicketTypes:', e); }
|
||||
}
|
||||
|
||||
function renderTicketTypes(types) {
|
||||
const container = document.getElementById('ticket-types-list');
|
||||
if (!container) return;
|
||||
if (!types || types.length === 0) {
|
||||
container.innerHTML = '<p style="color:#888;font-size:0.85rem;">Ei tikettityyppejä.</p>';
|
||||
return;
|
||||
}
|
||||
container.innerHTML = types.map(t =>
|
||||
`<div class="ticket-type-item">
|
||||
<span class="ticket-type-badge ticket-type-${t.value}">${esc(t.label)}</span>
|
||||
<span style="color:#888;font-size:0.8rem;">(${esc(t.value)})</span>
|
||||
<button class="btn-danger" onclick="deleteTicketType('${esc(t.value)}')" style="padding:2px 8px;font-size:0.75rem;margin-left:auto;">Poista</button>
|
||||
</div>`
|
||||
).join('');
|
||||
}
|
||||
|
||||
function populateTypeDropdowns() {
|
||||
const options = Object.entries(ticketTypeLabels).map(
|
||||
([val, label]) => `<option value="${val}">${esc(label)}</option>`
|
||||
).join('');
|
||||
|
||||
// Tikettilistan suodatin
|
||||
const filter = document.getElementById('ticket-type-filter');
|
||||
if (filter) {
|
||||
const current = filter.value;
|
||||
filter.innerHTML = '<option value="">Kaikki tyypit</option>' + options;
|
||||
filter.value = current;
|
||||
}
|
||||
|
||||
// Sääntölomakkeen tyyppi
|
||||
const ruleType = document.getElementById('rule-form-type');
|
||||
if (ruleType) {
|
||||
const current = ruleType.value;
|
||||
ruleType.innerHTML = '<option value="">— Ei muuteta —</option>' + options;
|
||||
ruleType.value = current;
|
||||
}
|
||||
|
||||
// Tiketin detail-näkymän tyyppi-select
|
||||
const detailType = document.getElementById('ticket-detail-type');
|
||||
if (detailType) {
|
||||
const current = detailType.value;
|
||||
detailType.innerHTML = options;
|
||||
detailType.value = current;
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('btn-add-ticket-type')?.addEventListener('click', async () => {
|
||||
const value = document.getElementById('new-ticket-type-value').value.trim().toLowerCase().replace(/[^a-z0-9_-]/g, '');
|
||||
const label = document.getElementById('new-ticket-type-label').value.trim();
|
||||
if (!value || !label) { alert('Täytä tunnus ja nimi'); return; }
|
||||
try {
|
||||
await apiCall('ticket_type_save', 'POST', { value, label });
|
||||
document.getElementById('new-ticket-type-value').value = '';
|
||||
document.getElementById('new-ticket-type-label').value = '';
|
||||
await loadTicketTypes();
|
||||
} catch (e) { alert(e.message); }
|
||||
});
|
||||
|
||||
window.deleteTicketType = async function(value) {
|
||||
if (!confirm(`Poistetaanko tikettityyppi "${value}"?`)) return;
|
||||
try {
|
||||
await apiCall('ticket_type_delete', 'POST', { value });
|
||||
await loadTicketTypes();
|
||||
} catch (e) { alert(e.message); }
|
||||
};
|
||||
|
||||
// ==================== VASTAUSPOHJAT (TUKITABISSA) ====================
|
||||
|
||||
function showTemplatesView() {
|
||||
document.getElementById('ticket-list-view').style.display = 'none';
|
||||
document.getElementById('ticket-detail-view').style.display = 'none';
|
||||
document.getElementById('ticket-rules-view').style.display = 'none';
|
||||
document.getElementById('ticket-settings-view').style.display = 'none';
|
||||
document.getElementById('ticket-templates-view').style.display = 'block';
|
||||
hideTplForm();
|
||||
renderTplList();
|
||||
}
|
||||
|
||||
function hideTemplatesView() {
|
||||
document.getElementById('ticket-templates-view').style.display = 'none';
|
||||
document.getElementById('ticket-list-view').style.display = 'block';
|
||||
}
|
||||
|
||||
function renderTplList() {
|
||||
const list = document.getElementById('tpl-list');
|
||||
if (!list) return;
|
||||
@@ -2158,11 +2201,6 @@ function hideTplForm() {
|
||||
document.getElementById('tpl-form-container').style.display = 'none';
|
||||
}
|
||||
|
||||
document.getElementById('btn-ticket-templates').addEventListener('click', async () => {
|
||||
await loadTemplates();
|
||||
showTemplatesView();
|
||||
});
|
||||
document.getElementById('btn-templates-back').addEventListener('click', () => hideTemplatesView());
|
||||
document.getElementById('btn-add-tpl').addEventListener('click', () => showTplForm(null));
|
||||
document.getElementById('btn-cancel-tpl').addEventListener('click', () => hideTplForm());
|
||||
|
||||
@@ -2195,14 +2233,7 @@ window.deleteTpl = async function(id) {
|
||||
|
||||
// ==================== OMAT ASETUKSET (TIKETTIEN ASETUKSET) ====================
|
||||
|
||||
async function openTicketSettings() {
|
||||
// Piilota muut näkymät, näytä asetukset
|
||||
document.getElementById('ticket-list-view').style.display = 'none';
|
||||
document.getElementById('ticket-detail-view').style.display = 'none';
|
||||
document.getElementById('ticket-rules-view').style.display = 'none';
|
||||
document.getElementById('ticket-templates-view').style.display = 'none';
|
||||
document.getElementById('ticket-settings-view').style.display = 'block';
|
||||
|
||||
async function initTicketSettings() {
|
||||
const sigContainer = document.getElementById('ticket-settings-signatures');
|
||||
const visContainer = document.getElementById('ticket-settings-mailbox-visibility');
|
||||
sigContainer.innerHTML = '<p style="color:#888;font-size:0.85rem;">Ladataan...</p>';
|
||||
@@ -2239,13 +2270,6 @@ async function openTicketSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
function closeTicketSettings() {
|
||||
document.getElementById('ticket-settings-view').style.display = 'none';
|
||||
document.getElementById('ticket-list-view').style.display = 'block';
|
||||
}
|
||||
|
||||
document.getElementById('btn-ticket-settings').addEventListener('click', () => openTicketSettings());
|
||||
|
||||
document.getElementById('btn-save-ticket-settings').addEventListener('click', async () => {
|
||||
// Kerää allekirjoitukset
|
||||
const signatures = {};
|
||||
@@ -2267,7 +2291,6 @@ document.getElementById('btn-save-ticket-settings').addEventListener('click', as
|
||||
// Päivitä lokaalit muuttujat
|
||||
currentUserSignatures = signatures;
|
||||
currentHiddenMailboxes = hiddenMailboxes;
|
||||
closeTicketSettings();
|
||||
// Lataa tiketit uudelleen suodatuksen päivittämiseksi
|
||||
loadTickets();
|
||||
alert('Asetukset tallennettu!');
|
||||
@@ -3052,8 +3075,19 @@ function switchSupportSubTab(target) {
|
||||
if (btn) btn.classList.add('active');
|
||||
const content = document.getElementById('subtab-' + target);
|
||||
if (content) content.classList.add('active');
|
||||
if (target === 'support-ohjeet') { loadGuides(); window.location.hash = 'support/ohjeet'; }
|
||||
else { window.location.hash = 'support'; }
|
||||
// Lataa data tarvittaessa
|
||||
const hashMap = {
|
||||
'support-tickets': 'support',
|
||||
'support-ohjeet': 'support/ohjeet',
|
||||
'support-saannot': 'support/saannot',
|
||||
'support-vastauspohjat': 'support/vastauspohjat',
|
||||
'support-asetukset': 'support/asetukset',
|
||||
};
|
||||
if (target === 'support-ohjeet') loadGuides();
|
||||
if (target === 'support-saannot') { loadRules(); loadTicketTypes(); }
|
||||
if (target === 'support-vastauspohjat') loadTemplates();
|
||||
if (target === 'support-asetukset') initTicketSettings();
|
||||
window.location.hash = hashMap[target] || 'support';
|
||||
}
|
||||
|
||||
document.querySelectorAll('#support-sub-tab-bar .sub-tab').forEach(btn => {
|
||||
|
||||
Reference in New Issue
Block a user