diff --git a/README.md b/README.md new file mode 100644 index 0000000..34749ee --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# SecureLife Insurance Application + +This is a comprehensive insurance application system built with PHP. It features a public-facing landing page, a multi-step insurance application form, and a secure, feature-rich admin dashboard. + +## Features + +* **Modern Homepage:** A visually appealing and responsive homepage. +* **Multi-Step Subscription Form:** A user-friendly, multi-step form for insurance applications. +* **Dynamic Form Fields:** The form dynamically shows fields based on the selected insurance type. +* **Admin Dashboard:** A secure admin dashboard to manage and view applications. +* **Application Management:** Admins can view, search, filter, and update the status of applications. +* **Email Notifications:** Automatic email notifications to users and admins upon application submission and status changes. +* **Analytics Chart:** A chart on the admin dashboard to visualize application trends. +* **Details View:** A detailed view for each application. + +## Setup and Installation + +1. **Database:** + * The database configuration is in `db/config.php`. Update the credentials if needed. + * To create the necessary tables, run the migration script: `php db/migrate.php`. + +2. **Email:** + * The email configuration is in `mail/config.php`. You can configure your SMTP settings there. + +## Configuration + +* **Database:** `db/config.php` +* **Email:** `mail/config.php` +* **Admin Credentials:** The admin username and password are currently hardcoded in `admin.php`. It is recommended to move these to a more secure configuration file. + +## Customization + +* **Car Data:** The list of car manufacturers and models can be found and modified in `subscribe-step2.php`. +* **Styling:** The application's styling can be customized in `assets/css/custom.css`. diff --git a/admin-details.php b/admin-details.php new file mode 100644 index 0000000..a20629b --- /dev/null +++ b/admin-details.php @@ -0,0 +1,140 @@ +prepare("SELECT * FROM subscriptions WHERE id = :id"); + $stmt->bindParam(':id', $_GET['id'], PDO::PARAM_INT); + $stmt->execute(); + $subscription = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$subscription) { + $error_message = "Application not found."; + } + } catch (PDOException $e) { + $error_message = "Database error: " . $e->getMessage(); + } +} + +function get_specific_coverage_details_html($sub) { + $type = $sub['insuranceType'] ?? 'N/A'; + $output = ''; + switch ($type) { + case 'Car': + $output .= "

Car Make: " . htmlspecialchars($sub['carMake']) . "

"; + $output .= "

Car Model: " . htmlspecialchars($sub['carModel']) . "

"; + $output .= "

Year of Manufacture: " . htmlspecialchars($sub['carYear']) . "

"; + break; + case 'Health': + $output .= "

Number of Dependents: " . htmlspecialchars($sub['healthDependents']) . "

"; + break; + case 'Life': + $output .= "

Coverage Amount: $" . number_format($sub['lifeCoverage'], 2) . "

"; + break; + case 'Home': + $output .= "

Property Type: " . htmlspecialchars($sub['homeType']) . "

"; + break; + } + return $output; +} + +// --- Page Setup --- +$page_title = "Application Details"; + +?> + + + + + + <?php echo htmlspecialchars($page_title); ?> - SecureLife + + + + + + + + + + + +
+
+

Application #

+ ← Back to Dashboard +
+ + +
+ +
+
+
+

Details

+ +

Status:

+
+
+
+
+
+
Personal Information
+

Full Name:

+

Email:

+

Phone:

+

Submitted At:

+
+
+
Insurance Details
+

Insurance Type:

