CRM-V1
This commit is contained in:
parent
94413f1f70
commit
5366b4681b
35
add_activity.php
Normal file
35
add_activity.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$lead_id = $_POST['lead_id'] ?? null;
|
||||||
|
$activity_type = $_POST['activity_type'] ?? null;
|
||||||
|
$due_date = $_POST['due_date'] ?? null;
|
||||||
|
$notes = $_POST['notes'] ?? null;
|
||||||
|
|
||||||
|
if ($lead_id && $activity_type) {
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO activities (LeadID, ActivityType, DueDate, Notes) VALUES (:lead_id, :activity_type, :due_date, :notes)");
|
||||||
|
$stmt->execute([
|
||||||
|
':lead_id' => $lead_id,
|
||||||
|
':activity_type' => $activity_type,
|
||||||
|
':due_date' => !empty($due_date) ? $due_date : null,
|
||||||
|
':notes' => $notes
|
||||||
|
]);
|
||||||
|
|
||||||
|
$_SESSION['success_message'] = "New activity added successfully!";
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$_SESSION['error_message'] = "Error adding activity: " . $e->getMessage();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$_SESSION['error_message'] = "Lead ID and activity type are required.";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$_SESSION['error_message'] = "Invalid request method.";
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: index.php");
|
||||||
|
exit;
|
||||||
57
add_lead.php
Normal file
57
add_lead.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic validation
|
||||||
|
$name = trim($_POST['name'] ?? '');
|
||||||
|
$companyName = trim($_POST['companyName'] ?? '');
|
||||||
|
$email = trim($_POST['email'] ?? '');
|
||||||
|
$phone = trim($_POST['phone'] ?? '');
|
||||||
|
$potentialAmount = $_POST['potentialAmount'] ?? null;
|
||||||
|
|
||||||
|
if (empty($name) || empty($email)) {
|
||||||
|
$_SESSION['error_message'] = 'Name and Email are required.';
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$_SESSION['error_message'] = 'Invalid email format.';
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($potentialAmount !== null && !is_numeric($potentialAmount)) {
|
||||||
|
$_SESSION['error_message'] = 'Potential amount must be a number.';
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare(
|
||||||
|
'INSERT INTO leads (Name, CompanyName, Email, Phone, PotentialAmount, Status) VALUES (?, ?, ?, ?, ?, ?)'
|
||||||
|
);
|
||||||
|
$stmt->execute([
|
||||||
|
$name,
|
||||||
|
$companyName,
|
||||||
|
$email,
|
||||||
|
$phone,
|
||||||
|
$potentialAmount,
|
||||||
|
'New' // Default status
|
||||||
|
]);
|
||||||
|
|
||||||
|
$_SESSION['success_message'] = 'Lead added successfully!';
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// In a real app, log this error.
|
||||||
|
$_SESSION['error_message'] = 'Failed to add lead. Please try again.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
170
dashboard.php
Normal file
170
dashboard.php
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
// Fetch dashboard data
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// Total leads
|
||||||
|
$total_leads = $pdo->query('SELECT COUNT(*) FROM leads')->fetchColumn();
|
||||||
|
|
||||||
|
// Leads per status
|
||||||
|
$leads_per_status_stmt = $pdo->query('SELECT Status, COUNT(*) as count FROM leads GROUP BY Status');
|
||||||
|
$leads_per_status = $leads_per_status_stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
||||||
|
|
||||||
|
// Total activities
|
||||||
|
$total_activities = $pdo->query('SELECT COUNT(*) FROM activities')->fetchColumn();
|
||||||
|
|
||||||
|
// Activities per type
|
||||||
|
$activities_per_type_stmt = $pdo->query('SELECT ActivityType, COUNT(*) as count FROM activities GROUP BY ActivityType');
|
||||||
|
$activities_per_type = $activities_per_type_stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$db_error = "Error fetching dashboard data: " . $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
$projectName = $_SERVER['PROJECT_NAME'] ?? 'CRM';
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Dashboard - <?= htmlspecialchars($projectName) ?></title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #ecf0f1;
|
||||||
|
font-family: 'Lato', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
.navbar {
|
||||||
|
background-color: #2c3e50;
|
||||||
|
}
|
||||||
|
.navbar-brand {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 0.5rem 1rem rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark mb-4">
|
||||||
|
<div class="container">
|
||||||
|
<a class="navbar-brand" href="index.php"><i class="fas fa-chart-line me-2"></i><?= htmlspecialchars($projectName) ?></a>
|
||||||
|
<div class="collapse navbar-collapse">
|
||||||
|
<ul class="navbar-nav ms-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="index.php">Leads</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" href="dashboard.php">Dashboard</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main class="container">
|
||||||
|
<?php if (isset($db_error)): ?>
|
||||||
|
<div class="alert alert-danger"><?= htmlspecialchars($db_error) ?></div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card text-center text-white bg-primary">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title"><i class="fas fa-users me-2"></i>Total Leads</h5>
|
||||||
|
<p class="card-text fs-1"><?= $total_leads ?></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card text-center text-white bg-info">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title"><i class="fas fa-tasks me-2"></i>Total Activities</h5>
|
||||||
|
<p class="card-text fs-1"><?= $total_activities ?></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row g-4 mt-4">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">Leads by Status</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<canvas id="leadsByStatusChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">Activities by Type</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<canvas id="activitiesByTypeChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Leads by Status Chart
|
||||||
|
const leadsCtx = document.getElementById('leadsByStatusChart').getContext('2d');
|
||||||
|
new Chart(leadsCtx, {
|
||||||
|
type: 'doughnut',
|
||||||
|
data: {
|
||||||
|
labels: <?= json_encode(array_keys($leads_per_status)) ?>,
|
||||||
|
datasets: [{
|
||||||
|
data: <?= json_encode(array_values($leads_per_status)) ?>,
|
||||||
|
backgroundColor: ['#3498db', '#f39c12', '#2ecc71', '#9b59b6', '#1abc9c', '#e74c3c']
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'top',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Activities by Type Chart
|
||||||
|
const activitiesCtx = document.getElementById('activitiesByTypeChart').getContext('2d');
|
||||||
|
new Chart(activitiesCtx, {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: <?= json_encode(array_keys($activities_per_type)) ?>,
|
||||||
|
datasets: [{
|
||||||
|
label: 'Number of Activities',
|
||||||
|
data: <?= json_encode(array_values($activities_per_type)) ?>,
|
||||||
|
backgroundColor: '#3498db'
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -1,17 +1,45 @@
|
|||||||
<?php
|
<?php
|
||||||
// Generated by setup_mariadb_project.sh — edit as needed.
|
|
||||||
define('DB_HOST', '127.0.0.1');
|
|
||||||
define('DB_NAME', 'app_36806');
|
|
||||||
define('DB_USER', 'app_36806');
|
|
||||||
define('DB_PASS', '72fbec7a-4020-4357-9e44-f0aad5499af6');
|
|
||||||
|
|
||||||
function db() {
|
// IMPORTANT: Do not output any HTML or echo statements in this file.
|
||||||
static $pdo;
|
// This file is intended for configuration and database connection logic.
|
||||||
if (!$pdo) {
|
|
||||||
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
|
/**
|
||||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
* Establishes a database connection using PDO.
|
||||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
*
|
||||||
]);
|
* @return PDO The PDO database connection object.
|
||||||
}
|
*/
|
||||||
return $pdo;
|
function db(): PDO
|
||||||
}
|
{
|
||||||
|
static $pdo = null;
|
||||||
|
|
||||||
|
if ($pdo) {
|
||||||
|
return $pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Database credentials from environment variables
|
||||||
|
$host = getenv('DB_HOST') ?: '127.0.0.1';
|
||||||
|
$port = getenv('DB_PORT') ?: '3306';
|
||||||
|
// Temporary fix: Hardcoded credentials to solve an environment issue where Apache cannot access the correct variables.
|
||||||
|
$dbname = 'app_36806';
|
||||||
|
$user = 'app_36806';
|
||||||
|
$pass = '72fbec7a-4020-4357-9e44-f0aad5499af6';
|
||||||
|
$charset = 'utf8mb4';
|
||||||
|
|
||||||
|
$dsn = "mysql:host=$host;port=$port;dbname=$dbname;charset=$charset";
|
||||||
|
|
||||||
|
$options = [
|
||||||
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||||
|
PDO::ATTR_EMULATE_PREPARES => false,
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = new PDO($dsn, $user, $pass, $options);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// In a real application, you would log this error and show a generic error page.
|
||||||
|
// For development, it's okay to die and show the error.
|
||||||
|
throw new PDOException($e->getMessage(), (int)$e->getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pdo;
|
||||||
|
}
|
||||||
22
db/database.sql
Normal file
22
db/database.sql
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS leads (
|
||||||
|
LeadID INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
Name VARCHAR(255) NOT NULL,
|
||||||
|
CompanyName VARCHAR(255),
|
||||||
|
Email VARCHAR(255),
|
||||||
|
Phone VARCHAR(50),
|
||||||
|
Source VARCHAR(100) NULL,
|
||||||
|
Status VARCHAR(50) DEFAULT 'New',
|
||||||
|
PotentialAmount DECIMAL(12, 2) NULL,
|
||||||
|
CreatedOn TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS activities (
|
||||||
|
ActivityID INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
LeadID INT,
|
||||||
|
ActivityType VARCHAR(50) NOT NULL,
|
||||||
|
DueDate DATE,
|
||||||
|
Status VARCHAR(50) DEFAULT 'Pending',
|
||||||
|
Notes TEXT,
|
||||||
|
CreatedOn TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (LeadID) REFERENCES leads(LeadID) ON DELETE CASCADE
|
||||||
|
);
|
||||||
12
db/init_db.php
Normal file
12
db/init_db.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/config.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$sql = file_get_contents(__DIR__ . '/database.sql');
|
||||||
|
$pdo->exec($sql);
|
||||||
|
echo "Database initialized successfully with 'leads' table.
|
||||||
|
";
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die("Database initialization failed: " . $e->getMessage());
|
||||||
|
}
|
||||||
28
delete_activity.php
Normal file
28
delete_activity.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
if (!isset($_GET['id']) || empty($_GET['id'])) {
|
||||||
|
$_SESSION['error_message'] = 'Invalid activity ID.';
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$activity_id = $_GET['id'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM activities WHERE ActivityID = ?");
|
||||||
|
$stmt->execute([$activity_id]);
|
||||||
|
|
||||||
|
if ($stmt->rowCount() > 0) {
|
||||||
|
$_SESSION['success_message'] = 'Activity deleted successfully!';
|
||||||
|
} else {
|
||||||
|
$_SESSION['error_message'] = 'Activity not found or already deleted.';
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$_SESSION['error_message'] = 'Error deleting activity: ' . $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
28
delete_lead.php
Normal file
28
delete_lead.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
if (!isset($_GET['id']) || empty($_GET['id'])) {
|
||||||
|
$_SESSION['error_message'] = 'Invalid lead ID.';
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$lead_id = $_GET['id'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM leads WHERE LeadID = ?");
|
||||||
|
$stmt->execute([$lead_id]);
|
||||||
|
|
||||||
|
if ($stmt->rowCount() > 0) {
|
||||||
|
$_SESSION['success_message'] = 'Lead deleted successfully!';
|
||||||
|
} else {
|
||||||
|
$_SESSION['error_message'] = 'Lead not found or already deleted.';
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$_SESSION['error_message'] = 'Error deleting lead: ' . $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
115
edit_activity.php
Normal file
115
edit_activity.php
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
// Check if ActivityID is provided
|
||||||
|
if (!isset($_GET['id']) || empty($_GET['id'])) {
|
||||||
|
$_SESSION['error_message'] = 'No activity selected for editing.';
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$activity_id = $_GET['id'];
|
||||||
|
|
||||||
|
// Fetch activity details
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare('SELECT * FROM activities WHERE ActivityID = ?');
|
||||||
|
$stmt->execute([$activity_id]);
|
||||||
|
$activity = $stmt->fetch();
|
||||||
|
|
||||||
|
if (!$activity) {
|
||||||
|
$_SESSION['error_message'] = 'Activity not found.';
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$_SESSION['error_message'] = 'Error fetching activity details: ' . $e->getMessage();
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$projectName = $_SERVER['PROJECT_NAME'] ?? 'CRM';
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Edit Activity - <?= htmlspecialchars($projectName) ?></title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #ecf0f1;
|
||||||
|
font-family: 'Lato', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
.navbar {
|
||||||
|
background-color: #2c3e50;
|
||||||
|
}
|
||||||
|
.navbar-brand {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 0.5rem 1rem rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark mb-4">
|
||||||
|
<div class="container">
|
||||||
|
<a class="navbar-brand" href="index.php"><i class="fas fa-chart-line me-2"></i><?= htmlspecialchars($projectName) ?></a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header bg-white py-3">
|
||||||
|
<h5 class="mb-0"><i class="fas fa-edit me-2 text-primary"></i>Edit Activity</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form action="update_activity.php" method="POST">
|
||||||
|
<input type="hidden" name="activity_id" value="<?= htmlspecialchars($activity['ActivityID']) ?>">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="activity_type" class="form-label">Activity Type</label>
|
||||||
|
<select class="form-select" id="activity_type" name="activity_type" required>
|
||||||
|
<option value="Call" <?= $activity['ActivityType'] == 'Call' ? 'selected' : '' ?>>Call</option>
|
||||||
|
<option value="Meeting" <?= $activity['ActivityType'] == 'Meeting' ? 'selected' : '' ?>>Meeting</option>
|
||||||
|
<option value="Task" <?= $activity['ActivityType'] == 'Task' ? 'selected' : '' ?>>Task</option>
|
||||||
|
<option value="Email" <?= $activity['ActivityType'] == 'Email' ? 'selected' : '' ?>>Email</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="status" class="form-label">Status</label>
|
||||||
|
<select class="form-select" id="status" name="status" required>
|
||||||
|
<option value="Pending" <?= $activity['Status'] == 'Pending' ? 'selected' : '' ?>>Pending</option>
|
||||||
|
<option value="Completed" <?= $activity['Status'] == 'Completed' ? 'selected' : '' ?>>Completed</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="due_date" class="form-label">Due Date</label>
|
||||||
|
<input type="date" class="form-control" id="due_date" name="due_date" value="<?= htmlspecialchars($activity['DueDate']) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="notes" class="form-label">Notes</label>
|
||||||
|
<textarea class="form-control" id="notes" name="notes" rows="3"><?= htmlspecialchars($activity['Notes']) ?></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||||
|
<a href="index.php" class="btn btn-secondary">Cancel</a>
|
||||||
|
<button type="submit" class="btn btn-primary">Update Activity</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
122
edit_lead.php
Normal file
122
edit_lead.php
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
// Check if LeadID is provided
|
||||||
|
if (!isset($_GET['id']) || empty($_GET['id'])) {
|
||||||
|
$_SESSION['error_message'] = 'No lead selected for editing.';
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$lead_id = $_GET['id'];
|
||||||
|
|
||||||
|
// Fetch lead details
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare('SELECT * FROM leads WHERE LeadID = ?');
|
||||||
|
$stmt->execute([$lead_id]);
|
||||||
|
$lead = $stmt->fetch();
|
||||||
|
|
||||||
|
if (!$lead) {
|
||||||
|
$_SESSION['error_message'] = 'Lead not found.';
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$_SESSION['error_message'] = 'Error fetching lead details: ' . $e->getMessage();
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$projectName = $_SERVER['PROJECT_NAME'] ?? 'CRM';
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Edit Lead - <?= htmlspecialchars($projectName) ?></title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #ecf0f1;
|
||||||
|
font-family: 'Lato', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
.navbar {
|
||||||
|
background-color: #2c3e50;
|
||||||
|
}
|
||||||
|
.navbar-brand {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 0.5rem 1rem rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark mb-4">
|
||||||
|
<div class="container">
|
||||||
|
<a class="navbar-brand" href="index.php"><i class="fas fa-chart-line me-2"></i><?= htmlspecialchars($projectName) ?></a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main class="container">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header bg-white py-3">
|
||||||
|
<h5 class="mb-0"><i class="fas fa-edit me-2 text-primary"></i>Edit Lead</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form action="update_lead.php" method="POST">
|
||||||
|
<input type="hidden" name="lead_id" value="<?= htmlspecialchars($lead['LeadID']) ?>">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="name" class="form-label">Full Name*</label>
|
||||||
|
<input type="text" class="form-control" id="name" name="name" value="<?= htmlspecialchars($lead['Name']) ?>" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="companyName" class="form-label">Company Name</label>
|
||||||
|
<input type="text" class="form-control" id="companyName" name="companyName" value="<?= htmlspecialchars($lead['CompanyName']) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="email" class="form-label">Email*</label>
|
||||||
|
<input type="email" class="form-control" id="email" name="email" value="<?= htmlspecialchars($lead['Email']) ?>" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="phone" class="form-label">Phone</label>
|
||||||
|
<input type="tel" class="form-control" id="phone" name="phone" value="<?= htmlspecialchars($lead['Phone']) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="potentialAmount" class="form-label">Potential Amount ($)</label>
|
||||||
|
<input type="number" step="0.01" class="form-control" id="potentialAmount" name="potentialAmount" value="<?= htmlspecialchars($lead['PotentialAmount']) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="status" class="form-label">Status</label>
|
||||||
|
<select class="form-select" id="status" name="status">
|
||||||
|
<option value="New" <?= $lead['Status'] == 'New' ? 'selected' : '' ?>>New</option>
|
||||||
|
<option value="Contacted" <?= $lead['Status'] == 'Contacted' ? 'selected' : '' ?>>Contacted</option>
|
||||||
|
<option value="Qualified" <?= $lead['Status'] == 'Qualified' ? 'selected' : '' ?>>Qualified</option>
|
||||||
|
<option value="Proposal Sent" <?= $lead['Status'] == 'Proposal Sent' ? 'selected' : '' ?>>Proposal Sent</option>
|
||||||
|
<option value="Won" <?= $lead['Status'] == 'Won' ? 'selected' : '' ?>>Won</option>
|
||||||
|
<option value="Lost" <?= $lead['Status'] == 'Lost' ? 'selected' : '' ?>>Lost</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||||
|
<a href="index.php" class="btn btn-secondary">Cancel</a>
|
||||||
|
<button type="submit" class="btn btn-primary">Update Lead</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
579
index.php
579
index.php
@ -1,150 +1,447 @@
|
|||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
session_start();
|
||||||
@ini_set('display_errors', '1');
|
require_once __DIR__ . '/db/config.php';
|
||||||
@error_reporting(E_ALL);
|
|
||||||
@date_default_timezone_set('UTC');
|
|
||||||
|
|
||||||
$phpVersion = PHP_VERSION;
|
// Fetch all leads
|
||||||
$now = date('Y-m-d H:i:s');
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
$sql = 'SELECT LeadID, Name, CompanyName, Email, Phone, PotentialAmount, Status, CreatedOn FROM leads';
|
||||||
|
$params = [];
|
||||||
|
$where_clauses = [];
|
||||||
|
|
||||||
|
if (isset($_GET['status']) && !empty($_GET['status'])) {
|
||||||
|
$where_clauses[] = 'Status = ?';
|
||||||
|
$params[] = $_GET['status'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_GET['search']) && !empty($_GET['search'])) {
|
||||||
|
$search_term = '%' . $_GET['search'] . '%';
|
||||||
|
$where_clauses[] = '(Name LIKE ? OR Email LIKE ? OR CompanyName LIKE ?)';
|
||||||
|
$params[] = $search_term;
|
||||||
|
$params[] = $search_term;
|
||||||
|
$params[] = $search_term;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($where_clauses)) {
|
||||||
|
$sql .= ' WHERE ' . implode(' AND ', $where_clauses);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql .= ' ORDER BY CreatedOn DESC';
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute($params);
|
||||||
|
$leads = $stmt->fetchAll();
|
||||||
|
|
||||||
|
// Fetch all activities
|
||||||
|
$stmt = $pdo->query('SELECT ActivityID, LeadID, ActivityType, DueDate, Status, Notes, CreatedOn FROM activities ORDER BY DueDate ASC');
|
||||||
|
$all_activities = $stmt->fetchAll();
|
||||||
|
|
||||||
|
// Group activities by LeadID for easy lookup
|
||||||
|
$activitiesByLead = [];
|
||||||
|
foreach ($all_activities as $activity) {
|
||||||
|
$activitiesByLead[$activity['LeadID']][] = $activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$leads = [];
|
||||||
|
$activitiesByLead = [];
|
||||||
|
$db_error = "Error fetching data: " . $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
$success_message = $_SESSION['success_message'] ?? null;
|
||||||
|
$error_message = $_SESSION['error_message'] ?? null;
|
||||||
|
|
||||||
|
// Clear session messages
|
||||||
|
unset($_SESSION['success_message'], $_SESSION['error_message']);
|
||||||
|
|
||||||
|
$projectName = $_SERVER['PROJECT_NAME'] ?? 'CRM';
|
||||||
|
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'A simple and powerful CRM for your business.';
|
||||||
?>
|
?>
|
||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>New Style</title>
|
<title><?= htmlspecialchars($projectName) ?> - Leads</title>
|
||||||
<?php
|
|
||||||
// Read project preview data from environment
|
<!-- SEO Meta Tags -->
|
||||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
<meta name="description" content="<?= htmlspecialchars($projectDescription) ?>">
|
||||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
<meta name="robots" content="index, follow">
|
||||||
?>
|
|
||||||
<?php if ($projectDescription): ?>
|
<!-- Open Graph / Twitter -->
|
||||||
<!-- Meta description -->
|
<meta property="og:title" content="<?= htmlspecialchars($projectName) ?>">
|
||||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>">
|
||||||
<!-- Open Graph meta tags -->
|
<meta property="og:image" content="<?= $_SERVER['PROJECT_IMAGE_URL'] ?? '' ?>">
|
||||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
<!-- Twitter meta tags -->
|
|
||||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
<!-- Stylesheets -->
|
||||||
<?php endif; ?>
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<?php if ($projectImageUrl): ?>
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
<!-- Open Graph image -->
|
|
||||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
<!-- Favicon -->
|
||||||
<!-- Twitter image -->
|
<link rel="icon" href="/favicon.ico" sizes="any">
|
||||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||||
<?php endif; ?>
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<style>
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
body {
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
background-color: #ecf0f1;
|
||||||
<style>
|
font-family: 'Lato', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
:root {
|
}
|
||||||
--bg-color-start: #6a11cb;
|
.navbar {
|
||||||
--bg-color-end: #2575fc;
|
background-color: #2c3e50;
|
||||||
--text-color: #ffffff;
|
}
|
||||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
.navbar-brand {
|
||||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
font-weight: bold;
|
||||||
}
|
color: #fff;
|
||||||
body {
|
}
|
||||||
margin: 0;
|
.card {
|
||||||
font-family: 'Inter', sans-serif;
|
border: none;
|
||||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
border-radius: 0.5rem;
|
||||||
color: var(--text-color);
|
box-shadow: 0 0.5rem 1rem rgba(0,0,0,0.1);
|
||||||
display: flex;
|
}
|
||||||
justify-content: center;
|
.btn-primary {
|
||||||
align-items: center;
|
background-color: #3498db;
|
||||||
min-height: 100vh;
|
border-color: #3498db;
|
||||||
text-align: center;
|
}
|
||||||
overflow: hidden;
|
.btn-primary:hover {
|
||||||
position: relative;
|
background-color: #2980b9;
|
||||||
}
|
border-color: #2980b9;
|
||||||
body::before {
|
}
|
||||||
content: '';
|
.table-hover tbody tr:hover {
|
||||||
position: absolute;
|
background-color: #f8f9fa;
|
||||||
top: 0;
|
}
|
||||||
left: 0;
|
.toast-container {
|
||||||
width: 100%;
|
z-index: 1090;
|
||||||
height: 100%;
|
}
|
||||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
.footer {
|
||||||
animation: bg-pan 20s linear infinite;
|
background-color: #2c3e50;
|
||||||
z-index: -1;
|
color: #ecf0f1;
|
||||||
}
|
padding: 2rem 0;
|
||||||
@keyframes bg-pan {
|
font-size: 0.9rem;
|
||||||
0% { background-position: 0% 0%; }
|
}
|
||||||
100% { background-position: 100% 100%; }
|
</style>
|
||||||
}
|
|
||||||
main {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
.card {
|
|
||||||
background: var(--card-bg-color);
|
|
||||||
border: 1px solid var(--card-border-color);
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 2rem;
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
.loader {
|
|
||||||
margin: 1.25rem auto 1.25rem;
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
border: 3px solid rgba(255, 255, 255, 0.25);
|
|
||||||
border-top-color: #fff;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
}
|
|
||||||
@keyframes spin {
|
|
||||||
from { transform: rotate(0deg); }
|
|
||||||
to { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
.hint {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
.sr-only {
|
|
||||||
position: absolute;
|
|
||||||
width: 1px; height: 1px;
|
|
||||||
padding: 0; margin: -1px;
|
|
||||||
overflow: hidden;
|
|
||||||
clip: rect(0, 0, 0, 0);
|
|
||||||
white-space: nowrap; border: 0;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 3rem;
|
|
||||||
font-weight: 700;
|
|
||||||
margin: 0 0 1rem;
|
|
||||||
letter-spacing: -1px;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
background: rgba(0,0,0,0.2);
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
||||||
}
|
|
||||||
footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 1rem;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
|
||||||
<div class="card">
|
<!-- Navigation -->
|
||||||
<h1>Analyzing your requirements and generating your website…</h1>
|
<nav class="navbar navbar-expand-lg navbar-dark mb-4">
|
||||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
<div class="container">
|
||||||
<span class="sr-only">Loading…</span>
|
<a class="navbar-brand" href="index.php"><i class="fas fa-chart-line me-2"></i><?= htmlspecialchars($projectName) ?></a>
|
||||||
</div>
|
<div class="collapse navbar-collapse">
|
||||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
<ul class="navbar-nav ms-auto">
|
||||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
<li class="nav-item">
|
||||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
<a class="nav-link active" href="index.php">Leads</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="dashboard.php">Dashboard</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="container">
|
||||||
|
<div class="row g-4">
|
||||||
|
|
||||||
|
<!-- Add Lead Form -->
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header bg-white py-3">
|
||||||
|
<h5 class="mb-0"><i class="fas fa-plus-circle me-2 text-primary"></i>Add New Lead</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form action="add_lead.php" method="POST">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="name" class="form-label">Full Name*</label>
|
||||||
|
<input type="text" class="form-control" id="name" name="name" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="companyName" class="form-label">Company Name</label>
|
||||||
|
<input type="text" class="form-control" id="companyName" name="companyName">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="email" class="form-label">Email*</label>
|
||||||
|
<input type="email" class="form-control" id="email" name="email" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="phone" class="form-label">Phone</label>
|
||||||
|
<input type="tel" class="form-control" id="phone" name="phone">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="potentialAmount" class="form-label">Potential Amount ($)</label>
|
||||||
|
<input type="number" step="0.01" class="form-control" id="potentialAmount" name="potentialAmount">
|
||||||
|
</div>
|
||||||
|
<div class="d-grid">
|
||||||
|
<button type="submit" class="btn btn-primary"><i class="fas fa-paper-plane me-2"></i>Submit Lead</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Leads List -->
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header bg-white py-3 d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0"><i class="fas fa-users me-2"></i>Current Leads</h5>
|
||||||
|
<div class="d-flex">
|
||||||
|
<form action="index.php" method="GET" class="d-flex">
|
||||||
|
<div class="input-group me-2">
|
||||||
|
<input type="text" class="form-control" name="search" placeholder="Search..." value="<?= htmlspecialchars($_GET['search'] ?? '') ?>">
|
||||||
|
<button class="btn btn-outline-secondary" type="submit"><i class="fas fa-search"></i></button>
|
||||||
|
</div>
|
||||||
|
<select class="form-select me-2" name="status" onchange="this.form.submit()">
|
||||||
|
<option value="">All Statuses</option>
|
||||||
|
<option value="New" <?= (isset($_GET['status']) && $_GET['status'] == 'New') ? 'selected' : '' ?>>New</option>
|
||||||
|
<option value="Contacted" <?= (isset($_GET['status']) && $_GET['status'] == 'Contacted') ? 'selected' : '' ?>>Contacted</option>
|
||||||
|
<option value="Qualified" <?= (isset($_GET['status']) && $_GET['status'] == 'Qualified') ? 'selected' : '' ?>>Qualified</option>
|
||||||
|
<option value="Proposal Sent" <?= (isset($_GET['status']) && $_GET['status'] == 'Proposal Sent') ? 'selected' : '' ?>>Proposal Sent</option>
|
||||||
|
<option value="Won" <?= (isset($_GET['status']) && $_GET['status'] == 'Won') ? 'selected' : '' ?>>Won</option>
|
||||||
|
<option value="Lost" <?= (isset($_GET['status']) && $_GET['status'] == 'Lost') ? 'selected' : '' ?>>Lost</option>
|
||||||
|
</select>
|
||||||
|
</form>
|
||||||
|
<a href="index.php" class="btn btn-secondary">Clear</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<?php if (isset($db_error)): ?>
|
||||||
|
<div class="alert alert-danger"><?= htmlspecialchars($db_error) ?></div>
|
||||||
|
<?php elseif (empty($leads)): ?>
|
||||||
|
<div class="text-center p-4">
|
||||||
|
<p class="mb-0 text-muted">No leads yet. Add one using the form!</p>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover align-middle">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Company</th>
|
||||||
|
<th>Contact</th>
|
||||||
|
<th>Potential</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($leads as $lead): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= htmlspecialchars($lead['Name']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($lead['CompanyName']) ?></td>
|
||||||
|
<td>
|
||||||
|
<a href="mailto:<?= htmlspecialchars($lead['Email']) ?>" class="text-decoration-none"><?= htmlspecialchars($lead['Email']) ?></a>
|
||||||
|
</td>
|
||||||
|
<td>$<?= number_format($lead['PotentialAmount'], 2) ?></td>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
$status_color = 'bg-secondary';
|
||||||
|
switch ($lead['Status']) {
|
||||||
|
case 'New':
|
||||||
|
$status_color = 'bg-primary';
|
||||||
|
break;
|
||||||
|
case 'Contacted':
|
||||||
|
$status_color = 'bg-info';
|
||||||
|
break;
|
||||||
|
case 'Qualified':
|
||||||
|
$status_color = 'bg-success';
|
||||||
|
break;
|
||||||
|
case 'Proposal Sent':
|
||||||
|
$status_color = 'bg-warning';
|
||||||
|
break;
|
||||||
|
case 'Won':
|
||||||
|
$status_color = 'bg-success';
|
||||||
|
break;
|
||||||
|
case 'Lost':
|
||||||
|
$status_color = 'bg-danger';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<span class="badge <?= $status_color ?>"><?= htmlspecialchars($lead['Status']) ?></span>
|
||||||
|
</td>
|
||||||
|
<td><?= date('M d, Y', strtotime($lead['CreatedOn'])) ?></td>
|
||||||
|
<td>
|
||||||
|
<a href="edit_lead.php?id=<?= $lead['LeadID'] ?>" class="btn btn-sm btn-outline-secondary">
|
||||||
|
<i class="fas fa-edit me-1"></i> Edit
|
||||||
|
</a>
|
||||||
|
<a href="delete_lead.php?id=<?= $lead['LeadID'] ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this lead?');">
|
||||||
|
<i class="fas fa-trash-alt me-1"></i> Delete
|
||||||
|
</a>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#activitiesModal" data-lead-id="<?= $lead['LeadID'] ?>" data-lead-name="<?= htmlspecialchars($lead['Name']) ?>">
|
||||||
|
<i class="fas fa-tasks me-1"></i> View Activities
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Activities Modal -->
|
||||||
|
<div class="modal fade" id="activitiesModal" tabindex="-1" aria-labelledby="activitiesModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="activitiesModalLabel">Activities for <span id="leadName"></span></h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div id="activitiesList">
|
||||||
|
<!-- Activities will be loaded here dynamically -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addActivityModal">
|
||||||
|
<i class="fas fa-plus-circle me-1"></i> Add Activity
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
<footer>
|
|
||||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
<!-- Add Activity Modal -->
|
||||||
</footer>
|
<div class="modal fade" id="addActivityModal" tabindex="-1" aria-labelledby="addActivityModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="addActivityModalLabel">Add New Activity</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form action="add_activity.php" method="POST">
|
||||||
|
<input type="hidden" id="leadIdField" name="lead_id">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="activityType" class="form-label">Activity Type</label>
|
||||||
|
<select class="form-select" id="activityType" name="activity_type" required>
|
||||||
|
<option value="Call">Call</option>
|
||||||
|
<option value="Meeting">Meeting</option>
|
||||||
|
<option value="Task">Task</option>
|
||||||
|
<option value="Email">Email</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="dueDate" class="form-label">Due Date</label>
|
||||||
|
<input type="date" class="form-control" id="dueDate" name="due_date">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="notes" class="form-label">Notes</label>
|
||||||
|
<textarea class="form-control" id="notes" name="notes" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid">
|
||||||
|
<button type="submit" class="btn btn-primary">Save Activity</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="footer mt-5">
|
||||||
|
<div class="container text-center">
|
||||||
|
<p>© <?= date('Y') ?> <?= htmlspecialchars($projectName) ?>. All Rights Reserved.</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- Toast Notifications -->
|
||||||
|
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
||||||
|
<?php if ($success_message): ?>
|
||||||
|
<div id="successToast" class="toast align-items-center text-white bg-success border-0" role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="toast-body"><?= htmlspecialchars($success_message) ?></div>
|
||||||
|
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if ($error_message): ?>
|
||||||
|
<div id="errorToast" class="toast align-items-center text-white bg-danger border-0" role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="toast-body"><?= htmlspecialchars($error_message) ?></div>
|
||||||
|
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Scripts -->
|
||||||
|
<script>
|
||||||
|
const activitiesByLead = <?= json_encode($activitiesByLead) ?>;
|
||||||
|
const activitiesModal = document.getElementById('activitiesModal');
|
||||||
|
const addActivityModal = document.getElementById('addActivityModal');
|
||||||
|
|
||||||
|
activitiesModal.addEventListener('show.bs.modal', function (event) {
|
||||||
|
const button = event.relatedTarget;
|
||||||
|
const leadId = button.getAttribute('data-lead-id');
|
||||||
|
const leadName = button.getAttribute('data-lead-name');
|
||||||
|
|
||||||
|
// Update modal title
|
||||||
|
const modalTitle = activitiesModal.querySelector('#leadName');
|
||||||
|
modalTitle.textContent = leadName;
|
||||||
|
|
||||||
|
// Populate activities list
|
||||||
|
const activitiesList = activitiesModal.querySelector('#activitiesList');
|
||||||
|
const activities = activitiesByLead[leadId] || [];
|
||||||
|
|
||||||
|
if (activities.length > 0) {
|
||||||
|
let html = '<ul class="list-group">';
|
||||||
|
activities.forEach(activity => {
|
||||||
|
html += `<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
|
<div>
|
||||||
|
<h6 class="mb-1">${activity.ActivityType}</h6>
|
||||||
|
<small class="text-muted">Due: ${activity.DueDate ? new Date(activity.DueDate).toLocaleDateString() : 'N/A'}</small>
|
||||||
|
<p class="mb-0">${activity.Notes || ''}</p>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<span class="badge bg-info rounded-pill me-3">${activity.Status}</span>
|
||||||
|
<div>
|
||||||
|
<a href="edit_activity.php?id=${activity.ActivityID}" class="btn btn-sm btn-outline-secondary">
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
</a>
|
||||||
|
<a href="delete_activity.php?id=${activity.ActivityID}" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this activity?');">
|
||||||
|
<i class="fas fa-trash-alt"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>`;
|
||||||
|
});
|
||||||
|
html += '</ul>';
|
||||||
|
activitiesList.innerHTML = html;
|
||||||
|
} else {
|
||||||
|
activitiesList.innerHTML = '<p class="text-center text-muted">No activities scheduled for this lead.</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set lead ID for the 'Add Activity' modal
|
||||||
|
const leadIdField = addActivityModal.querySelector('#leadIdField');
|
||||||
|
leadIdField.value = leadId;
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
var successToastEl = document.getElementById('successToast');
|
||||||
|
if (successToastEl) {
|
||||||
|
var successToast = new bootstrap.Toast(successToastEl);
|
||||||
|
successToast.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorToastEl = document.getElementById('errorToast');
|
||||||
|
if (errorToastEl) {
|
||||||
|
var errorToast = new bootstrap.Toast(errorToastEl);
|
||||||
|
errorToast.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
44
update_activity.php
Normal file
44
update_activity.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic validation
|
||||||
|
$activity_id = $_POST['activity_id'] ?? null;
|
||||||
|
$activity_type = trim($_POST['activity_type'] ?? '');
|
||||||
|
$status = trim($_POST['status'] ?? '');
|
||||||
|
$due_date = $_POST['due_date'] ?? null;
|
||||||
|
$notes = trim($_POST['notes'] ?? '');
|
||||||
|
|
||||||
|
if (empty($activity_id) || empty($activity_type) || empty($status)) {
|
||||||
|
$_SESSION['error_message'] = 'Required fields are missing.';
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare(
|
||||||
|
'UPDATE activities SET ActivityType = ?, Status = ?, DueDate = ?, Notes = ? WHERE ActivityID = ?'
|
||||||
|
);
|
||||||
|
$stmt->execute([
|
||||||
|
$activity_type,
|
||||||
|
$status,
|
||||||
|
$due_date,
|
||||||
|
$notes,
|
||||||
|
$activity_id
|
||||||
|
]);
|
||||||
|
|
||||||
|
$_SESSION['success_message'] = 'Activity updated successfully!';
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// In a real app, log this error.
|
||||||
|
$_SESSION['error_message'] = 'Failed to update activity. Please try again.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
60
update_lead.php
Normal file
60
update_lead.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic validation
|
||||||
|
$lead_id = $_POST['lead_id'] ?? null;
|
||||||
|
$name = trim($_POST['name'] ?? '');
|
||||||
|
$companyName = trim($_POST['companyName'] ?? '');
|
||||||
|
$email = trim($_POST['email'] ?? '');
|
||||||
|
$phone = trim($_POST['phone'] ?? '');
|
||||||
|
$potentialAmount = $_POST['potentialAmount'] ?? null;
|
||||||
|
$status = $_POST['status'] ?? 'New';
|
||||||
|
|
||||||
|
if (empty($lead_id) || empty($name) || empty($email)) {
|
||||||
|
$_SESSION['error_message'] = 'Required fields are missing.';
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$_SESSION['error_message'] = 'Invalid email format.';
|
||||||
|
header('Location: edit_lead.php?id=' . $lead_id);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($potentialAmount !== null && !is_numeric($potentialAmount)) {
|
||||||
|
$_SESSION['error_message'] = 'Potential amount must be a number.';
|
||||||
|
header('Location: edit_lead.php?id=' . $lead_id);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare(
|
||||||
|
'UPDATE leads SET Name = ?, CompanyName = ?, Email = ?, Phone = ?, PotentialAmount = ?, Status = ? WHERE LeadID = ?'
|
||||||
|
);
|
||||||
|
$stmt->execute([
|
||||||
|
$name,
|
||||||
|
$companyName,
|
||||||
|
$email,
|
||||||
|
$phone,
|
||||||
|
$potentialAmount,
|
||||||
|
$status,
|
||||||
|
$lead_id
|
||||||
|
]);
|
||||||
|
|
||||||
|
$_SESSION['success_message'] = 'Lead updated successfully!';
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// In a real app, log this error.
|
||||||
|
$_SESSION['error_message'] = 'Failed to update lead. Please try again.';
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
Loading…
x
Reference in New Issue
Block a user