nhance/app/Views/sales/sales_manager_level_dashboard.php
2026-03-04 18:32:11 +05:30

368 lines
19 KiB
PHP

<style>
.my-dash { padding: 25px; margin-top: -21px; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #fff; }
.target-card { background: #1e252b; color: white; padding: 30px; border-radius: 15px; position: relative; margin-bottom: 30px; }
.target-amount { font-size: 32px; font-weight: bold; margin: 10px 0; }
.progress-container { background: #333; height: 8px; border-radius: 4px; margin: 20px 0; overflow: hidden; position: relative; }
.progress-bar { background: #ff6b35; height: 100%; border-radius: 4px; }
.stats-row { display: flex; gap: 15px; margin-top: 25px; }
.stat-box { background: rgba(255,255,255,0.05); padding: 15px; border-radius: 10px; flex: 1; text-align: left; border: 1px solid rgba(255,255,255,0.1); }
.stat-box .num { font-size: 20px; font-weight: bold; display: block; margin-bottom: 2px; }
.stat-box .lbl { font-size: 11px; color: #999; }
/* .chart-circle { position: absolute; right: 40px; top: 35px; width: 75px; height: 75px; border: 6px solid #333; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 18px; border-top-color: #ff6b35; } */
.dashboard-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 25px; }
.list-card { border: 1px solid #f0f0f0; border-radius: 12px; padding: 25px; background: #fff; }
.list-item { display: flex; justify-content: space-between; align-items: center; padding: 15px 0; border-bottom: 1px solid #f9f9f9; }
.badge-done { background: #00c853; color: white; padding: 5px 15px; border-radius: 8px; font-size: 11px; font-weight: 600; cursor: pointer; border: none; }
.lead-initial { width: 35px; height: 35px; background: #f0f0f0; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; color: #ff6b35; }
.status-pill { font-size: 10px; padding: 2px 10px; border-radius: 12px; font-weight: 600; }
/* 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: 5px 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; }
.empty-state { text-align: center; padding: 60px 20px; color: #999; }
.empty-icon { width: 80px; height: 80px; margin: 0 auto 20px; background: #f5f5f5; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 36px; }
input, select, textarea { background-color: white !important; border-radius: 6px !important; box-shadow: none !important; border-color: #ddd !important; }
input, select, textarea { width: 400px; padding: 10px 15px; border: 1px solid #e0e0e0; border-radius: 8px; font-size: 14px; outline: none; }
input:focus, select:focus, textarea:focus { border-color: #ff6b35; outline: none; /* removes blue browser outline */ }
.modal .form-control { transition: 0.2s ease; }
.modal .form-control:focus { border-color: #ff6b35; box-shadow: 0 0 0 2px rgba(255, 107, 53, 0.2);
outline: none;
}
.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); }
.btn-complete { background: #4caf50; color: white; border: none;padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 13px; transition: all 0.2s;}
.btn-complete:hover { background: #4caf50; transform: translateY(-1px); }
.btn-view { background: #f0f0f0; color: #666; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 13px; transition: all 0.2s;}
.btn-view:hover { background: #f0f0f0; transform: translateY(-1px); }
/* Fix for the grid layout on smaller screens */
@media (max-width: 992px) {
.dashboard-grid { grid-template-columns: 1fr; }
}
/* Fix for inputs inside modal rows */
/* input, select, textarea {
width: 100%; * Change from 400px to 100% *
box-sizing: border-box; * Ensures padding doesn't add to width *
padding: 10px 15px;
border: 1px solid #e0e0e0;
border-radius: 8px;
font-size: 14px;
} */
</style>
<div class="my-dash">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:25px;">
<div>
<!-- <h1 style="font-size:22px; margin:0; font-weight: 700;">My Dashboard</h1>
<p style="color:#666; font-size:13px; margin-top: 4px;">Welcome back, <?= $user_name ?></p> -->
</div>
<div style="position: relative;">
<select id="financial_year" name="financial_year" onchange="onFinancialYearChange(this)" style="width: 250px; font-size: 13px; cursor: pointer;">
<?php if(!empty($fin_years)): ?>
<?php foreach($fin_years as $year): ?>
<option value="<?= $year; ?>"><?= $year; ?></option>
<?php endforeach; ?>
<?php endif; ?>
</select>
</div>
</div>
<div class="target-card">
<div style="font-size:11px; color:#999; letter-spacing: 1px; font-weight: 600;">YEARLY TARGET</div>
<div class="target-amount">₹<?= number_format($target_amt / 100000, 1) ?>L</div>
<div style="font-size:12px; color:#777;"><?= $display_fin_years ?></div>
<div class="chart-circle" style="position: absolute; right: 40px; top: 35px; width: 75px; height: 75px;">
<svg width="75" height="75" viewBox="0 0 75 75" style="transform: rotate(-90deg);">
<circle cx="37.5" cy="37.5" r="30" fill="none" stroke="#333" stroke-width="6"/>
<circle cx="37.5" cy="37.5" r="30" fill="none" stroke="#ff6b35" stroke-width="6"
stroke-dasharray="<?= round(2 * M_PI * 30 * $percent / 100, 2) ?> 188.5"
stroke-linecap="round"/>
</svg>
<span style="position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); color:#ff6b35; font-weight:bold; font-size:14px;"><?= $percent ?>%</span>
</div>
<div class="progress-container">
<div class="progress-bar" style="width: <?= $percent ?>%"></div>
</div>
<div style="display:flex; justify-content:space-between; font-size:12px; color: #999;">
<span>Achieved <strong style="color:white">₹<?= number_format($achieved / 100000, 1) ?>L</strong></span>
<span>Remaining <strong style="color:white">₹<?= number_format($remaining / 100000, 1) ?>L</strong></span>
</div>
<div class="stats-row">
<div class="stat-box"><span class="num"><?= $acts['total'] ?></span><span class="lbl">My Activities</span></div>
<div class="stat-box"><span class="num"><?= $acts['pending'] ?></span><span class="lbl">Pending</span></div>
<div class="stat-box"><span class="num"><?= $acts['completed'] ?></span><span class="lbl">Completed</span></div>
<div class="stat-box"><span class="num"><?= $lead_count ?></span><span class="lbl">My Leads</span></div>
</div>
</div>
<div class="dashboard-grid">
<div class="list-card">
<div style="display:flex; justify-content:space-between; margin-bottom:15px; align-items: center;">
<h3 style="font-size:16px; font-weight: 700; margin: 0;">My Upcoming Activities</h3>
<span style="color:#ff6b35; font-size:12px; font-weight: 600;"><?= $acts['pending'] ?> pending</span>
</div>
<!-- <?php foreach($upcoming as $u): ?>
<div class="list-item">
<div>
<div style="font-weight:600; font-size:14px; color: #333;"><?= $u['company_name'] ?></div>
<div style="font-size:11px; color:#999; margin-top: 3px;"><?= strtoupper($u['activity_type'] ?? 'EMAIL') ?> - <?= date('d Mar Y', strtotime($u['scheduled_date'])) ?></div>
</div>
<button class="badge-done" onclick="openComp(<?= $u['activity_id'] ?>,<?= $u['lead_id'] ?>)">✓ Mark as completed</button>
</div>
<?php endforeach; ?> -->
<?php if (!empty($upcoming)) : ?>
<?php foreach($upcoming as $u):
$activityIcons = [ 'Call' => '📞', 'Email' => '✉️', 'Meeting' => '📅', 'Visit' => '🚗', 'Demo' => '🖥️', 'Share Docs' => '📄', 'To Do' => '✓' ];
$icon = $activityIcons[$u['activity_type']] ?? '📌';
$formattedscheduledDate = date('M d, Y, h:i A', strtotime($u['scheduled_date']));
?>
<div class="list-item" style="display: flex; align-items: center; gap: 15px;">
<div class="lead-initial" style="flex-shrink: 0;">
<?= $icon ?>
</div>
<div style="flex-grow: 1; display: flex; flex-direction: column; min-width: 0;">
<div style="font-weight: 700; font-size: 14px; color: #333; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
<?= esc($u['company_name']) ?>
</div>
<div style="font-size: 11px; color: #999; margin-top: 2px; white-space: nowrap;">
<span style="color: #ff6b35; font-weight: 600;"><?= strtoupper($u['activity_type'] ?? 'EMAIL') ?></span>
- <?= $formattedscheduledDate ?>
</div>
</div>
<div style="flex-shrink: 0;">
<button class="badge-done" onclick="openComp(<?= $u['activity_id'] ?>, <?= $u['lead_id'] ?>)">
✓ Mark as completed
</button>
</div>
</div>
<?php endforeach; ?>
<?php else : ?>
<div class="text-center text-muted py-3">
No Upcoming Activities for this Financial Year.
</div>
<?php endif; ?>
</div>
<div class="list-card">
<h3 style="font-size:16px; font-weight: 700; margin-bottom:20px;">My Recent Leads</h3>
<?php if (!empty($recent_leads)) : ?>
<?php foreach($recent_leads as $rl): ?>
<!-- <div class="list-item">
<div style="display:flex; gap:12px; align-items:center;">
<div class="lead-initial"><?= substr($rl['company_name'], 0, 1) ?></div>
<div>
<div style="font-weight:600; font-size:14px; color: #333;"><?= $rl['company_name'] ?></div>
<div style="font-size:11px; color:#999; margin-top: 2px;"><?= $rl['email'] ?></div>
</div>
</div>
<span class="status-pill" style="background: #e3f2fd; color: #1976d2;"><?= $rl['status'] ?></span>
</div> -->
<div class="list-item" style="display: flex; align-items: center; gap: 15px; padding: 12px 0; border-bottom: 1px solid #f9f9f9;">
<div style="flex-shrink: 0;">
<div class="avatar-title"
style="background-color: #f1f3fa; width: 42px; height: 42px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #1976d2; font-weight: 700; font-size: 16px;">
<?php
$firstLetter = !empty($rl['company_name']) ? substr($rl['company_name'], 0, 1) : '?';
echo strtoupper(esc($firstLetter));
?>
</div>
</div>
<div style="flex-grow: 1; min-width: 0;">
<div style="font-weight: 600; font-size: 14px; color: #333; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
<?= esc($rl['company_name']) ?>
</div>
<div style="font-size: 11px; color: #999; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
<?= esc($rl['email']) ?>
</div>
</div>
<div style="flex-shrink: 0;">
<span class="status-pill"
style="background: #e3f2fd; color: #1976d2; padding: 4px 10px; border-radius: 12px; font-size: 10px; font-weight: 600; white-space: nowrap;">
<?= esc($rl['status']) ?>
</span>
</div>
</div>
<?php endforeach; ?>
<?php else : ?>
<div class="text-center text-muted py-3">
No Leads Found for this Financial Year.
</div>
<?php endif; ?>
</div>
</div>
</div>
<div class="modal" id="completeModal">
<div class="modal-content" style="max-width: 675px;">
<div class="modal-header">
<h3>Complete Activity</h3>
<button class="btn-primary" style="background:none; color:#666; font-size:24px;" title="Close" onclick="closeModal('completeModal')">&times;</button>
</div>
<form class="parsley-examples" id="completeForm" enctype="multipart/form-data">
<hr class="my-0">
<div class="modal-body p-4">
<input type="hidden" id="comp_id">
<input type="hidden" id="lead_id">
<div class="form-group">
<label class="form-label">Outcome / Notes <span class="text-danger">*</span></label>
<textarea id="comp_notes" class="search-input" style="width:100%; height:80px;" required placeholder="What happened? Any next steps?" rows="3" required></textarea>
</div>
</div>
<div class="modal-footer">
<div class="form-group text-right mb-0">
<button type="button" class="btn-primary" style="background:#eee; color:#333; margin-right:10px;" onclick="closeModal('completeModal')">Cancel</button>
<button type="submit" class="btn-primary">Submit Outcome</button>
</div>
</div>
</form>
</div>
</div>
<script>
const API = '<?= base_url('sales') ?>';
function openModal(id) { document.getElementById(id).classList.add('active'); }
function openComp(id, lead_id) {
document.getElementById('completeForm').reset();
document.getElementById('comp_id').value = id;
document.getElementById('lead_id').value = lead_id;
openModal('completeModal');
}
document.getElementById('completeForm').onsubmit = async (e) => {
e.preventDefault();
// 1. Grab button variables first, DO NOT disable yet
const submitBtn = e.target.querySelector('button[type="submit"]');
const originalBtnText = submitBtn.innerText;
// 2. Capture values into variables
const activityId = document.getElementById('comp_id').value;
const leadId = document.getElementById('lead_id').value;
// This creates a TRUE or FALSE boolean
let compNotes = document.getElementById('comp_notes').value.trim();
// --- VALIDATION CHECKS (Button is still normal here) ---
if (compNotes === '') {
toastr.warning('Completion notes is required');
return;
}
// --- ALL VALIDATION PASSED: Now disable button ---
submitBtn.disabled = true;
submitBtn.innerText = 'Submitting...';
try {
const payload = {
completion_notes: compNotes, // Use the already trimmed variable
create_followup: 'no',
};
// Mark current activity complete
const res = await fetch(`${API}/activities/${activityId}/complete`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload)
});
if (res.ok) {
toastr.success('Updated Successfully');
// Final Actions
closeModal('completeModal');
e.target.reset();
window.location.reload();
} else {
let err = await res.json();
if (res.status === 400) {
let errorMessages = "";
let seenMessages = [];
if (err.messages) {
Object.entries(err.messages).forEach(([field, message]) => {
if (!seenMessages.includes(message)) {
errorMessages += `• ${message}<br>`;
seenMessages.push(message);
}
});
toastr.error(errorMessages, 'Validation Error', { allowHtml: true });
} else {
toastr.warning(err.message || 'Validation failed', 'Warning');
}
} else {
toastr.error(err.message || 'Error completing activity');
}
}
} catch (err) {
toastr.error("A network error occurred.");
} finally {
// ALWAYS runs to reset button
submitBtn.disabled = false;
submitBtn.innerText = originalBtnText;
}
};
function closeModal(id) {
const modal = document.getElementById(id);
if (modal) {
modal.classList.remove('active'); // Hide the modal
const form = modal.querySelector('form'); // Find the form inside this specific modal
}
}
document.addEventListener('DOMContentLoaded', function () {
const params = new URLSearchParams(window.location.search);
const fy = params.get('fy');
if (fy) {
document.getElementById('financial_year').value = fy;
}
});
function onFinancialYearChange(select) {
const url = new URL(window.location.href);
url.searchParams.set('fy', select.value);
window.location.href = url.toString();
}
</script>