Versioiva dokumentinhallinta + Laitetilat-moduuli
Dokumentit: versioiva tiedostonhallinta asiakkaille (sopimukset, laskut, ohjeet). Sisältää versiohistorian, tiedostojen latauksen/palautuksen ja asiakas-suodatuksen. Laitetilat: laitetilojen hallinta kuvagallerialla ja tiedostolistauksella. Sisältää korttipohjaisen listanäkymän, kuvien esikatselun ja tiedostojen hallinnan. Molemmat moduulit: 4 DB-taulua, 14 API-endpointtia, täysi CRUD, tiedostoupload. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
520
script.js
520
script.js
@@ -200,7 +200,7 @@ async function showDashboard() {
|
||||
// Avaa oikea tabi URL-hashin perusteella (tai customers oletuks)
|
||||
const hash = window.location.hash.replace('#', '');
|
||||
const [mainHash, subHash] = hash.split('/');
|
||||
const validTabs = ['customers', 'leads', 'tekniikka', 'ohjeet', 'todo', 'archive', 'changelog', 'support', 'users', 'settings', 'companies'];
|
||||
const validTabs = ['customers', 'leads', 'tekniikka', 'ohjeet', 'todo', 'documents', 'laitetilat', 'archive', 'changelog', 'support', 'users', 'settings', 'companies'];
|
||||
const startTab = validTabs.includes(mainHash) ? mainHash : 'customers';
|
||||
switchToTab(startTab, subHash);
|
||||
}
|
||||
@@ -263,6 +263,8 @@ function switchToTab(target, subTab) {
|
||||
if (target === 'ohjeet') loadGuides();
|
||||
if (target === 'todo') { loadTodos(); if (subTab) switchTodoSubTab(subTab); }
|
||||
if (target === 'support') { loadTickets(); showTicketListView(); if (document.getElementById('ticket-auto-refresh').checked) startTicketAutoRefresh(); }
|
||||
if (target === 'documents') { loadDocuments(); showDocsListView(); }
|
||||
if (target === 'laitetilat') { loadLaitetilat(); showLaitetilatListView(); }
|
||||
if (target === 'users') loadUsers();
|
||||
if (target === 'settings') loadSettings();
|
||||
if (target === 'companies') loadCompaniesTab();
|
||||
@@ -4392,9 +4394,523 @@ document.getElementById('btn-time-cancel')?.addEventListener('click', () => {
|
||||
});
|
||||
document.getElementById('btn-time-save')?.addEventListener('click', () => addTimeEntry());
|
||||
|
||||
// ==================== DOKUMENTIT ====================
|
||||
|
||||
let allDocuments = [];
|
||||
let currentDocument = null;
|
||||
|
||||
const docCategoryLabels = {
|
||||
sopimus: 'Sopimus',
|
||||
lasku: 'Lasku',
|
||||
ohje: 'Ohje',
|
||||
raportti: 'Raportti',
|
||||
muu: 'Muu'
|
||||
};
|
||||
|
||||
function showDocsListView() {
|
||||
document.getElementById('docs-list-view').style.display = '';
|
||||
document.getElementById('doc-read-view').style.display = 'none';
|
||||
document.getElementById('doc-edit-view').style.display = 'none';
|
||||
}
|
||||
|
||||
function showDocReadView() {
|
||||
document.getElementById('docs-list-view').style.display = 'none';
|
||||
document.getElementById('doc-read-view').style.display = '';
|
||||
document.getElementById('doc-edit-view').style.display = 'none';
|
||||
}
|
||||
|
||||
function showDocEditView() {
|
||||
document.getElementById('docs-list-view').style.display = 'none';
|
||||
document.getElementById('doc-read-view').style.display = 'none';
|
||||
document.getElementById('doc-edit-view').style.display = '';
|
||||
}
|
||||
|
||||
async function loadDocuments() {
|
||||
try {
|
||||
allDocuments = await apiCall('documents');
|
||||
populateDocCustomerFilter();
|
||||
renderDocumentsList();
|
||||
} catch (e) { console.error('Dokumenttien lataus epäonnistui:', e); }
|
||||
}
|
||||
|
||||
function populateDocCustomerFilter() {
|
||||
const sel = document.getElementById('doc-filter-customer');
|
||||
const existing = sel.value;
|
||||
// Kerää uniikki lista asiakkaista
|
||||
const customerMap = {};
|
||||
allDocuments.forEach(d => {
|
||||
if (d.customer_id) {
|
||||
customerMap[d.customer_id] = d.customer_id; // käytetään myöhemmin nimeä jos saatavilla
|
||||
}
|
||||
});
|
||||
// Käytä customers-listaa nimien näyttämiseen
|
||||
sel.innerHTML = '<option value="">Kaikki asiakkaat</option>';
|
||||
if (typeof customers !== 'undefined' && customers.length > 0) {
|
||||
customers.forEach(c => {
|
||||
sel.innerHTML += `<option value="${c.id}">${esc(c.yritys)}</option>`;
|
||||
});
|
||||
} else {
|
||||
Object.keys(customerMap).forEach(id => {
|
||||
sel.innerHTML += `<option value="${id}">${id}</option>`;
|
||||
});
|
||||
}
|
||||
sel.value = existing || '';
|
||||
}
|
||||
|
||||
function renderDocumentsList() {
|
||||
const query = (document.getElementById('doc-search')?.value || '').toLowerCase().trim();
|
||||
const filterCustomer = document.getElementById('doc-filter-customer')?.value || '';
|
||||
const filterCategory = document.getElementById('doc-filter-category')?.value || '';
|
||||
|
||||
let filtered = allDocuments;
|
||||
if (query) {
|
||||
filtered = filtered.filter(d =>
|
||||
(d.title || '').toLowerCase().includes(query) ||
|
||||
(d.description || '').toLowerCase().includes(query)
|
||||
);
|
||||
}
|
||||
if (filterCustomer) {
|
||||
filtered = filtered.filter(d => d.customer_id === filterCustomer);
|
||||
}
|
||||
if (filterCategory) {
|
||||
filtered = filtered.filter(d => d.category === filterCategory);
|
||||
}
|
||||
|
||||
const tbody = document.getElementById('docs-tbody');
|
||||
const noDocsEl = document.getElementById('no-docs');
|
||||
|
||||
if (filtered.length === 0) {
|
||||
tbody.innerHTML = '';
|
||||
noDocsEl.style.display = '';
|
||||
return;
|
||||
}
|
||||
noDocsEl.style.display = 'none';
|
||||
|
||||
// Hae asiakasnimien map
|
||||
const customerNameMap = {};
|
||||
if (typeof customers !== 'undefined') {
|
||||
customers.forEach(c => { customerNameMap[c.id] = c.yritys; });
|
||||
}
|
||||
|
||||
tbody.innerHTML = filtered.map(d => {
|
||||
const customerName = d.customer_id ? (customerNameMap[d.customer_id] || d.customer_id) : '<span style="color:#aaa;">Yleinen</span>';
|
||||
const catLabel = docCategoryLabels[d.category] || d.category || '-';
|
||||
const version = d.current_version || 0;
|
||||
const date = d.muokattu ? new Date(d.muokattu).toLocaleDateString('fi-FI') : '-';
|
||||
const author = d.version_author || d.created_by || '-';
|
||||
return `<tr onclick="openDocRead('${d.id}')" style="cursor:pointer;">
|
||||
<td><strong>${esc(d.title)}</strong></td>
|
||||
<td>${customerName}</td>
|
||||
<td><span class="doc-category cat-${d.category || 'muu'}">${catLabel}</span></td>
|
||||
<td style="text-align:center;">v${version}</td>
|
||||
<td>${date}</td>
|
||||
<td>${esc(author)}</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
document.getElementById('doc-search')?.addEventListener('input', renderDocumentsList);
|
||||
document.getElementById('doc-filter-customer')?.addEventListener('change', renderDocumentsList);
|
||||
document.getElementById('doc-filter-category')?.addEventListener('change', renderDocumentsList);
|
||||
|
||||
async function openDocRead(docId) {
|
||||
try {
|
||||
currentDocument = await apiCall(`document&id=${docId}`);
|
||||
renderDocReadView();
|
||||
showDocReadView();
|
||||
} catch (e) { alert('Dokumentin avaus epäonnistui: ' + e.message); }
|
||||
}
|
||||
|
||||
function renderDocReadView() {
|
||||
const d = currentDocument;
|
||||
if (!d) return;
|
||||
|
||||
// Asiakasnimen haku
|
||||
let customerName = 'Ei asiakasta (yleinen)';
|
||||
if (d.customer_id && typeof customers !== 'undefined') {
|
||||
const c = customers.find(c => c.id === d.customer_id);
|
||||
if (c) customerName = c.yritys;
|
||||
}
|
||||
|
||||
document.getElementById('doc-read-title').textContent = d.title || '';
|
||||
document.getElementById('doc-read-customer').textContent = '👤 ' + customerName;
|
||||
document.getElementById('doc-read-category').innerHTML = `<span class="doc-category cat-${d.category || 'muu'}">${docCategoryLabels[d.category] || d.category || 'Muu'}</span>`;
|
||||
document.getElementById('doc-read-version').textContent = `📌 Versio ${d.current_version || 0}`;
|
||||
document.getElementById('doc-read-date').textContent = d.muokattu ? '📅 ' + new Date(d.muokattu).toLocaleDateString('fi-FI') : '';
|
||||
document.getElementById('doc-read-description').textContent = d.description || '';
|
||||
|
||||
// Admin-napit
|
||||
const isAdmin = currentUser?.role === 'admin' || currentUser?.role === 'superadmin';
|
||||
document.getElementById('btn-doc-delete').style.display = isAdmin ? '' : 'none';
|
||||
|
||||
// Latausnappi - piilota jos ei versioita
|
||||
document.getElementById('btn-doc-download').style.display = (d.current_version && d.current_version > 0) ? '' : 'none';
|
||||
|
||||
// Versiohistoria
|
||||
const vtbody = document.getElementById('doc-versions-tbody');
|
||||
if (!d.versions || d.versions.length === 0) {
|
||||
vtbody.innerHTML = '<tr><td colspan="6" style="text-align:center;color:#aaa;padding:1rem;">Ei versioita vielä.</td></tr>';
|
||||
} else {
|
||||
vtbody.innerHTML = d.versions.map(v => {
|
||||
const date = v.luotu ? new Date(v.luotu).toLocaleDateString('fi-FI', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }) : '-';
|
||||
const isCurrent = v.version_number === d.current_version;
|
||||
return `<tr${isCurrent ? ' style="background:#f0f7ff;"' : ''}>
|
||||
<td style="font-weight:600;">v${v.version_number}${isCurrent ? ' ✓' : ''}</td>
|
||||
<td>${date}</td>
|
||||
<td>${esc(v.created_by || '-')}</td>
|
||||
<td>${esc(v.change_notes || '-')}</td>
|
||||
<td>${formatFileSize(v.file_size || 0)}</td>
|
||||
<td class="actions-cell">
|
||||
<a href="${API}?action=document_download&id=${d.id}&version=${v.version_number}" target="_blank" title="Lataa">⬇️</a>
|
||||
${isAdmin && !isCurrent ? `<button onclick="restoreDocVersion('${d.id}', '${v.id}', ${v.version_number})" title="Palauta tämä versio" style="background:none;border:none;cursor:pointer;font-size:1rem;">🔄</button>` : ''}
|
||||
</td>
|
||||
</tr>`;
|
||||
}).join('');
|
||||
}
|
||||
}
|
||||
|
||||
// Latausnappi
|
||||
document.getElementById('btn-doc-download')?.addEventListener('click', () => {
|
||||
if (!currentDocument || !currentDocument.current_version) return;
|
||||
window.open(`${API}?action=document_download&id=${currentDocument.id}&version=${currentDocument.current_version}`, '_blank');
|
||||
});
|
||||
|
||||
// Uusi versio
|
||||
document.getElementById('btn-doc-upload-version')?.addEventListener('click', async () => {
|
||||
const fileInput = document.getElementById('doc-version-file');
|
||||
const notesInput = document.getElementById('doc-version-notes');
|
||||
if (!fileInput.files.length) { alert('Valitse tiedosto'); return; }
|
||||
if (!currentDocument) return;
|
||||
|
||||
const fd = new FormData();
|
||||
fd.append('document_id', currentDocument.id);
|
||||
fd.append('file', fileInput.files[0]);
|
||||
fd.append('change_notes', notesInput.value || '');
|
||||
|
||||
try {
|
||||
const res = await fetch(`${API}?action=document_upload`, { method: 'POST', credentials: 'include', body: fd });
|
||||
const text = await res.text();
|
||||
let data;
|
||||
try { data = JSON.parse(text); } catch (e) { throw new Error('Palvelin palautti virheellisen vastauksen'); }
|
||||
if (!res.ok) throw new Error(data.error || 'Virhe');
|
||||
currentDocument = data;
|
||||
renderDocReadView();
|
||||
fileInput.value = '';
|
||||
notesInput.value = '';
|
||||
} catch (e) { alert('Tiedoston lataus epäonnistui: ' + e.message); }
|
||||
});
|
||||
|
||||
async function restoreDocVersion(docId, versionId, versionNum) {
|
||||
if (!confirm(`Palautetaanko versio ${versionNum}? Siitä tulee uusi nykyinen versio.`)) return;
|
||||
try {
|
||||
currentDocument = await apiCall('document_restore', 'POST', { document_id: docId, version_id: versionId });
|
||||
renderDocReadView();
|
||||
} catch (e) { alert('Palautus epäonnistui: ' + e.message); }
|
||||
}
|
||||
|
||||
// Poista dokumentti
|
||||
document.getElementById('btn-doc-delete')?.addEventListener('click', async () => {
|
||||
if (!currentDocument) return;
|
||||
if (!confirm(`Poistetaanko dokumentti "${currentDocument.title}" ja kaikki sen versiot?`)) return;
|
||||
try {
|
||||
await apiCall('document_delete', 'POST', { id: currentDocument.id });
|
||||
currentDocument = null;
|
||||
showDocsListView();
|
||||
loadDocuments();
|
||||
} catch (e) { alert('Poisto epäonnistui: ' + e.message); }
|
||||
});
|
||||
|
||||
// Navigaatio
|
||||
document.getElementById('btn-doc-back')?.addEventListener('click', () => { showDocsListView(); });
|
||||
document.getElementById('btn-doc-edit')?.addEventListener('click', () => { openDocEdit(currentDocument); });
|
||||
document.getElementById('btn-doc-edit-back')?.addEventListener('click', () => {
|
||||
if (currentDocument) showDocReadView();
|
||||
else showDocsListView();
|
||||
});
|
||||
document.getElementById('btn-doc-edit-cancel')?.addEventListener('click', () => {
|
||||
if (currentDocument) showDocReadView();
|
||||
else showDocsListView();
|
||||
});
|
||||
|
||||
// Uusi dokumentti
|
||||
document.getElementById('btn-new-document')?.addEventListener('click', () => { openDocEdit(null); });
|
||||
|
||||
function openDocEdit(doc) {
|
||||
document.getElementById('doc-edit-id').value = doc?.id || '';
|
||||
document.getElementById('doc-edit-name').value = doc?.title || '';
|
||||
document.getElementById('doc-edit-description').value = doc?.description || '';
|
||||
document.getElementById('doc-edit-category').value = doc?.category || 'muu';
|
||||
document.getElementById('doc-edit-title').textContent = doc ? 'Muokkaa dokumenttia' : 'Uusi dokumentti';
|
||||
|
||||
// Täytä asiakas-dropdown
|
||||
const custSel = document.getElementById('doc-edit-customer');
|
||||
custSel.innerHTML = '<option value="">Ei asiakasta (yleinen)</option>';
|
||||
if (typeof customers !== 'undefined') {
|
||||
customers.forEach(c => {
|
||||
custSel.innerHTML += `<option value="${c.id}" ${doc?.customer_id === c.id ? 'selected' : ''}>${esc(c.yritys)}</option>`;
|
||||
});
|
||||
}
|
||||
if (doc?.customer_id) custSel.value = doc.customer_id;
|
||||
|
||||
// Piilota tiedostokenttä muokkaustilassa (versiot hoidetaan read-viewissä)
|
||||
document.getElementById('doc-edit-file').parentElement.style.display = doc ? 'none' : '';
|
||||
|
||||
showDocEditView();
|
||||
}
|
||||
|
||||
// Lomakkeen lähetys
|
||||
document.getElementById('doc-edit-form')?.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const id = document.getElementById('doc-edit-id').value;
|
||||
const docData = {
|
||||
id: id || undefined,
|
||||
title: document.getElementById('doc-edit-name').value.trim(),
|
||||
description: document.getElementById('doc-edit-description').value.trim(),
|
||||
category: document.getElementById('doc-edit-category').value,
|
||||
customer_id: document.getElementById('doc-edit-customer').value || null,
|
||||
created_by: currentUser?.username || ''
|
||||
};
|
||||
|
||||
if (!docData.title) { alert('Otsikko on pakollinen'); return; }
|
||||
|
||||
try {
|
||||
const saved = await apiCall('document_save', 'POST', docData);
|
||||
const docId = saved.id;
|
||||
|
||||
// Jos uusi dokumentti ja tiedosto valittu → lataa ensimmäinen versio
|
||||
const fileInput = document.getElementById('doc-edit-file');
|
||||
if (!id && fileInput.files.length > 0) {
|
||||
const fd = new FormData();
|
||||
fd.append('document_id', docId);
|
||||
fd.append('file', fileInput.files[0]);
|
||||
fd.append('change_notes', 'Ensimmäinen versio');
|
||||
const res = await fetch(`${API}?action=document_upload`, { method: 'POST', credentials: 'include', body: fd });
|
||||
const text = await res.text();
|
||||
let data;
|
||||
try { data = JSON.parse(text); } catch (err) { throw new Error('Tiedoston lataus epäonnistui'); }
|
||||
if (!res.ok) throw new Error(data.error || 'Virhe');
|
||||
}
|
||||
|
||||
currentDocument = await apiCall(`document&id=${docId}`);
|
||||
renderDocReadView();
|
||||
showDocReadView();
|
||||
loadDocuments();
|
||||
} catch (e) { alert('Tallennus epäonnistui: ' + e.message); }
|
||||
});
|
||||
|
||||
// ==================== LAITETILAT ====================
|
||||
|
||||
let allLaitetilat = [];
|
||||
let currentLaitetila = null;
|
||||
|
||||
function showLaitetilatListView() {
|
||||
document.getElementById('laitetilat-list-view').style.display = '';
|
||||
document.getElementById('laitetila-read-view').style.display = 'none';
|
||||
document.getElementById('laitetila-edit-view').style.display = 'none';
|
||||
}
|
||||
|
||||
function showLaitetilaReadView() {
|
||||
document.getElementById('laitetilat-list-view').style.display = 'none';
|
||||
document.getElementById('laitetila-read-view').style.display = '';
|
||||
document.getElementById('laitetila-edit-view').style.display = 'none';
|
||||
}
|
||||
|
||||
function showLaitetilaEditView() {
|
||||
document.getElementById('laitetilat-list-view').style.display = 'none';
|
||||
document.getElementById('laitetila-read-view').style.display = 'none';
|
||||
document.getElementById('laitetila-edit-view').style.display = '';
|
||||
}
|
||||
|
||||
async function loadLaitetilat() {
|
||||
try {
|
||||
allLaitetilat = await apiCall('laitetilat');
|
||||
renderLaitetilatList();
|
||||
} catch (e) { console.error('Laitetilojen lataus epäonnistui:', e); }
|
||||
}
|
||||
|
||||
function renderLaitetilatList() {
|
||||
const grid = document.getElementById('laitetilat-grid');
|
||||
const noEl = document.getElementById('no-laitetilat');
|
||||
|
||||
if (allLaitetilat.length === 0) {
|
||||
grid.innerHTML = '';
|
||||
noEl.style.display = '';
|
||||
return;
|
||||
}
|
||||
noEl.style.display = 'none';
|
||||
|
||||
grid.innerHTML = allLaitetilat.map(t => `
|
||||
<div class="laitetila-card" onclick="openLaitetilaRead('${t.id}')">
|
||||
<h4>${esc(t.nimi)}</h4>
|
||||
<p class="laitetila-osoite">${esc(t.osoite || '')}</p>
|
||||
<p class="laitetila-meta">📁 ${t.file_count || 0} tiedostoa</p>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
async function openLaitetilaRead(tilaId) {
|
||||
try {
|
||||
currentLaitetila = await apiCall(`laitetila&id=${tilaId}`);
|
||||
renderLaitetilaReadView();
|
||||
showLaitetilaReadView();
|
||||
} catch (e) { alert('Laitetilan avaus epäonnistui: ' + e.message); }
|
||||
}
|
||||
|
||||
function renderLaitetilaReadView() {
|
||||
const t = currentLaitetila;
|
||||
if (!t) return;
|
||||
|
||||
document.getElementById('laitetila-read-nimi').textContent = t.nimi || '';
|
||||
document.getElementById('laitetila-read-osoite').textContent = t.osoite ? '📍 ' + t.osoite : '';
|
||||
document.getElementById('laitetila-read-kuvaus').textContent = t.kuvaus || '';
|
||||
|
||||
const isAdmin = currentUser?.role === 'admin' || currentUser?.role === 'superadmin';
|
||||
document.getElementById('btn-laitetila-delete').style.display = isAdmin ? '' : 'none';
|
||||
|
||||
// Erota kuvat ja muut tiedostot
|
||||
const files = t.files || [];
|
||||
const images = files.filter(f => (f.mime_type || '').startsWith('image/'));
|
||||
const otherFiles = files.filter(f => !(f.mime_type || '').startsWith('image/'));
|
||||
|
||||
// Kuvagalleria
|
||||
const gallerySection = document.getElementById('laitetila-gallery');
|
||||
const galleryGrid = document.getElementById('laitetila-gallery-grid');
|
||||
if (images.length > 0) {
|
||||
gallerySection.style.display = '';
|
||||
galleryGrid.innerHTML = images.map(f => {
|
||||
const imgUrl = `${API}?action=laitetila_file_download&laitetila_id=${t.id}&file_id=${f.id}`;
|
||||
return `<div class="gallery-item">
|
||||
<img src="${imgUrl}" alt="${esc(f.original_name)}" onclick="window.open('${imgUrl}', '_blank')" title="Klikkaa avataksesi">
|
||||
<div class="gallery-caption">
|
||||
<span>${esc(f.original_name)}</span>
|
||||
${isAdmin ? `<button onclick="deleteLaitetilaFile('${f.id}')" class="btn-icon" title="Poista">🗑</button>` : ''}
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
} else {
|
||||
gallerySection.style.display = 'none';
|
||||
}
|
||||
|
||||
// Muut tiedostot
|
||||
const filesSection = document.getElementById('laitetila-files-section');
|
||||
const filesList = document.getElementById('laitetila-files-list');
|
||||
if (otherFiles.length > 0) {
|
||||
filesSection.style.display = '';
|
||||
filesList.innerHTML = otherFiles.map(f => {
|
||||
const dlUrl = `${API}?action=laitetila_file_download&laitetila_id=${t.id}&file_id=${f.id}`;
|
||||
return `<div class="laitetila-file-item">
|
||||
<div>
|
||||
<a href="${dlUrl}" target="_blank" class="file-name">${esc(f.original_name)}</a>
|
||||
<span class="file-meta">${formatFileSize(f.file_size || 0)} · ${f.luotu ? new Date(f.luotu).toLocaleDateString('fi-FI') : ''}</span>
|
||||
${f.description ? `<span class="file-desc">${esc(f.description)}</span>` : ''}
|
||||
</div>
|
||||
${isAdmin ? `<button onclick="deleteLaitetilaFile('${f.id}')" class="btn-icon" title="Poista">🗑</button>` : ''}
|
||||
</div>`;
|
||||
}).join('');
|
||||
} else {
|
||||
filesSection.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Tiedoston lataus
|
||||
document.getElementById('btn-laitetila-upload')?.addEventListener('click', async () => {
|
||||
const fileInput = document.getElementById('laitetila-file-input');
|
||||
const descInput = document.getElementById('laitetila-file-desc');
|
||||
if (!fileInput.files.length) { alert('Valitse tiedosto'); return; }
|
||||
if (!currentLaitetila) return;
|
||||
|
||||
for (const file of fileInput.files) {
|
||||
const fd = new FormData();
|
||||
fd.append('laitetila_id', currentLaitetila.id);
|
||||
fd.append('file', file);
|
||||
fd.append('description', descInput.value || '');
|
||||
|
||||
try {
|
||||
const res = await fetch(`${API}?action=laitetila_file_upload`, { method: 'POST', credentials: 'include', body: fd });
|
||||
const text = await res.text();
|
||||
let data;
|
||||
try { data = JSON.parse(text); } catch (e) { throw new Error('Palvelin palautti virheellisen vastauksen'); }
|
||||
if (!res.ok) throw new Error(data.error || 'Virhe');
|
||||
currentLaitetila = data;
|
||||
} catch (e) { alert('Tiedoston lataus epäonnistui: ' + e.message); }
|
||||
}
|
||||
|
||||
renderLaitetilaReadView();
|
||||
fileInput.value = '';
|
||||
descInput.value = '';
|
||||
});
|
||||
|
||||
async function deleteLaitetilaFile(fileId) {
|
||||
if (!confirm('Poistetaanko tiedosto?')) return;
|
||||
try {
|
||||
await apiCall('laitetila_file_delete', 'POST', { id: fileId });
|
||||
// Päivitä näkymä
|
||||
currentLaitetila = await apiCall(`laitetila&id=${currentLaitetila.id}`);
|
||||
renderLaitetilaReadView();
|
||||
} catch (e) { alert('Poisto epäonnistui: ' + e.message); }
|
||||
}
|
||||
|
||||
// Navigaatio
|
||||
document.getElementById('btn-laitetila-back')?.addEventListener('click', () => { showLaitetilatListView(); });
|
||||
document.getElementById('btn-laitetila-edit')?.addEventListener('click', () => { openLaitetilaEdit(currentLaitetila); });
|
||||
document.getElementById('btn-laitetila-edit-back')?.addEventListener('click', () => {
|
||||
if (currentLaitetila) showLaitetilaReadView();
|
||||
else showLaitetilatListView();
|
||||
});
|
||||
document.getElementById('btn-laitetila-edit-cancel')?.addEventListener('click', () => {
|
||||
if (currentLaitetila) showLaitetilaReadView();
|
||||
else showLaitetilatListView();
|
||||
});
|
||||
|
||||
// Poista laitetila
|
||||
document.getElementById('btn-laitetila-delete')?.addEventListener('click', async () => {
|
||||
if (!currentLaitetila) return;
|
||||
if (!confirm(`Poistetaanko laitetila "${currentLaitetila.nimi}" ja kaikki sen tiedostot?`)) return;
|
||||
try {
|
||||
await apiCall('laitetila_delete', 'POST', { id: currentLaitetila.id });
|
||||
currentLaitetila = null;
|
||||
showLaitetilatListView();
|
||||
loadLaitetilat();
|
||||
} catch (e) { alert('Poisto epäonnistui: ' + e.message); }
|
||||
});
|
||||
|
||||
// Uusi laitetila
|
||||
document.getElementById('btn-new-laitetila')?.addEventListener('click', () => { openLaitetilaEdit(null); });
|
||||
|
||||
function openLaitetilaEdit(tila) {
|
||||
document.getElementById('laitetila-edit-id').value = tila?.id || '';
|
||||
document.getElementById('laitetila-edit-nimi').value = tila?.nimi || '';
|
||||
document.getElementById('laitetila-edit-osoite').value = tila?.osoite || '';
|
||||
document.getElementById('laitetila-edit-kuvaus').value = tila?.kuvaus || '';
|
||||
document.getElementById('laitetila-edit-title').textContent = tila ? 'Muokkaa laitetilaa' : 'Uusi laitetila';
|
||||
showLaitetilaEditView();
|
||||
}
|
||||
|
||||
// Lomakkeen lähetys
|
||||
document.getElementById('laitetila-edit-form')?.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const id = document.getElementById('laitetila-edit-id').value;
|
||||
const tilaData = {
|
||||
id: id || undefined,
|
||||
nimi: document.getElementById('laitetila-edit-nimi').value.trim(),
|
||||
osoite: document.getElementById('laitetila-edit-osoite').value.trim(),
|
||||
kuvaus: document.getElementById('laitetila-edit-kuvaus').value.trim()
|
||||
};
|
||||
|
||||
if (!tilaData.nimi) { alert('Nimi on pakollinen'); return; }
|
||||
|
||||
try {
|
||||
const saved = await apiCall('laitetila_save', 'POST', tilaData);
|
||||
currentLaitetila = saved;
|
||||
renderLaitetilaReadView();
|
||||
showLaitetilaReadView();
|
||||
loadLaitetilat();
|
||||
} catch (e) { alert('Tallennus epäonnistui: ' + e.message); }
|
||||
});
|
||||
|
||||
// ==================== MODUULIT ====================
|
||||
|
||||
const ALL_MODULES = ['customers', 'support', 'leads', 'tekniikka', 'ohjeet', 'todo', 'archive', 'changelog', 'settings'];
|
||||
const ALL_MODULES = ['customers', 'support', 'leads', 'tekniikka', 'ohjeet', 'todo', 'documents', 'laitetilat', 'archive', 'changelog', 'settings'];
|
||||
const DEFAULT_MODULES = ['customers', 'support', 'archive', 'changelog', 'settings'];
|
||||
|
||||
function applyModules(modules) {
|
||||
|
||||
Reference in New Issue
Block a user