nhance/app/Views/sales/tracker_view.php
2026-02-20 15:26:00 +05:30

356 lines
18 KiB
PHP

<style>
/* POC Exact Styling */
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; color: #333; }
.main-content { flex: 1; display: flex; flex-direction: column; overflow: hidden; height: 100vh; }
.top-bar { background: white; padding: 15px 30px; border-bottom: 1px solid #e0e0e0; display: flex; justify-content: space-between; align-items: center; }
.search-input { width: 400px; padding: 10px 15px; border: 1px solid #e0e0e0; border-radius: 8px; font-size: 14px; outline: none; }
.search-input:focus { border-color: #ff6b35; }
.btn-primary { background: #ff6b35; color: white; border: none; padding: 10px 20px; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.2s; }
.btn-primary:hover { background: #ff5722; transform: translateY(-1px); }
/* Filter Tabs */
.filter-tabs { display: flex; gap: 10px; padding: 20px 30px; background: #f5f5f5; }
.tab { padding: 10px 20px; border-radius: 20px; cursor: pointer; font-size: 14px; background: white; color: #666; border: 1px solid #e0e0e0; transition: all 0.2s; }
.tab.active { background: #ff6b35; color: white; border-color: #ff6b35; }
/* Leads Grid */
.leads-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); gap: 20px; padding: 0 30px 30px; overflow-y: auto; }
.lead-card { background: white; border-radius: 12px; padding: 20px; border: 1px solid #e0e0e0; cursor: pointer; transition: all 0.2s; }
.lead-card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.1); transform: translateY(-2px); }
.lead-status { display: inline-block; padding: 4px 10px; border-radius: 12px; font-size: 11px; font-weight: 500; margin-top: 5px; }
.status-new { background: #e3f2fd; color: #1976d2; }
.status-potential { background: #fff3e0; color: #f57c00; }
.status-prospects { background: #e8f5e9; color: #388e3c; }
/* Modals */
.modal { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 2000; align-items: center; justify-content: center; }
.modal.active { display: flex; }
.modal-content { background: white; border-radius: 12px; width: 90%; max-width: 800px; max-height: 90vh; overflow-y: auto; display: flex; flex-direction: column; }
.modal-header { padding: 25px; border-bottom: 1px solid #e0e0e0; display: flex; justify-content: space-between; align-items: center; }
.modal-body { padding: 25px; flex: 1; }
.form-group { margin-bottom: 20px; }
.form-label { display: block; margin-bottom: 8px; font-size: 14px; font-weight: 500; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; }
/* Activity Type Buttons (POC Style) */
.activity-types { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 20px; }
.activity-type-btn { padding: 12px; border: 1px solid #e0e0e0; background: white; border-radius: 8px; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; font-size: 14px; transition: 0.2s; }
.activity-type-btn:hover { border-color: #ff6b35; background: #fff5f2; }
.activity-type-btn.active { border-color: #ff6b35; background: #ff6b35; color: white; }
/* Timeline Styling */
.timeline { position: relative; padding-left: 30px; margin-top: 20px; }
.timeline-item { position: relative; padding-bottom: 25px; }
.timeline-item::before { content: ''; position: absolute; left: -21px; top: 10px; width: 2px; height: 100%; background: #e0e0e0; }
.timeline-dot { position: absolute; left: -26px; top: 2px; width: 12px; height: 12px; border-radius: 50%; background: #ff6b35; border: 2px solid white; box-shadow: 0 0 0 1px #ff6b35; }
.timeline-dot.completed { background: #4caf50; box-shadow: 0 0 0 1px #4caf50; }
.timeline-content { background: #f8f8f8; padding: 15px; border-radius: 8px; }
.timeline-item {display : block !important;}
</style>
<div class="main-content">
<div class="top-bar">
<input type="text" class="search-input" id="mainSearch" placeholder="Search leads..." onkeyup="fetchLeads()">
<button class="btn-primary" onclick="openModal('addLeadModal')">+ Add Lead</button>
</div>
<div class="filter-tabs">
<div class="tab active" data-filter="all" onclick="setFilter('all', this)">All</div>
<div class="tab" data-filter="New" onclick="setFilter('New', this)">New</div>
<div class="tab" data-filter="Potential" onclick="setFilter('Potential', this)">Potential</div>
<div class="tab" data-filter="Prospects" onclick="setFilter('Prospects', this)">Prospects</div>
</div>
<div class="leads-grid" id="leadsGrid"></div>
</div>
<div class="modal" id="addLeadModal">
<div class="modal-content">
<div class="modal-header">
<h2>Add New Lead</h2>
<button class="btn-primary" style="background:none; color:#666; font-size:24px;" onclick="closeModal('addLeadModal')">&times;</button>
</div>
<form id="addLeadForm" class="modal-body">
<div class="form-group">
<label class="form-label">Company Name</label>
<input type="text" name="company_name" class="search-input" style="width:100%" required>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">Email</label>
<input type="email" name="email" class="search-input" style="width:100%" required>
</div>
<div class="form-group">
<label class="form-label">Phone</label>
<input type="text" name="phone" class="search-input" style="width:100%" required>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">Status</label>
<select name="status" class="search-input" style="width:100%" required>
<option value="New">New</option>
<option value="Potential">Potential</option>
<option value="Prospects">Prospects</option>
<option value="Non prospects">Non prospects</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Assign To</label>
<select name="assigned_to" class="search-input" style="width:100%">
<?php foreach($users as $user): ?>
<option value="<?= $user['id'] ?>"><?= $user['first_name'] ?> </option>
<?php endforeach; ?>
</select>
</div>
</div>
<div style="text-align:right; border-top:1px solid #eee; padding-top:20px;">
<button type="button" class="btn-primary" style="background:#eee; color:#333; margin-right:10px;" onclick="closeModal('addLeadModal')">Cancel</button>
<button type="submit" class="btn-primary">Create Lead</button>
</div>
</form>
</div>
</div>
<div class="modal" id="leadDetailModal">
<div class="modal-content" style="max-width: 850px;">
<div class="modal-header">
<div>
<h2 id="det_company">Lead Detail</h2>
<span id="det_status_badge" class="lead-status"></span>
</div>
<button class="btn-primary" style="background:none; color:#666; font-size:24px;" onclick="closeModal('leadDetailModal')">&times;</button>
</div>
<div class="modal-body">
<div style="background:#f8f8f8; padding:20px; border-radius:12px; margin-bottom:20px; display:grid; grid-template-columns: 1fr 1fr; gap:15px;">
<div><small style="color:#999">Email</small><div id="det_email" style="font-weight:500"></div></div>
<div><small style="color:#999">Phone</small><div id="det_phone" style="font-weight:500"></div></div>
<div><small style="color:#999">Owner</small><div id="det_owner" style="font-weight:500"></div></div>
<div><small style="color:#999">Address</small><div id="det_address" style="font-weight:500">N/A</div></div>
</div>
<div style="display:flex; justify-content:space-between; align-items:center; border-bottom:1px solid #eee; margin-bottom:20px;">
<h3 style="padding-bottom:10px; border-bottom:2px solid #ff6b35;">Activity Timeline</h3>
<button class="btn-primary" style="padding:6px 15px; font-size:12px;" onclick="openActivityModal()">+ Add Activity</button>
</div>
<div id="timelineContainer" class="timeline"></div>
</div>
</div>
</div>
<div class="modal" id="activityModal">
<div class="modal-content" style="max-width: 600px;">
<div class="modal-header">
<h2>Add Activity</h2>
<button class="btn-primary" style="background:none; color:#666; font-size:24px;" onclick="closeModal('activityModal')">&times;</button>
</div>
<form id="activityForm" class="modal-body">
<input type="hidden" id="act_lead_id">
<label class="form-label">Activity Type</label>
<div class="activity-types" id="typeButtons">
<button type="button" class="activity-type-btn active" onclick="selectType('Call', this)">📞 Call</button>
<button type="button" class="activity-type-btn" onclick="selectType('Email', this)">✉️ Email</button>
<button type="button" class="activity-type-btn" onclick="selectType('Meeting', this)">📅 Meeting</button>
<button type="button" class="activity-type-btn" onclick="selectType('Demo', this)">🎬 Demo</button>
<button type="button" class="activity-type-btn" onclick="selectType('Share', this)">📄 Share Docs</button>
<button type="button" class="activity-type-btn" onclick="selectType('Todo', this)">✓ To Do</button>
</div>
<div class="form-group">
<label class="form-label">Notes</label>
<textarea id="act_notes" class="search-input" style="width:100%; height:100px;" placeholder="Add notes about this activity..." required></textarea>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">Scheduled Date & Time</label>
<input type="datetime-local" id="act_date" class="search-input" style="width:100%" required>
</div>
<div class="form-group">
<label class="form-label">Assigned To</label>
<select id="act_owner" class="search-input" style="width:100%">
<?php foreach($users as $user): ?>
<option value="<?= $user['id'] ?>"><?= $user['first_name'] ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div style="text-align:right; margin-top:10px;">
<button type="submit" class="btn-primary" style="width:100%">Create Activity</button>
</div>
</form>
</div>
</div>
<div class="modal" id="completeModal">
<div class="modal-content" style="max-width: 500px;">
<div class="modal-header">
<h3>Complete Activity</h3>
<button class="btn-primary" style="background:none; color:#666; font-size:24px;" onclick="closeModal('completeModal')">&times;</button>
</div>
<form id="completeForm" class="modal-body">
<input type="hidden" id="comp_id">
<div class="form-group">
<label class="form-label">Outcome Notes</label>
<textarea id="comp_notes" class="search-input" style="width:100%; height:80px;" required></textarea>
</div>
<div class="form-group">
<label class="form-label">Next Follow-up?</label>
<select id="do_follow" class="search-input" onchange="document.getElementById('f_up').style.display=this.value==='yes'?'block':'none'">
<option value="no">No</option>
<option value="yes">Yes</option>
</select>
</div>
<div id="f_up" style="display:none; border-top:1px dashed #ccc; padding-top:15px;">
<div class="form-group">
<label class="form-label">Date</label>
<input type="datetime-local" id="f_date" class="search-input">
</div>
</div>
<button type="submit" class="btn-primary" style="width:100%; margin-top:15px;">Submit Outcome</button>
</form>
</div>
</div>
<script>
const API = '<?= base_url('sales') ?>';
let filter = 'all';
let lead_id = null;
let selectedType = 'Call';
function openModal(id) { document.getElementById(id).classList.add('active'); }
function closeModal(id) { document.getElementById(id).classList.remove('active'); }
function selectType(val, el) {
document.querySelectorAll('.activity-type-btn').forEach(b => b.classList.remove('active'));
el.classList.add('active');
selectedType = val;
}
// 1. Leads Grid Logic
async function fetchLeads() {
const q = document.getElementById('mainSearch').value;
const res = await fetch(`${API}/leads?status=${filter==='all'?'':filter}&search=${q}`);
const json = await res.json();
document.getElementById('leadsGrid').innerHTML = json.data.map(l => `
<div class="lead-card" onclick="viewDetail(${l.lead_id})">
<div style="font-weight:600; font-size:17px;">${l.company_name}</div>
<span class="lead-status status-${l.status.toLowerCase().replace(' ', '-')}">${l.status}</span>
<div style="margin-top:12px; color:#666; font-size:13px;">
<div>✉️ ${l.email}</div>
<div>📞 ${l.phone}</div>
</div>
<div style="margin-top:15px; border-top:1px solid #f0f0f0; padding-top:10px; font-size:12px;">
Assigned to: <b>${l.assigned_to_name || 'Unassigned'}</b>
</div>
</div>
`).join('');
}
// 2. Detail Logic
async function viewDetail(id) {
lead_id = id;
const res = await fetch(`${API}/leads/${id}`);
const json = await res.json();
const l = json.data;
document.getElementById('det_company').innerText = l.company_name;
document.getElementById('det_email').innerText = l.email;
document.getElementById('det_phone').innerText = l.phone;
document.getElementById('det_owner').innerText = l.assigned_to_name;
document.getElementById('det_address').innerText = l.address || 'N/A';
const badge = document.getElementById('det_status_badge');
badge.innerText = l.status;
badge.className = `lead-status status-${l.status.toLowerCase().replace(' ', '-')}`;
renderTimeline(l.activities);
openModal('leadDetailModal');
}
function renderTimeline(acts) {
const cont = document.getElementById('timelineContainer');
cont.innerHTML = acts.length ? acts.map(a => `
<div class="timeline-item">
<div class="timeline-dot ${a.status==='completed'?'completed':''}"></div>
<div class="timeline-content">
<div style="display:flex; justify-content:space-between; margin-bottom:5px;">
<b>${a.activity_type}</b>
<span style="font-size:11px; color:#999">${a.scheduled_date}</span>
</div>
<div style="font-size:13px; color:#444;">${a.notes}</div>
${a.status === 'pending' ?
`<button class="btn-primary" style="padding:4px 10px; font-size:11px; margin-top:8px;" onclick="openComp(${a.activity_id})">Mark Complete</button>` :
`<div style="font-size:12px; color:#388e3c; margin-top:8px; font-weight:500;">✓ Outcome: ${a.completion_notes}</div>`
}
</div>
</div>
`).join('') : '<p style="color:#bbb; text-align:center;">No activities logged yet.</p>';
}
function openActivityModal() {
document.getElementById('act_lead_id').value = lead_id;
openModal('activityModal');
}
function openComp(id) {
document.getElementById('comp_id').value = id;
openModal('completeModal');
}
// 3. Form Submissions
document.getElementById('addLeadForm').onsubmit = async (e) => {
e.preventDefault();
const data = Object.fromEntries(new FormData(e.target).entries());
const res = await fetch(`${API}/leads`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
});
if(res.ok) { closeModal('addLeadModal'); fetchLeads(); e.target.reset(); }
else { const err = await res.json(); alert(err.messages.status || 'Error adding lead'); }
};
document.getElementById('activityForm').onsubmit = async (e) => {
e.preventDefault();
const payload = {
lead_id: document.getElementById('act_lead_id').value,
activity_type: selectedType,
notes: document.getElementById('act_notes').value,
scheduled_date: document.getElementById('act_date').value,
assigned_to: document.getElementById('act_owner').value,
status: 'pending'
};
const res = await fetch(`${API}/activities`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload)
});
if(res.ok) { closeModal('activityModal'); viewDetail(lead_id); }
};
document.getElementById('completeForm').onsubmit = async (e) => {
e.preventDefault();
const payload = {
completion_notes: document.getElementById('comp_notes').value,
create_followup: document.getElementById('do_follow').value === 'yes',
followup_date: document.getElementById('f_date').value
};
const res = await fetch(`${API}/activities/${document.getElementById('comp_id').value}/complete`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload)
});
if(res.ok) { closeModal('completeModal'); viewDetail(lead_id); }
};
function setFilter(val, el) {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
el.classList.add('active');
filter = val;
fetchLeads();
}
fetchLeads();
</script>