Compare commits

...

1 Commits

Author SHA1 Message Date
Flatlogic Bot
5366b4681b CRM-V1 2025-12-11 07:55:09 +00:00
13 changed files with 1174 additions and 156 deletions

35
add_activity.php Normal file
View 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
View 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
View 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>

View File

@ -1,17 +1,45 @@
<?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() {
static $pdo;
if (!$pdo) {
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
}
// IMPORTANT: Do not output any HTML or echo statements in this file.
// This file is intended for configuration and database connection logic.
/**
* Establishes a database connection using PDO.
*
* @return PDO The PDO database connection object.
*/
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
View 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
View 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
View 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
View 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
View 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
View 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>

545
index.php
View File

@ -1,150 +1,447 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
session_start();
require_once __DIR__ . '/db/config.php';
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
// Fetch all leads
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">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Style</title>
<?php
// Read project preview data from environment
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<?php if ($projectDescription): ?>
<!-- Meta description -->
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= htmlspecialchars($projectName) ?> - Leads</title>
<!-- SEO Meta Tags -->
<meta name="description" content="<?= htmlspecialchars($projectDescription) ?>">
<meta name="robots" content="index, follow">
<!-- Open Graph / Twitter -->
<meta property="og:title" content="<?= htmlspecialchars($projectName) ?>">
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>">
<meta property="og:image" content="<?= $_SERVER['PROJECT_IMAGE_URL'] ?? '' ?>">
<meta name="twitter:card" content="summary_large_image">
<!-- Stylesheets -->
<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">
<!-- Favicon -->
<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
background-color: #ecf0f1;
font-family: 'Lato', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
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>');
animation: bg-pan 20s linear infinite;
z-index: -1;
.navbar {
background-color: #2c3e50;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
.navbar-brand {
font-weight: bold;
color: #fff;
}
.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);
border: none;
border-radius: 0.5rem;
box-shadow: 0 0.5rem 1rem 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;
.btn-primary {
background-color: #3498db;
border-color: #3498db;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
.btn-primary:hover {
background-color: #2980b9;
border-color: #2980b9;
}
.hint {
opacity: 0.9;
.table-hover tbody tr:hover {
background-color: #f8f9fa;
}
.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;
.toast-container {
z-index: 1090;
}
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;
.footer {
background-color: #2c3e50;
color: #ecf0f1;
padding: 2rem 0;
font-size: 0.9rem;
}
</style>
</head>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
<!-- Navigation -->
<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 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>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will update automatically as the plan is implemented.</p>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
</div>
</main>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
<!-- 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>
<!-- Add Activity Modal -->
<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>&copy; <?= 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>
</html>

44
update_activity.php Normal file
View 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
View 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();