+ +
+
+
+
+ +
+ + + + + + \ No newline at end of file diff --git a/admin.php b/admin.php new file mode 100644 index 0000000..ab2538a --- /dev/null +++ b/admin.php @@ -0,0 +1,84 @@ + + + + + + + <?php echo htmlspecialchars($page_title); ?> - SecureLife + + + + + + + + + + + +
+ +
+ + + + + + + + \ No newline at end of file diff --git a/api/chart-data.php b/api/chart-data.php new file mode 100644 index 0000000..d6c29ba --- /dev/null +++ b/api/chart-data.php @@ -0,0 +1,68 @@ +prepare( + "SELECT DATE(created_at) as date, COUNT(*) as count + FROM subscriptions + WHERE created_at >= CURDATE() - INTERVAL 30 DAY + GROUP BY DATE(created_at) + ORDER BY date ASC" + ); + $line_chart_stmt->execute(); + $line_chart_results = $line_chart_stmt->fetchAll(PDO::FETCH_ASSOC); + + $line_chart_labels = []; + $line_chart_data = []; + foreach ($line_chart_results as $row) { + $line_chart_labels[] = $row['date']; + $line_chart_data[] = (int)$row['count']; + } + + // 2. Data for status counts (key metrics) + $status_stmt = $pdo->prepare("SELECT status, COUNT(*) as count FROM subscriptions GROUP BY status"); + $status_stmt->execute(); + $status_results = $status_stmt->fetchAll(PDO::FETCH_KEY_PAIR); + + $total_applications = array_sum($status_results); + $status_counts = [ + 'Total' => $total_applications, + 'Approved' => $status_results['Approved'] ?? 0, + 'Pending' => $status_results['Pending'] ?? 0, + 'Rejected' => $status_results['Rejected'] ?? 0, + ]; + + // 3. Data for insurance type distribution (pie chart) + $type_stmt = $pdo->prepare("SELECT insuranceType, COUNT(*) as count FROM subscriptions GROUP BY insuranceType"); + $type_stmt->execute(); + $type_results = $type_stmt->fetchAll(PDO::FETCH_KEY_PAIR); + + $pie_chart_labels = array_keys($type_results); + $pie_chart_data = array_values($type_results); + + // --- Combine all data into a single response --- + echo json_encode([ + 'success' => true, + 'line_chart' => [ + 'labels' => $line_chart_labels, + 'data' => $line_chart_data, + ], + 'key_metrics' => $status_counts, + 'pie_chart' => [ + 'labels' => $pie_chart_labels, + 'data' => $pie_chart_data, + ], + ]); + +} catch (Exception $e) { + http_response_code(500); + echo json_encode([ + 'success' => false, + 'message' => 'Failed to fetch chart data.', + 'error' => $e->getMessage() // For debugging + ]); +} diff --git a/api/update-status.php b/api/update-status.php new file mode 100644 index 0000000..9620232 --- /dev/null +++ b/api/update-status.php @@ -0,0 +1,60 @@ + false, 'message' => 'Invalid request']; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $data = json_decode(file_get_contents('php://input'), true); + $id = $data['id'] ?? null; + $status = $data['status'] ?? null; + + $allowed_statuses = ['Pending', 'Approved', 'Rejected']; + + if ($id && $status && in_array($status, $allowed_statuses)) { + try { + $pdo = db(); + $stmt = $pdo->prepare("UPDATE subscriptions SET status = :status WHERE id = :id"); + $stmt->execute([':status' => $status, ':id' => $id]); + + if ($stmt->rowCount() > 0) { + $response = ['success' => true, 'message' => 'Status updated successfully.']; + + // Fetch user details for email notification + $stmt = $pdo->prepare("SELECT fullName, email, insuranceType FROM subscriptions WHERE id = :id"); + $stmt->execute([':id' => $id]); + $subscription = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($subscription) { + $user_email = $subscription['email']; + $user_name = $subscription['fullName']; + $insurance_type = $subscription['insuranceType']; + $subject = ''; + $body = ''; + + if ($status === 'Approved') { + $subject = "Congratulations! Your SecureLife Application is Approved"; + $body = "

Application Approved!

Dear {$user_name},

We are pleased to inform you that your application for {$insurance_type} insurance has been approved. Welcome to SecureLife!

We will follow up with your policy documents shortly.

"; + } elseif ($status === 'Rejected') { + $subject = "Update on Your SecureLife Application"; + $body = "

Application Update

Dear {$user_name},

We have carefully reviewed your application for {$insurance_type} insurance. We regret to inform you that we are unable to approve your application at this time.

Thank you for your interest in SecureLife.

