Add leads (liidit) tab for tracking potential customers
- New Liidit tab with table, search, add/edit/delete - Lead fields: company, contact, phone, email, address, city, status, notes - Status workflow: Uusi → Kontaktoitu → Kiinnostunut → Odottaa toimitusta - Color-coded status badges - Detail view with notes display - "Muuta asiakkaaksi" converts lead to customer with pre-filled data - Lead CRUD endpoints in api.php with changelog logging - leads.json added to .gitignore Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
166
script.js
166
script.js
@@ -182,6 +182,7 @@ document.querySelectorAll('.tab').forEach(tab => {
|
||||
const target = tab.dataset.tab;
|
||||
document.getElementById('tab-content-' + target).classList.add('active');
|
||||
// Lataa sisältö tarvittaessa
|
||||
if (target === 'leads') loadLeads();
|
||||
if (target === 'archive') loadArchive();
|
||||
if (target === 'changelog') loadChangelog();
|
||||
if (target === 'users') loadUsers();
|
||||
@@ -604,6 +605,163 @@ customerForm.addEventListener('submit', async (e) => {
|
||||
await loadCustomers();
|
||||
});
|
||||
|
||||
// ==================== LEADS ====================
|
||||
|
||||
let leads = [];
|
||||
let currentLeadId = null;
|
||||
const leadModal = document.getElementById('lead-modal');
|
||||
const leadDetailModal = document.getElementById('lead-detail-modal');
|
||||
|
||||
const leadStatusLabels = {
|
||||
uusi: 'Uusi',
|
||||
kontaktoitu: 'Kontaktoitu',
|
||||
kiinnostunut: 'Kiinnostunut',
|
||||
odottaa: 'Odottaa toimitusta',
|
||||
ei_kiinnosta: 'Ei kiinnosta',
|
||||
};
|
||||
|
||||
async function loadLeads() {
|
||||
try {
|
||||
leads = await apiCall('leads');
|
||||
renderLeads();
|
||||
} catch (e) { console.error(e); }
|
||||
}
|
||||
|
||||
function renderLeads() {
|
||||
const query = document.getElementById('lead-search-input').value.toLowerCase().trim();
|
||||
let filtered = leads;
|
||||
if (query) {
|
||||
filtered = leads.filter(l =>
|
||||
(l.yritys || '').toLowerCase().includes(query) ||
|
||||
(l.yhteyshenkilo || '').toLowerCase().includes(query) ||
|
||||
(l.kaupunki || '').toLowerCase().includes(query)
|
||||
);
|
||||
}
|
||||
const ltbody = document.getElementById('leads-tbody');
|
||||
const noLeads = document.getElementById('no-leads');
|
||||
if (filtered.length === 0) {
|
||||
ltbody.innerHTML = '';
|
||||
noLeads.style.display = 'block';
|
||||
document.getElementById('leads-table').style.display = 'none';
|
||||
} else {
|
||||
noLeads.style.display = 'none';
|
||||
document.getElementById('leads-table').style.display = 'table';
|
||||
ltbody.innerHTML = filtered.map(l => `<tr data-lead-id="${l.id}">
|
||||
<td><strong>${esc(l.yritys)}</strong></td>
|
||||
<td>${esc(l.yhteyshenkilo || '')}</td>
|
||||
<td>${esc(l.kaupunki || '')}</td>
|
||||
<td><span class="lead-status lead-status-${l.tila || 'uusi'}">${leadStatusLabels[l.tila] || l.tila || 'Uusi'}</span></td>
|
||||
<td class="nowrap">${esc((l.luotu || '').substring(0, 10))}</td>
|
||||
<td class="actions-cell">
|
||||
<button onclick="event.stopPropagation();editLead('${l.id}')" title="Muokkaa">✎</button>
|
||||
<button onclick="event.stopPropagation();deleteLead('${l.id}','${esc(l.yritys)}')" title="Poista">🗑</button>
|
||||
</td>
|
||||
</tr>`).join('');
|
||||
}
|
||||
document.getElementById('lead-count').textContent = `${leads.length} liidiä`;
|
||||
}
|
||||
|
||||
document.getElementById('lead-search-input').addEventListener('input', () => renderLeads());
|
||||
|
||||
document.getElementById('leads-tbody').addEventListener('click', (e) => {
|
||||
const row = e.target.closest('tr');
|
||||
if (row && row.dataset.leadId) showLeadDetail(row.dataset.leadId);
|
||||
});
|
||||
|
||||
function showLeadDetail(id) {
|
||||
const l = leads.find(x => x.id === id);
|
||||
if (!l) return;
|
||||
currentLeadId = id;
|
||||
document.getElementById('lead-detail-title').textContent = l.yritys;
|
||||
document.getElementById('lead-detail-body').innerHTML = `
|
||||
<div style="padding:1.5rem;">
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item"><div class="detail-label">Yritys</div><div class="detail-value">${detailVal(l.yritys)}</div></div>
|
||||
<div class="detail-item"><div class="detail-label">Tila</div><div class="detail-value"><span class="lead-status lead-status-${l.tila || 'uusi'}">${leadStatusLabels[l.tila] || 'Uusi'}</span></div></div>
|
||||
<div class="detail-item"><div class="detail-label">Yhteyshenkilö</div><div class="detail-value">${detailVal(l.yhteyshenkilo)}</div></div>
|
||||
<div class="detail-item"><div class="detail-label">Puhelin</div><div class="detail-value">${detailLink(l.puhelin, 'tel')}</div></div>
|
||||
<div class="detail-item"><div class="detail-label">Sähköposti</div><div class="detail-value">${detailLink(l.sahkoposti, 'email')}</div></div>
|
||||
<div class="detail-item"><div class="detail-label">Osoite</div><div class="detail-value">${detailVal([l.osoite, l.kaupunki].filter(Boolean).join(', '))}</div></div>
|
||||
<div class="detail-item"><div class="detail-label">Lisätty</div><div class="detail-value">${detailVal(l.luotu)} (${esc(l.luoja || '')})</div></div>
|
||||
${l.muokattu ? `<div class="detail-item"><div class="detail-label">Muokattu</div><div class="detail-value">${esc(l.muokattu)} (${esc(l.muokkaaja || '')})</div></div>` : ''}
|
||||
</div>
|
||||
${l.muistiinpanot ? `<div style="margin-top:1.25rem;"><div class="detail-label" style="margin-bottom:0.5rem;">MUISTIINPANOT</div><div style="white-space:pre-wrap;color:#555;background:#f8f9fb;padding:1rem;border-radius:8px;border:1px solid #e8ebf0;font-size:0.9rem;">${esc(l.muistiinpanot)}</div></div>` : ''}
|
||||
</div>`;
|
||||
leadDetailModal.style.display = 'flex';
|
||||
}
|
||||
|
||||
// Lead form
|
||||
document.getElementById('btn-add-lead').addEventListener('click', () => openLeadForm());
|
||||
document.getElementById('lead-modal-close').addEventListener('click', () => leadModal.style.display = 'none');
|
||||
document.getElementById('lead-form-cancel').addEventListener('click', () => leadModal.style.display = 'none');
|
||||
|
||||
function openLeadForm(lead = null) {
|
||||
document.getElementById('lead-modal-title').textContent = lead ? 'Muokkaa liidiä' : 'Lisää liidi';
|
||||
document.getElementById('lead-form-submit').textContent = lead ? 'Päivitä' : 'Tallenna';
|
||||
document.getElementById('lead-form-id').value = lead ? lead.id : '';
|
||||
document.getElementById('lead-form-yritys').value = lead ? lead.yritys : '';
|
||||
document.getElementById('lead-form-yhteyshenkilo').value = lead ? (lead.yhteyshenkilo || '') : '';
|
||||
document.getElementById('lead-form-puhelin').value = lead ? (lead.puhelin || '') : '';
|
||||
document.getElementById('lead-form-sahkoposti').value = lead ? (lead.sahkoposti || '') : '';
|
||||
document.getElementById('lead-form-osoite').value = lead ? (lead.osoite || '') : '';
|
||||
document.getElementById('lead-form-kaupunki').value = lead ? (lead.kaupunki || '') : '';
|
||||
document.getElementById('lead-form-tila').value = lead ? (lead.tila || 'uusi') : 'uusi';
|
||||
document.getElementById('lead-form-muistiinpanot').value = lead ? (lead.muistiinpanot || '') : '';
|
||||
leadModal.style.display = 'flex';
|
||||
document.getElementById('lead-form-yritys').focus();
|
||||
}
|
||||
|
||||
function editLead(id) {
|
||||
const l = leads.find(x => x.id === id);
|
||||
if (l) { leadDetailModal.style.display = 'none'; openLeadForm(l); }
|
||||
}
|
||||
|
||||
async function deleteLead(id, name) {
|
||||
if (!confirm(`Poistetaanko liidi "${name}"?`)) return;
|
||||
await apiCall('lead_delete', 'POST', { id });
|
||||
leadDetailModal.style.display = 'none';
|
||||
await loadLeads();
|
||||
}
|
||||
|
||||
async function convertLeadToCustomer(id) {
|
||||
const l = leads.find(x => x.id === id);
|
||||
if (!l) return;
|
||||
if (!confirm(`Muutetaanko "${l.yritys}" asiakkaaksi?\n\nLiidi poistetaan ja asiakas luodaan sen tiedoilla.`)) return;
|
||||
await apiCall('lead_to_customer', 'POST', { id });
|
||||
leadDetailModal.style.display = 'none';
|
||||
await loadLeads();
|
||||
await loadCustomers();
|
||||
}
|
||||
|
||||
document.getElementById('lead-form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const id = document.getElementById('lead-form-id').value;
|
||||
const data = {
|
||||
yritys: document.getElementById('lead-form-yritys').value,
|
||||
yhteyshenkilo: document.getElementById('lead-form-yhteyshenkilo').value,
|
||||
puhelin: document.getElementById('lead-form-puhelin').value,
|
||||
sahkoposti: document.getElementById('lead-form-sahkoposti').value,
|
||||
osoite: document.getElementById('lead-form-osoite').value,
|
||||
kaupunki: document.getElementById('lead-form-kaupunki').value,
|
||||
tila: document.getElementById('lead-form-tila').value,
|
||||
muistiinpanot: document.getElementById('lead-form-muistiinpanot').value,
|
||||
};
|
||||
if (id) { data.id = id; await apiCall('lead_update', 'POST', data); }
|
||||
else { await apiCall('lead_create', 'POST', data); }
|
||||
leadModal.style.display = 'none';
|
||||
await loadLeads();
|
||||
});
|
||||
|
||||
// Lead detail actions
|
||||
document.getElementById('lead-detail-close').addEventListener('click', () => leadDetailModal.style.display = 'none');
|
||||
document.getElementById('lead-detail-cancel').addEventListener('click', () => leadDetailModal.style.display = 'none');
|
||||
document.getElementById('lead-detail-edit').addEventListener('click', () => editLead(currentLeadId));
|
||||
document.getElementById('lead-detail-delete').addEventListener('click', () => {
|
||||
const l = leads.find(x => x.id === currentLeadId);
|
||||
if (l) deleteLead(currentLeadId, l.yritys);
|
||||
});
|
||||
document.getElementById('lead-detail-convert').addEventListener('click', () => convertLeadToCustomer(currentLeadId));
|
||||
|
||||
// ==================== ARCHIVE ====================
|
||||
|
||||
async function loadArchive() {
|
||||
@@ -656,6 +814,10 @@ const actionLabels = {
|
||||
user_create: 'Lisäsi käyttäjän',
|
||||
user_update: 'Muokkasi käyttäjää',
|
||||
user_delete: 'Poisti käyttäjän',
|
||||
lead_create: 'Lisäsi liidin',
|
||||
lead_update: 'Muokkasi liidiä',
|
||||
lead_delete: 'Poisti liidin',
|
||||
lead_to_customer: 'Muutti liidin asiakkaaksi',
|
||||
};
|
||||
|
||||
async function loadChangelog() {
|
||||
@@ -760,11 +922,15 @@ document.getElementById('user-form').addEventListener('submit', async (e) => {
|
||||
customerModal.addEventListener('click', (e) => { if (e.target === customerModal) customerModal.style.display = 'none'; });
|
||||
detailModal.addEventListener('click', (e) => { if (e.target === detailModal) detailModal.style.display = 'none'; });
|
||||
userModal.addEventListener('click', (e) => { if (e.target === userModal) userModal.style.display = 'none'; });
|
||||
leadModal.addEventListener('click', (e) => { if (e.target === leadModal) leadModal.style.display = 'none'; });
|
||||
leadDetailModal.addEventListener('click', (e) => { if (e.target === leadDetailModal) leadDetailModal.style.display = 'none'; });
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
customerModal.style.display = 'none';
|
||||
detailModal.style.display = 'none';
|
||||
userModal.style.display = 'none';
|
||||
leadModal.style.display = 'none';
|
||||
leadDetailModal.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user