368 lines
19 KiB
PHP
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')">×</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>
|