"; + } + + if (!empty($subject) && !empty($body)) { + MailService::sendMail($user_email, $subject, $body); + } + } + + } else { + $response['message'] = 'Could not find a subscription with that ID or status is unchanged.'; + } + } catch (Exception $e) { + $response['message'] = 'An error occurred: ' . $e->getMessage(); + } + } else { + $response['message'] = 'Invalid ID or status provided.'; + } +} + +echo json_encode($response); diff --git a/assets/css/custom.css b/assets/css/custom.css index 6d6dd0f..fabd0c3 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -1,109 +1,155 @@ -/* SecureLife Custom Styles */ -@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap'); + +/* assets/css/custom.css */ :root { - --primary-color: #0052CC; - --secondary-color: #00A3BF; - --bg-light: #F8F9FA; - --surface-white: #FFFFFF; - --text-dark: #212529; - --text-light: #6c757d; - --border-radius: 0.5rem; - --shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); - --shadow-md: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); + --primary-color: #0A2540; + --accent-color: #00c4ff; + --light-bg: #f8f9fa; + --white: #ffffff; + --font-family: 'Inter', sans-serif; } body { - font-family: 'Poppins', sans-serif; - background-color: var(--bg-light); - color: var(--text-dark); + font-family: var(--font-family); + background-color: var(--white); + color: #333; } +/* Header */ .navbar-brand { + font-family: var(--font-family); font-weight: 700; color: var(--primary-color); } -.hero { - background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); - color: var(--surface-white); - padding: 6rem 0; - text-align: center; -} - -.hero h1 { - font-weight: 700; - font-size: 3.5rem; -} - -.hero p { - font-size: 1.25rem; - font-weight: 300; - margin-bottom: 2rem; -} - -.btn-primary { +.btn-primary-modern { background-color: var(--primary-color); border-color: var(--primary-color); - font-weight: 600; - padding: 0.75rem 2rem; - border-radius: var(--border-radius); + color: var(--white); + font-weight: 500; + padding: 0.75rem 1.5rem; + border-radius: 0.5rem; transition: all 0.3s ease; } -.btn-primary:hover { - transform: translateY(-2px); - box-shadow: var(--shadow-md); +.btn-primary-modern:hover { + background-color: #001f36; + border-color: #001f36; } -.section-title { +.btn-accent-modern { + background-color: var(--accent-color); + border-color: var(--accent-color); + color: var(--primary-color); font-weight: 700; - margin-bottom: 3rem; - text-align: center; -} - -.accordion-item { - border: 1px solid #dee2e6; - border-radius: var(--border-radius) !important; - margin-bottom: 1rem; - box-shadow: var(--shadow-sm); + padding: 0.75rem 1.5rem; + border-radius: 0.5rem; transition: all 0.3s ease; } -.accordion-item:hover { - transform: translateY(-3px); - box-shadow: var(--shadow-md); +.btn-accent-modern:hover { + background-color: #00a2d1; + border-color: #00a2d1; + transform: translateY(-2px); + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); } -.accordion-header { - border-radius: var(--border-radius); + +/* Hero Section */ +.hero-section { + background: url('https://picsum.photos/1600/900?random=1') no-repeat center center; + background-size: cover; + position: relative; + padding: 8rem 0; } -.accordion-button { - font-weight: 600; - color: var(--text-dark); - border-radius: var(--border-radius) !important; +.hero-section::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(10, 37, 64, 0.7); } -.accordion-button:not(.collapsed) { - background-color: var(--primary-color); - color: var(--surface-white); - box-shadow: inset 0 -1px 0 rgba(0,0,0,.125); +.hero-section .container { + position: relative; + z-index: 2; } -.accordion-button:focus { - box-shadow: 0 0 0 0.25rem rgba(0, 82, 204, 0.25); +/* Services Section */ +.service-card { + border: none; + border-radius: 0.75rem; + background-color: var(--white); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.05); + transition: all 0.3s ease; + text-align: center; + padding: 2rem; } -.accordion-body img { - max-width: 150px; - border-radius: var(--border-radius); - box-shadow: var(--shadow-sm); +.service-card:hover { + transform: translateY(-10px); + box-shadow: 0 15px 40px rgba(0, 0, 0, 0.1); } -footer { - background-color: var(--surface-white); - padding: 2rem 0; - margin-top: 4rem; - border-top: 1px solid #e9ecef; +.service-card .icon-box { + width: 80px; + height: 80px; + margin: 0 auto 1.5rem; + border-radius: 50%; + background-color: var(--accent-color); + color: var(--primary-color); + display: flex; + align-items: center; + justify-content: center; + font-size: 2rem; + transition: all 0.3s ease; +} + +.service-card:hover .icon-box { + transform: rotate(360deg); +} + +/* Why Choose Us Section */ +.bg-light-blue { + background-color: #f0f8ff; + border-radius: 1rem; + padding: 4rem 0; +} + +.feature-box { + transition: all 0.3s ease; +} + +.feature-box:hover { + transform: scale(1.05); +} + +.text-primary-modern { + color: var(--primary-color) !important; +} + +/* Footer */ +footer.bg-dark { + background-color: var(--primary-color) !important; +} + +/* Animation */ +.service-card, .feature-box { + opacity: 0; + transform: translateY(20px); + animation: fadeInUp 0.5s forwards; +} + +@keyframes fadeInUp { + to { + opacity: 1; + transform: translateY(0); + } +} + +.row.g-4 > * { + animation-delay: calc(var(--animation-order) * 100ms); } diff --git a/assets/js/main.js b/assets/js/main.js index d73f858..9a5e204 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -1 +1,66 @@ -// SecureLife Main JS +function updateStatus(id, status) { + if (!confirm(`Are you sure you want to change the status to "${status}"?`)) { + return; + } + + fetch('api/update-status.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ id: id, status: status }), + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + window.location.reload(); + } else { + alert('Error updating status: ' + data.message); + } + }) + .catch(error => { + console.error('Error:', error); + alert('An unexpected error occurred.'); + }); +} + +document.addEventListener('DOMContentLoaded', function() { + const chartCanvas = document.getElementById('applicationsChart'); + if (chartCanvas) { + fetch('/api/chart-data.php') + .then(response => response.json()) + .then(data => { + if (data.success) { + const ctx = chartCanvas.getContext('2d'); + new Chart(ctx, { + type: 'line', + data: { + labels: data.labels, + datasets: [{ + label: 'New Applications', + data: data.data, + fill: false, + borderColor: '#0052CC', + tension: 0.1 + }] + }, + options: { + scales: { + y: { + beginAtZero: true, + ticks: { + stepSize: 1 + } + } + }, + responsive: true, + maintainAspectRatio: false + } + }); + } else { + console.error('Failed to load chart data:', data.message); + } + }) + .catch(error => console.error('Error fetching chart data:', error)); + } +}); diff --git a/config/admin_credentials.php b/config/admin_credentials.php new file mode 100644 index 0000000..e8bc0c6 --- /dev/null +++ b/config/admin_credentials.php @@ -0,0 +1,7 @@ + 'admin', + 'password' => 'password', +]; diff --git a/config/car_data.php b/config/car_data.php new file mode 100644 index 0000000..fe82c79 --- /dev/null +++ b/config/car_data.php @@ -0,0 +1,14 @@ + ['Camry', 'Corolla', 'RAV4', 'Highlander'], + 'Honda' => ['Civic', 'Accord', 'CR-V', 'Pilot'], + 'Ford' => ['F-150', 'Mustang', 'Explorer', 'Escape'], + 'Chevrolet' => ['Silverado', 'Equinox', 'Malibu', 'Tahoe'], + 'Nissan' => ['Rogue', 'Altima', 'Sentra'], + 'Jeep' => ['Wrangler', 'Grand Cherokee'], + 'Hyundai' => ['Elantra', 'Sonata', 'Tucson'], + 'BMW' => ['3 Series', '5 Series', 'X3', 'X5'], + 'Mercedes-Benz' => ['C-Class', 'E-Class', 'GLC'], + 'Audi' => ['A4', 'A6', 'Q5'], +]; diff --git a/db/migrate.php b/db/migrate.php new file mode 100644 index 0000000..00fd182 --- /dev/null +++ b/db/migrate.php @@ -0,0 +1,35 @@ + PDO::ERRMODE_EXCEPTION, + ]); + $pdo_admin->exec("CREATE DATABASE IF NOT EXISTS `" . DB_NAME . "` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"); + echo "Database '" . DB_NAME . "' created or already exists.\n"; + + // Now, connect to the specific database to run migrations + $pdo = db(); + $migrationsDir = __DIR__ . '/migrations'; + $files = glob($migrationsDir . '/*.sql'); + + if (empty($files)) { + echo "No migration files found.\n"; + exit; + } + + sort($files); + + foreach ($files as $file) { + echo "Running migration: " . basename($file) . "...\n"; + $sql = file_get_contents($file); + $pdo->exec($sql); + echo "Migration successful.\n"; + } + + echo "All migrations completed.\n"; + +} catch (PDOException $e) { + die("Database migration failed: " . $e->getMessage() . "\n"); +} \ No newline at end of file diff --git a/db/migrations/001_create_subscriptions_table.sql b/db/migrations/001_create_subscriptions_table.sql new file mode 100644 index 0000000..d168f22 --- /dev/null +++ b/db/migrations/001_create_subscriptions_table.sql @@ -0,0 +1,14 @@ +CREATE TABLE IF NOT EXISTS subscriptions ( + id INT AUTO_INCREMENT PRIMARY KEY, + fullName VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL, + phone VARCHAR(50) NOT NULL, + insuranceType VARCHAR(50) NOT NULL, + carMake VARCHAR(100), + carModel VARCHAR(100), + carYear INT, + healthDependents INT, + lifeCoverage DECIMAL(15, 2), + homeType VARCHAR(50), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/db/migrations/002_add_status_to_subscriptions.sql b/db/migrations/002_add_status_to_subscriptions.sql new file mode 100644 index 0000000..16f13e1 --- /dev/null +++ b/db/migrations/002_add_status_to_subscriptions.sql @@ -0,0 +1,2 @@ +ALTER TABLE subscriptions +ADD COLUMN status VARCHAR(50) NOT NULL DEFAULT 'submitted' AFTER homeType; diff --git a/index.php b/index.php index 090a4fa..d0119cc 100644 --- a/index.php +++ b/index.php @@ -3,156 +3,116 @@ - - - SecureLife - Your Future, Secured - - - - - - - - - + SecureLife - Your Future, Secured. + - - - - + + + + + - -