Dokumentit: asiakaskohtaiset kansiot

- Dokumentit-tab näyttää ensin asiakaskansioruudukon (jokainen asiakas = oma kansio)
- Klikkaamalla asiakaskansiota → avaa asiakkaan dokumenttilista
- Takaisin-nappi palaa kansionäkymään
- Asiakas-sarake poistettu dokumenttitaulusta (tarpeeton kansiossa)
- Asiakas-dropdown piilotettu dokumentin luonnissa (valitaan automaattisesti)
- Hakukenttä asiakkaiden suodatukseen kansionäkymässä
- Kansiot järjestetty: ensin eniten dokumentteja, sitten aakkosittain
- URL hash tuki: #documents/customerId

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 09:57:56 +02:00
parent de384b5cb9
commit 711193e1ce
3 changed files with 197 additions and 45 deletions

167
script.js
View File

@@ -307,7 +307,18 @@ function switchToTab(target, subTab) {
switchSupportSubTab('support-tickets');
}
}
if (target === 'documents') { loadDocuments(); showDocsListView(); if (subTab === 'kokoukset') switchDocSubTab('docs-kokoukset'); else switchDocSubTab('docs-all'); }
if (target === 'documents') {
if (subTab && subTab !== 'kokoukset') {
// subTab on customer_id → avaa suoraan asiakkaan kansio
currentDocCustomerId = subTab;
loadDocuments();
openDocCustomerFolder(subTab);
} else {
currentDocCustomerId = null;
loadDocuments();
showDocCustomerFoldersView();
}
}
if (target === 'netadmin') loadNetadmin();
if (target === 'users') loadUsers();
if (target === 'settings') loadSettings();
@@ -4833,19 +4844,31 @@ const docCategoryLabels = {
muu: 'Muu'
};
let currentDocCustomerId = null; // Valittu asiakaskansio
function showDocCustomerFoldersView() {
document.getElementById('docs-customer-folders-view').style.display = '';
document.getElementById('docs-list-view').style.display = 'none';
document.getElementById('doc-read-view').style.display = 'none';
document.getElementById('doc-edit-view').style.display = 'none';
}
function showDocsListView() {
document.getElementById('docs-customer-folders-view').style.display = 'none';
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-customer-folders-view').style.display = 'none';
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-customer-folders-view').style.display = 'none';
document.getElementById('docs-list-view').style.display = 'none';
document.getElementById('doc-read-view').style.display = 'none';
document.getElementById('doc-edit-view').style.display = '';
@@ -4855,36 +4878,115 @@ async function loadDocuments() {
try {
allDocuments = await apiCall('documents');
try { allDocFolders = await apiCall('document_folders'); } catch (e2) { allDocFolders = []; }
populateDocCustomerFilter();
renderDocFolderBar();
renderDocumentsList();
if (currentDocCustomerId) {
// Ollaan asiakkaan kansion sisällä → näytä dokumenttilista
renderDocFolderBar();
renderDocumentsList();
} else {
// Näytä asiakaskansiot
renderDocCustomerFolders();
}
} 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 = {};
function renderDocCustomerFolders() {
const grid = document.getElementById('doc-customer-folders-grid');
const noFolders = document.getElementById('no-doc-folders');
const search = (document.getElementById('doc-folder-search')?.value || '').toLowerCase().trim();
// Hae asiakasnimien map
const customerNameMap = {};
if (typeof customers !== 'undefined') {
customers.forEach(c => { customerNameMap[c.id] = c.yritys; });
}
// Laske dokumenttien määrä per asiakas
const docCountMap = {};
allDocuments.forEach(d => {
if (d.customer_id) {
customerMap[d.customer_id] = d.customer_id; // käytetään myöhemmin nimeä jos saatavilla
docCountMap[d.customer_id] = (docCountMap[d.customer_id] || 0) + 1;
}
});
// Käytä customers-listaa nimien näyttämiseen
sel.innerHTML = '<option value="">Kaikki asiakkaat</option>';
if (typeof customers !== 'undefined' && customers.length > 0) {
// Näytä kaikki asiakkaat joilla on dokumentteja TAI kaikki aktiiviset asiakkaat
let folderList = [];
if (typeof customers !== 'undefined') {
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>`;
const count = docCountMap[c.id] || 0;
folderList.push({ id: c.id, name: c.yritys || c.id, count });
});
}
sel.value = existing || '';
// Lisää asiakkaat jotka ovat dokumenteissa mutta eivät customers-listassa
Object.keys(docCountMap).forEach(custId => {
if (!folderList.find(f => f.id === custId)) {
folderList.push({ id: custId, name: customerNameMap[custId] || custId, count: docCountMap[custId] });
}
});
// Suodata hakusanalla
if (search) {
folderList = folderList.filter(f => f.name.toLowerCase().includes(search));
}
// Järjestä: ensin ne joilla on dokumentteja (count desc), sitten aakkosjärjestys
folderList.sort((a, b) => {
if (b.count !== a.count) return b.count - a.count;
return a.name.localeCompare(b.name, 'fi');
});
if (folderList.length === 0) {
grid.innerHTML = '';
noFolders.style.display = '';
return;
}
noFolders.style.display = 'none';
grid.innerHTML = folderList.map(f => `
<div class="doc-customer-folder${f.count === 0 ? ' empty' : ''}" onclick="openDocCustomerFolder('${f.id}')">
<div class="doc-customer-folder-icon">🏢</div>
<div class="doc-customer-folder-name">${esc(f.name)}</div>
<div class="doc-customer-folder-count">${f.count} ${f.count === 1 ? 'dokumentti' : 'dokumenttia'}</div>
</div>
`).join('');
}
function openDocCustomerFolder(customerId) {
currentDocCustomerId = customerId;
currentDocFolderId = null;
docSubTabMode = 'docs-all';
// Aseta otsikko
const customerNameMap = {};
if (typeof customers !== 'undefined') {
customers.forEach(c => { customerNameMap[c.id] = c.yritys; });
}
const name = customerNameMap[customerId] || customerId;
document.getElementById('docs-list-title').textContent = '📄 ' + name;
// Reset sub-tab
document.querySelectorAll('#doc-sub-tab-bar .sub-tab').forEach(t => t.classList.remove('active'));
const allBtn = document.querySelector('[data-doc-subtab="docs-all"]');
if (allBtn) allBtn.classList.add('active');
document.getElementById('btn-new-document').style.display = '';
document.getElementById('btn-new-meeting-note').style.display = 'none';
showDocsListView();
renderDocFolderBar();
renderDocumentsList();
window.location.hash = 'documents/' + customerId;
}
function backToDocCustomerFolders() {
currentDocCustomerId = null;
currentDocFolderId = null;
showDocCustomerFoldersView();
renderDocCustomerFolders();
window.location.hash = 'documents';
}
document.getElementById('btn-docs-back-to-folders')?.addEventListener('click', backToDocCustomerFolders);
document.getElementById('doc-folder-search')?.addEventListener('input', renderDocCustomerFolders);
// ---- Kansionavigointi ----
function renderDocFolderBar() {
@@ -4974,11 +5076,15 @@ document.querySelectorAll('#doc-sub-tab-bar .sub-tab').forEach(btn => {
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;
// Suodata valitun asiakkaan perusteella
if (currentDocCustomerId) {
filtered = filtered.filter(d => d.customer_id === currentDocCustomerId);
}
// Sub-tab suodatus: kokoukset = vain kokousmuistiot
if (docSubTabMode === 'docs-kokoukset') {
filtered = filtered.filter(d => d.category === 'kokousmuistio');
@@ -5000,9 +5106,6 @@ function renderDocumentsList() {
(d.description || '').toLowerCase().includes(query)
);
}
if (filterCustomer) {
filtered = filtered.filter(d => d.customer_id === filterCustomer);
}
if (filterCategory && docSubTabMode !== 'docs-kokoukset') {
filtered = filtered.filter(d => d.category === filterCategory);
}
@@ -5017,21 +5120,13 @@ function renderDocumentsList() {
}
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>
@@ -5041,7 +5136,6 @@ function renderDocumentsList() {
}
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) {
@@ -5267,7 +5361,7 @@ function openDocEdit(doc, forceCategory, forceCustomerId) {
? (isMeeting ? 'Muokkaa kokousmuistiota' : 'Muokkaa dokumenttia')
: (isMeeting ? 'Uusi kokousmuistio' : 'Uusi dokumentti');
// Täytä asiakas-dropdown
// Aseta asiakas automaattisesti nykyisen kansion perusteella
const custSel = document.getElementById('doc-edit-customer');
custSel.innerHTML = '<option value="">Ei asiakasta (yleinen)</option>';
if (typeof customers !== 'undefined') {
@@ -5275,8 +5369,13 @@ function openDocEdit(doc, forceCategory, forceCustomerId) {
custSel.innerHTML += `<option value="${c.id}" ${(forceCustomerId === c.id || doc?.customer_id === c.id) ? 'selected' : ''}>${esc(c.yritys)}</option>`;
});
}
if (forceCustomerId) custSel.value = forceCustomerId;
// Aseta customer_id: kansionäkymästä tai parametrista
const effectiveCustomerId = forceCustomerId || currentDocCustomerId;
if (effectiveCustomerId) custSel.value = effectiveCustomerId;
else if (doc?.customer_id) custSel.value = doc.customer_id;
// Piilota asiakas-dropdown kun ollaan asiakkaan kansiossa (automaattinen valinta)
const custGroup = custSel.closest('.form-group');
if (custGroup) custGroup.style.display = currentDocCustomerId ? 'none' : '';
// Toggle kokousmuistio vs tiedostokenttä
toggleDocMeetingFields(cat);