diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 387c3dfd..d7353cb7 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -418,7 +418,7 @@ $routes->group("/util", ["filter" => "authMVC"], function ($routes) { $routes->get('getTheEmpDataForClaim/(:any)', 'ClientController::getTheEmpDataForClaim/$1'); // $routes->get('getTheEmpDataForClaimSearchByMobile/(:any)', 'ClientController::getTheEmpDataForClaimSearchByMobile/$1'); $routes->get('getTheEmpDataForClaimSearchByMobile', 'ClientController::getTheEmpDataForClaimSearchByMobile'); - $routes->get('getLeadNonEB/(:any)', 'LeadsController::getLeadNonEB/$1'); + $routes->get('getLeadNonEB/(:any)/(:any)', 'LeadsController::getLeadNonEB/$1/$2'); $routes->get('getPolicyTypeFields', 'LeadsController::getPolicyTypeFields'); $routes->get('removeMultiFile', 'LeadsController::removeMultiFile'); $routes->get('removeInstallments', 'LeadsController::removeInstallments'); @@ -912,6 +912,10 @@ $routes->group('sales', function($routes) { $routes->get('/', 'SalesController::index'); + $routes->get('activities', 'SalesController::activities'); + + $routes->get('page/(:segment)', 'SalesController::noPage/$1'); + // Get all leads with filters $routes->get('leads', 'SalesController::getLeads'); diff --git a/app/Controllers/LeadsController.php b/app/Controllers/LeadsController.php index b4eaac06..2c2d9bcf 100644 --- a/app/Controllers/LeadsController.php +++ b/app/Controllers/LeadsController.php @@ -363,8 +363,9 @@ class LeadsController extends BaseController public function createLead() { - // print_r($this->request->getPost()); die; $id = $this->request->getPost('id'); + $actual_lead_id = $this->request->getPost('actual_lead_id'); + $actual_lead_id = !empty($actual_lead_id) ? $actual_lead_id : null; $postData = $this->request->getPost(); $data = $this->prepareLeadData(); $rules = [ @@ -4349,8 +4350,10 @@ class LeadsController extends BaseController } // get lead data for edit both EB and NON-EB - public function getLeadNonEB($type, $id = null) + public function getLeadNonEB($type, $actual_lead_id = null ,$id = null) { + // Convert '0' to null so it doesn't break your existing DB checks + $actual_lead_id = ($actual_lead_id == 0 || $actual_lead_id == '0') ? null : $actual_lead_id; // Set basic data $data = [ 'issuer' => $this->issuer, @@ -4365,6 +4368,7 @@ class LeadsController extends BaseController 'gpaClaimType' => $this->claim_type_for_gpa, 'causeOfDeath' => $this->cause_of_death, 'selected_lead_type' => $type, + 'actual_lead_id' => $actual_lead_id, ]; // Fetch sales team members who are active in team 5 diff --git a/app/Controllers/SalesController.php b/app/Controllers/SalesController.php index 047be5b3..904217b1 100644 --- a/app/Controllers/SalesController.php +++ b/app/Controllers/SalesController.php @@ -27,15 +27,107 @@ class SalesController extends BaseController $this->noteModel = new SalesLeadNoteModel(); } + public function index() { - $db = \Config\Database::connect(); - // Fetch users for the assignment dropdowns - $data['users'] = $db->table('user_profiles') - ->select('id, first_name, last_name') - ->where('is_active', 1) - ->get()->getResultArray(); + $data = $this->getSalesStaffData(); + + $data['tab_name'] = 'Leads'; + $data['page_name'] = 'Leads'; + + return $this->loadLayout('sales/tracker_view', $data); + } - $this->loadLayout('sales/tracker_view', $data); + public function activities(){ + $data = $this->getSalesStaffData(); + + $data['tab_name'] = 'Activities'; + $data['page_name'] = 'Activities'; + + $data['leads'] = $this->leadModel->select('lead_id, company_name') + ->orderBy('lead_id', 'DESC') + ->findAll(); + + return $this->loadLayout('sales/activity_view', $data); + } + + /** + * HELPER: Fetches Sales Managers based on the logged-in user's role and branch + */ + private function getSalesStaffData(): array + { + $db = \Config\Database::connect(); + $logged_user_id = get_session_userid(); + + $data = [ + 'users' => [], + 'sales_manager_ids' => [] + ]; + + // Get the Branch ID, Role, Team, and Name of the logged-in user + $row = $db->table('user_profiles up') + ->select('up.id, up.first_name, up.last_name, up.nhance_branch_id, up.role, ut.team_id') + ->join('user_teams ut', 'ut.user_id = up.id', 'left') + ->where('up.is_active', 1) + ->where('up.id', $logged_user_id) + ->get() + ->getRow(); + + $nhance_branch_id = $row ? $row->nhance_branch_id : null; + $role = $row ? $row->role : null; + $team_id = $row ? $row->team_id : null; + + // Is the logged-in user a Sales Manager? (Role 4, Team 5) + if ($role == 4 && $team_id == 5) { + + $data['sales_manager_ids'] = [$logged_user_id]; + + $data['users'] = [ + [ + 'id' => $row->id, + 'sales_manager' => trim($row->first_name . ' ' . $row->last_name), + 'nhance_branch_id' => $nhance_branch_id + ] + ]; + + } + // Otherwise fetch ALL sales managers in this branch + elseif ($nhance_branch_id) { + + $data['users'] = $db->table('user_profiles up') + ->select('up.id, up.first_name, up.last_name, up.nhance_branch_id') + ->join('user_teams ut', 'ut.user_id = up.id') + ->where('up.is_active', 1) + ->where('ut.is_active', 1) + ->where('up.role', 4) + ->where('ut.team_id', 5) + ->where('up.nhance_branch_id', $nhance_branch_id) + ->get() + ->getResultArray(); + + $data['sales_manager_ids'] = array_column($data['users'], 'id'); + } + + return $data; + } + + public function noPage($type = null) + { + + $page_type = $type ?? 'Page'; + + $data['tab_name'] = 'Sales ' . $page_type; + $data['page_name'] = 'Sales ' . $page_type; + + echo view('layout/header', $data); + echo '
+
+
+

Oops! ' . esc($page_type) . ' is under construction.

+ Error Image +
+
+
'; + echo view('layout/footer', $data); } public function completeActivity($id) { diff --git a/app/Models/LeadsModel.php b/app/Models/LeadsModel.php index 1c028645..72ac2f59 100644 --- a/app/Models/LeadsModel.php +++ b/app/Models/LeadsModel.php @@ -10,6 +10,7 @@ class LeadsModel extends Model protected $primaryKey = 'id'; protected $allowedFields = [ 'id', + 'actual_lead_id', 'lead_type', 'issuer', 'client_type', diff --git a/app/Models/SalesActivityModel.php b/app/Models/SalesActivityModel.php index 80b8aef6..8d82097f 100644 --- a/app/Models/SalesActivityModel.php +++ b/app/Models/SalesActivityModel.php @@ -38,9 +38,9 @@ class SalesActivityModel extends Model // Validation protected $validationRules = [ 'lead_id' => 'required|integer', - 'activity_type' => 'required|in_list[Call,Email,Meeting,Demo,Share,Todo]', + 'activity_type' => 'required|in_list[Call,Email,Meeting,Demo,Share,Todo,Visit]', 'notes' => 'required', - 'scheduled_date' => 'required|valid_date', + 'scheduled_date' => 'required', 'assigned_to' => 'required|integer', 'status' => 'in_list[pending,completed]', ]; @@ -80,35 +80,63 @@ class SalesActivityModel extends Model */ public function getActivitiesWithFilters($filters = [], $limit = 10, $offset = 0) { - $builder = $this->select('sales_activities.*, actual_leads.company_name, user_profiles.first_name as assigned_to_name') - ->join('actual_leads', 'actual_leads.lead_id = sales_activities.lead_id', 'left') + $this->select('sales_activities.*, sales_actual_leads.company_name, user_profiles.first_name as assigned_to_name') + ->join('sales_actual_leads', 'sales_actual_leads.lead_id = sales_activities.lead_id', 'left') ->join('user_profiles', 'user_profiles.id = sales_activities.assigned_to', 'left'); if (!empty($filters['status'])) { - $builder->where('sales_activities.status', $filters['status']); + $this->where('sales_activities.status', $filters['status']); } if (!empty($filters['activity_type'])) { - $builder->where('sales_activities.activity_type', $filters['activity_type']); + $this->where('sales_activities.activity_type', $filters['activity_type']); } + // if (!empty($filters['assigned_to'])) { + // $this->where('sales_activities.assigned_to', $filters['assigned_to']); + // } if (!empty($filters['assigned_to'])) { - $builder->where('sales_activities.assigned_to', $filters['assigned_to']); + $assigned_to = $filters['assigned_to']; + + // If it's already an array, use it. If it's a string, explode it into an array. + $assignedToIds = is_array($assigned_to) ? $assigned_to : explode(',', $assigned_to); + + // Now it is guaranteed to be an array, making whereIn perfectly safe + $this->whereIn('sales_actual_leads.assigned_to', $assignedToIds); } + + if (!empty($filters['date_from'])) { - $builder->where('sales_activities.scheduled_date >=', $filters['date_from']); + $this->where('sales_activities.scheduled_date >=', $filters['date_from']); } if (!empty($filters['date_to'])) { - $builder->where('sales_activities.scheduled_date <=', $filters['date_to']); + $this->where('sales_activities.scheduled_date <=', $filters['date_to']); } - return [ - 'data' => $builder->orderBy('sales_activities.scheduled_date', 'DESC') - ->limit($limit, $offset)->findAll(), - 'total' => $builder->countAllResults(false) - ]; + if (!empty($filters['search'])) { + $this->groupStart() + ->like('sales_actual_leads.company_name', $filters['search']) + ->orLike('sales_actual_leads.email', $filters['search']) + ->orLike('sales_actual_leads.phone', $filters['search']) + ->orLike('user_profiles.first_name', $filters['search']) + ->groupEnd(); + } + + $this->orderBy('sales_activities.created_at', 'DESC'); + + $total = $this->countAllResults(false); + + $data = $this->findAll($limit, $offset); + + return ['data' => $data,'total' => $total]; + + // return [ + // 'data' => $builder->orderBy('sales_activities.scheduled_date', 'DESC') + // ->limit($limit, $offset)->findAll(), + // 'total' => $builder->countAllResults(false) + // ]; } /** @@ -142,8 +170,8 @@ class SalesActivityModel extends Model { $endDate = date('Y-m-d H:i:s', strtotime("+{$days} days")); - return $this->select('sales_activities.*, actual_leads.company_name') - ->join('actual_leads', 'actual_leads.lead_id = sales_activities.lead_id', 'left') + return $this->select('sales_activities.*, sales_actual_leads.company_name') + ->join('sales_actual_leads', 'sales_actual_leads.lead_id = sales_activities.lead_id', 'left') ->where('sales_activities.assigned_to', $userId) ->where('sales_activities.status', 'pending') ->where('sales_activities.scheduled_date <=', $endDate) diff --git a/app/Models/SalesActualLeadModel.php b/app/Models/SalesActualLeadModel.php index 25603740..22db6a2a 100644 --- a/app/Models/SalesActualLeadModel.php +++ b/app/Models/SalesActualLeadModel.php @@ -37,23 +37,37 @@ class SalesActualLeadModel extends Model // Validation protected $validationRules = [ - 'company_name' => 'required|min_length[2]|max_length[255]', - 'email' => 'required|valid_email|max_length[255]', - 'phone' => 'required|min_length[10]|max_length[20]', - 'status' => 'in_list[New,Potential,Prospects,Non prospects]', + 'company_name' => 'required|alpha_space|min_length[2]|max_length[255]', + 'email' => 'required_without[phone]|permit_empty|valid_email|max_length[255]', + 'phone' => 'required_without[email]|permit_empty|regex_match[/^[0-9+\s]+$/]|min_length[10]|max_length[20]', + 'status' => 'in_list[New,Potential,Prospects,Not a Prospects]', + 'assigned_to' => 'required', + 'gst_number' => 'permit_empty|exact_length[15]|regex_match[/^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][0-9A-Z]Z[0-9A-Z]$/]', ]; protected $validationMessages = [ 'company_name' => [ - 'required' => 'Company name is required', + 'required' => 'Company Name is Missing', + 'alpha_space' => 'Company Name must contain only letters and spaces', + 'min_length' => 'Company Name must be at least 2 characters long', + 'max_length' => 'Company Name must be at most 255 characters long' ], 'email' => [ - 'required' => 'Email is required', + 'required_without' => 'Either Email or Mobile Number is Needed.', 'valid_email' => 'Please provide a valid email address', + 'max_length' => 'Email must be at most 255 characters long', ], 'phone' => [ - 'required' => 'Phone number is required', + 'required_without' => 'Either Email or Mobile Number is Needed.', + 'regex_match' => 'Phone number can contain only digits, + and spaces.', + 'min_length' => 'Mobile Number must be at least 10 characters long', + 'max_length' => 'Mobile Number must be at most 20 characters long', ], + 'status' => [ 'in_list' => 'Please select a valid Status'], + 'assigned_to'=> [ 'required' => 'Please select a valid User' ], + 'gst_number' => [ 'exact_length' => 'GST Number must be exactly 15 characters long.', + 'regex_match' => 'Please enter a valid GST Number (e.g., 22AAAAA0000A1Z5).' + ], ]; protected $skipValidation = false; @@ -74,29 +88,42 @@ class SalesActualLeadModel extends Model */ public function getLeadsWithFilters($filters = [], $limit = 10, $offset = 0) { - $builder = $this->select('sales_actual_leads.*, user_profiles.first_name as assigned_to_name') + $this->select('sales_actual_leads.*, user_profiles.first_name as assigned_to_name, user_profiles.email as assigned_to_email') ->join('user_profiles', 'user_profiles.id = sales_actual_leads.assigned_to', 'left'); if (!empty($filters['status'])) { - $builder->where('sales_actual_leads.status', $filters['status']); + $this->where('sales_actual_leads.status', $filters['status']); } + // if (!empty($filters['assigned_to'])) { + // $this->where('sales_actual_leads.assigned_to', $filters['assigned_to']); + // } if (!empty($filters['assigned_to'])) { - $builder->where('sales_actual_leads.assigned_to', $filters['assigned_to']); + $assigned_to = $filters['assigned_to']; + + // If it's already an array, use it. If it's a string, explode it into an array. + $assignedToIds = is_array($assigned_to) ? $assigned_to : explode(',', $assigned_to); + + // Now it is guaranteed to be an array, making whereIn perfectly safe + $this->whereIn('sales_actual_leads.assigned_to', $assignedToIds); } + $this->orderBy('sales_actual_leads.created_at', 'DESC'); + if (!empty($filters['search'])) { - $builder->groupStart() + $this->groupStart() ->like('sales_actual_leads.company_name', $filters['search']) ->orLike('sales_actual_leads.email', $filters['search']) ->orLike('sales_actual_leads.phone', $filters['search']) + ->orLike('user_profiles.first_name', $filters['search']) ->groupEnd(); } - return [ - 'data' => $builder->limit($limit, $offset)->findAll(), - 'total' => $builder->countAllResults(false) - ]; + $total = $this->countAllResults(false); + + $data = $this->findAll($limit, $offset); + + return ['data' => $data,'total' => $total]; } /** @@ -113,9 +140,11 @@ class SalesActualLeadModel extends Model $contactModel = new SalesContactPersonModel(); $activityModel = new SalesActivityModel(); $noteModel = new SalesLeadNoteModel(); + $leadsModel = new LeadsModel(); $lead['contact_persons'] = $contactModel->where('lead_id', $leadId)->findAll(); $lead['activities'] = $activityModel->getActivitiesByLead($leadId); + $lead['opportunities'] = $leadsModel->where('actual_lead_id', $leadId)->findAll(); $lead['notes'] = $noteModel->getNotesByLead($leadId); return $lead; diff --git a/app/Views/layout/header.php b/app/Views/layout/header.php index 57aff2c2..61e63b23 100755 --- a/app/Views/layout/header.php +++ b/app/Views/layout/header.php @@ -2077,6 +2077,37 @@ + +
  • + + /assets/images/sales_tracker_dark_sb.png" alt="Logo" height="20"> + Sales Tracker + +
    + +
    +
  • +
  • diff --git a/app/Views/leads_form.php b/app/Views/leads_form.php index ef30ba59..87868d39 100644 --- a/app/Views/leads_form.php +++ b/app/Views/leads_form.php @@ -131,6 +131,8 @@ enctype="multipart/form-data"> + +
    @@ -741,6 +743,7 @@ $('#page_title').text(page_title); $('#leads_primarykey').val(res.data.id); + $('#actual_lead_id').val(res.data.actual_lead_id || 0); $('#policy_start_date').val(res.data.policy_end_date); $('#lead_type').val(res.data.lead_type); $('#issuer').val(res.data.issuer); diff --git a/app/Views/leads_form_handler.php b/app/Views/leads_form_handler.php index 6677520d..01960410 100644 --- a/app/Views/leads_form_handler.php +++ b/app/Views/leads_form_handler.php @@ -956,6 +956,7 @@ if (isset($selected_lead_type)) { $('#page_title').text('Edit Lead'); $('#leads_primarykey').val(data.id || ''); + $('#actual_lead_id').val(data.actual_lead_id || 0); $('#policy_start_date').val(data.policy_end_date || ''); $('#lead_type').val(data.lead_type || ''); $('#issuer').val(data.issuer || ''); @@ -1130,6 +1131,7 @@ if (isset($selected_lead_type)) { $('#page_title').text('Edit Lead'); $('#leads_primarykey').val(data.id || ''); + $('#actual_lead_id').val(data.actual_lead_id || 0); $('#policy_start_date').val(data.policy_end_date || ''); $('#lead_type').val(data.lead_type || ''); $('#issuer').val(data.issuer || ''); diff --git a/app/Views/leads_list.php b/app/Views/leads_list.php index 896a1117..98b9f232 100644 --- a/app/Views/leads_list.php +++ b/app/Views/leads_list.php @@ -533,8 +533,11 @@ table.dataTable tbody td { console.log('lead_id', lead_id); console.log('lead_form_type', lead_form_type); - - let url = '' + lead_form_type + '/' + lead_id + let actual_lead_id = 0; // Hardcoded to 0 since this is an edit operation + //Here After Edit URL: /type / actual_lead_id (0) / lead_id + let url = '' + lead_form_type + '/' + actual_lead_id + '/' + lead_id; + // old URL + // let url = '' + lead_form_type + '/' + lead_id console.log('url ', url) window.location.href = url; diff --git a/app/Views/leads_non_eb.php b/app/Views/leads_non_eb.php index 6456935a..6d2049f0 100644 --- a/app/Views/leads_non_eb.php +++ b/app/Views/leads_non_eb.php @@ -74,6 +74,7 @@ +
    diff --git a/app/Views/sales/activity_view.php b/app/Views/sales/activity_view.php new file mode 100644 index 00000000..4c5f939b --- /dev/null +++ b/app/Views/sales/activity_view.php @@ -0,0 +1,1744 @@ + + +
    +
    +
    + + +
    +
    All
    +
    Pending
    +
    Completed
    +
    + + +
    + + +
    + +
    + +
    + + + +
    + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/Views/sales/tracker_view.php b/app/Views/sales/tracker_view.php index b317712c..d2c3085d 100644 --- a/app/Views/sales/tracker_view.php +++ b/app/Views/sales/tracker_view.php @@ -2,19 +2,20 @@ /* 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; } + .main-content { flex: 1; display: flex; flex-direction: column; overflow: hidden; } + /* .top-bar { background: white; padding: 15px 30px; border-bottom: 1px solid #e0e0e0; display: flex; justify-content: space-between; align-items: center; } */ + .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; } + .filter-tabs { display: flex; gap: 10px; padding: 20px 30px; } .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 */ + .lead-header { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; /* responsive */ gap: 15px; } + .lead-actions { display: flex; align-items: center; gap: 10px; } .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); } @@ -22,12 +23,13 @@ .status-new { background: #e3f2fd; color: #1976d2; } .status-potential { background: #fff3e0; color: #f57c00; } .status-prospects { background: #e8f5e9; color: #388e3c; } + .status-not-a-prospects { background: #ffebee; color: #d32f2f; } /* 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-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; } @@ -39,6 +41,14 @@ .activity-type-btn:hover { border-color: #ff6b35; background: #fff5f2; } .activity-type-btn.active { border-color: #ff6b35; background: #ff6b35; color: white; } + .activity-types { display: grid; grid-template-columns: repeat(12, 1fr); /* 12-column grid */ gap: 10px; margin-bottom: 20px; } + + /* First 3 buttons → 4 columns each (3 × 4 = 12) */ + .activity-types button:nth-child(-n+3) { grid-column: span 4; } + + /* Next 4 buttons → 3 columns each (4 × 3 = 12) */ + .activity-types button:nth-child(n+4) { grid-column: span 3; } + /* Timeline Styling */ .timeline { position: relative; padding-left: 30px; margin-top: 20px; } .timeline-item { position: relative; padding-bottom: 25px; } @@ -47,71 +57,167 @@ .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;} + .timeline::before { display: none !important; content: none !important; } + .timeline.no-line::before { display: none; } + .timeline { border-left: none !important; } + .timeline-item:last-child { border-left: none !important;} + .timeline-item:last-child::before { display: none !important;} + + /* opportunities Styling */ + .opportunities-list { display: grid; gap: 15px;} + .opportunity-card { background: white; border-radius: 12px; padding: 20px; border: 1px solid #e0e0e0; transition: all 0.2s;} + .opportunity-header { display: flex; justify-content: space-between; align-items: start; margin-bottom: 15px;} + .opportunity-title { font-weight: 600; font-size: 16px; margin-bottom: 5px;} + .opportunity-amount { font-size: 20px; font-weight: 700; color: #4caf50;} + .opportunity-details { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-top: 15px;} + .opportunity-detail-item { font-size: 13px; color: #666;} + .opportunity-footer { margin-top: 15px; padding-top: 15px; border-top: 1px solid #f0f0f0; font-size: 13px; color: #666; } + + /* Base style for both tabs */ + .tab-item { cursor: pointer; padding-bottom: 10px; margin: 0; font-size: 16px; color: #999; /* Default grey for unselected */ border-bottom: 2px solid transparent; transition: all 0.2s ease; } + + /* Style for the SELECTED tab */ + .tab-item.active { color: #333; /* Darker text */ border-bottom: 2px solid #ff6b35; /* Orange underline */ } + + .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; + } + + /* The animation definition */ + @keyframes validation-blink { + 0% { transform: translateX(0); box-shadow: 0 0 0px red; } + 25% { transform: translateX(-5px); box-shadow: 0 0 10px red; } + 50% { transform: translateX(5px); box-shadow: 0 0 10px red; } + 75% { transform: translateX(-5px); box-shadow: 0 0 10px red; } + 100% { transform: translateX(0); box-shadow: 0 0 0px red; } + } + + /* The class we will add via JavaScript */ + .blink-error { + animation: validation-blink 0.3s ease-in-out; + border-color: #ff4d4d !important; + } +
    -
    + +
    +
    + + +
    +
    All
    +
    New
    +
    Potential
    +
    Prospects
    +
    Not a Prospects
    +
    + + +
    + + +
    +
    + + +