diff --git a/add_customer.php b/add_customer.php
new file mode 100644
index 0000000..1bf9981
--- /dev/null
+++ b/add_customer.php
@@ -0,0 +1,84 @@
+prepare("INSERT INTO customers (name, contact_email, status) VALUES (?, ?, 'onboarding')");
+ $stmt->execute([$name, $email]);
+ $customer_id = db()->lastInsertId();
+
+ // Seed onboarding tasks
+ $tasks = [
+ 'Initial Kick-off Meeting',
+ 'Discovery & Asset Inventory',
+ 'Agent Deployment (RMM)',
+ 'Network Security Audit',
+ 'Backup & Disaster Recovery Setup',
+ 'Microsoft 365 Tenant Review',
+ 'Final Configuration Handover'
+ ];
+
+ $stmt_task = db()->prepare("INSERT INTO onboarding_tasks (customer_id, task_name) VALUES (?, ?)");
+ foreach ($tasks as $task) {
+ $stmt_task->execute([$customer_id, $task]);
+ }
+
+ $message = "Customer added successfully! View Onboarding Checklist";
+ } catch (PDOException $e) {
+ $error = "Error adding customer: " . $e->getMessage();
+ }
+ }
+}
+?>
+
+
+
+
Add New Customer
+
Enter details to start the onboarding process.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Adding a customer will automatically generate a standard 7-step onboarding checklist to track progress.
+
+
+
+
diff --git a/api/update_customer_status.php b/api/update_customer_status.php
new file mode 100644
index 0000000..f7472ba
--- /dev/null
+++ b/api/update_customer_status.php
@@ -0,0 +1,24 @@
+ false, 'message' => 'Invalid request method']);
+ exit;
+}
+
+$id = $_POST['id'] ?? null;
+$status = $_POST['status'] ?? 'onboarding';
+
+if (!$id) {
+ echo json_encode(['success' => false, 'message' => 'Missing ID']);
+ exit;
+}
+
+try {
+ $stmt = db()->prepare("UPDATE customers SET status = ? WHERE id = ?");
+ $result = $stmt->execute([$status, (int)$id]);
+ echo json_encode(['success' => true]);
+} catch (PDOException $e) {
+ echo json_encode(['success' => false, 'message' => $e->getMessage()]);
+}
diff --git a/api/update_task.php b/api/update_task.php
new file mode 100644
index 0000000..255ac07
--- /dev/null
+++ b/api/update_task.php
@@ -0,0 +1,24 @@
+ false, 'message' => 'Invalid request method']);
+ exit;
+}
+
+$id = $_POST['id'] ?? null;
+$completed = $_POST['completed'] ?? 0;
+
+if (!$id) {
+ echo json_encode(['success' => false, 'message' => 'Missing ID']);
+ exit;
+}
+
+try {
+ $stmt = db()->prepare("UPDATE onboarding_tasks SET is_completed = ?, updated_at = NOW() WHERE id = ?");
+ $result = $stmt->execute([(int)$completed, (int)$id]);
+ echo json_encode(['success' => true]);
+} catch (PDOException $e) {
+ echo json_encode(['success' => false, 'message' => $e->getMessage()]);
+}
diff --git a/assets/css/custom.css b/assets/css/custom.css
index 50e0502..84450de 100644
--- a/assets/css/custom.css
+++ b/assets/css/custom.css
@@ -1,302 +1,219 @@
+:root {
+ --primary: #2563eb;
+ --primary-hover: #1d4ed8;
+ --secondary: #64748b;
+ --bg-color: #f8fafc;
+ --surface: #ffffff;
+ --border: #e2e8f0;
+ --text-main: #0f172a;
+ --text-muted: #64748b;
+ --success: #10b981;
+ --warning: #f59e0b;
+ --danger: #ef4444;
+ --radius: 6px;
+}
+
body {
- background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
- background-size: 400% 400%;
- animation: gradient 15s ease infinite;
- color: #212529;
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
- font-size: 14px;
- margin: 0;
- min-height: 100vh;
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+ background-color: var(--bg-color);
+ color: var(--text-main);
+ line-height: 1.5;
+ margin: 0;
}
-.main-wrapper {
- display: flex;
- align-items: center;
- justify-content: center;
- min-height: 100vh;
- width: 100%;
- padding: 20px;
- box-sizing: border-box;
- position: relative;
- z-index: 1;
+.navbar {
+ background-color: var(--surface);
+ border-bottom: 1px solid var(--border);
+ padding: 1rem 2rem;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
}
-@keyframes gradient {
- 0% {
- background-position: 0% 50%;
- }
- 50% {
- background-position: 100% 50%;
- }
- 100% {
- background-position: 0% 50%;
- }
+.navbar-brand {
+ font-weight: 700;
+ font-size: 1.25rem;
+ color: var(--primary);
+ text-decoration: none;
}
-.chat-container {
- width: 100%;
- max-width: 600px;
- background: rgba(255, 255, 255, 0.85);
- border: 1px solid rgba(255, 255, 255, 0.3);
- border-radius: 20px;
- display: flex;
- flex-direction: column;
- height: 85vh;
- box-shadow: 0 20px 40px rgba(0,0,0,0.2);
- backdrop-filter: blur(15px);
- -webkit-backdrop-filter: blur(15px);
- overflow: hidden;
+.nav-links {
+ display: flex;
+ gap: 1.5rem;
}
-.chat-header {
- padding: 1.5rem;
- border-bottom: 1px solid rgba(0, 0, 0, 0.05);
- background: rgba(255, 255, 255, 0.5);
- font-weight: 700;
- font-size: 1.1rem;
- display: flex;
- justify-content: space-between;
- align-items: center;
+.nav-link {
+ color: var(--text-muted);
+ text-decoration: none;
+ font-size: 0.875rem;
+ font-weight: 500;
+ transition: color 0.2s;
}
-.chat-messages {
- flex: 1;
- overflow-y: auto;
- padding: 1.5rem;
- display: flex;
- flex-direction: column;
- gap: 1.25rem;
+.nav-link:hover, .nav-link.active {
+ color: var(--primary);
}
-/* Custom Scrollbar */
-::-webkit-scrollbar {
- width: 6px;
+.container {
+ max-width: 1200px;
+ margin: 2rem auto;
+ padding: 0 1rem;
}
-::-webkit-scrollbar-track {
- background: transparent;
+.card {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ padding: 1.5rem;
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
+ margin-bottom: 1.5rem;
}
-::-webkit-scrollbar-thumb {
- background: rgba(255, 255, 255, 0.3);
- border-radius: 10px;
+.card-title {
+ font-size: 1.125rem;
+ font-weight: 600;
+ margin-bottom: 1.25rem;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
}
-::-webkit-scrollbar-thumb:hover {
- background: rgba(255, 255, 255, 0.5);
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0.5rem 1rem;
+ font-size: 0.875rem;
+ font-weight: 500;
+ border-radius: var(--radius);
+ cursor: pointer;
+ transition: all 0.2s;
+ border: 1px solid transparent;
+ text-decoration: none;
}
-.message {
- max-width: 85%;
- padding: 0.85rem 1.1rem;
- border-radius: 16px;
- line-height: 1.5;
- font-size: 0.95rem;
- box-shadow: 0 4px 15px rgba(0,0,0,0.05);
- animation: fadeIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+.btn-primary {
+ background-color: var(--primary);
+ color: white;
}
-@keyframes fadeIn {
- from { opacity: 0; transform: translateY(20px) scale(0.95); }
- to { opacity: 1; transform: translateY(0) scale(1); }
+.btn-primary:hover {
+ background-color: var(--primary-hover);
}
-.message.visitor {
- align-self: flex-end;
- background: linear-gradient(135deg, #212529 0%, #343a40 100%);
- color: #fff;
- border-bottom-right-radius: 4px;
+.btn-outline {
+ border-color: var(--border);
+ background-color: transparent;
+ color: var(--text-muted);
}
-.message.bot {
- align-self: flex-start;
- background: #ffffff;
- color: #212529;
- border-bottom-left-radius: 4px;
-}
-
-.chat-input-area {
- padding: 1.25rem;
- background: rgba(255, 255, 255, 0.5);
- border-top: 1px solid rgba(0, 0, 0, 0.05);
-}
-
-.chat-input-area form {
- display: flex;
- gap: 0.75rem;
-}
-
-.chat-input-area input {
- flex: 1;
- border: 1px solid rgba(0, 0, 0, 0.1);
- border-radius: 12px;
- padding: 0.75rem 1rem;
- outline: none;
- background: rgba(255, 255, 255, 0.9);
- transition: all 0.3s ease;
-}
-
-.chat-input-area input:focus {
- border-color: #23a6d5;
- box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.2);
-}
-
-.chat-input-area button {
- background: #212529;
- color: #fff;
- border: none;
- padding: 0.75rem 1.5rem;
- border-radius: 12px;
- cursor: pointer;
- font-weight: 600;
- transition: all 0.3s ease;
-}
-
-.chat-input-area button:hover {
- background: #000;
- transform: translateY(-2px);
- box-shadow: 0 5px 15px rgba(0,0,0,0.2);
-}
-
-/* Background Animations */
-.bg-animations {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 0;
- overflow: hidden;
- pointer-events: none;
-}
-
-.blob {
- position: absolute;
- width: 500px;
- height: 500px;
- background: rgba(255, 255, 255, 0.2);
- border-radius: 50%;
- filter: blur(80px);
- animation: move 20s infinite alternate cubic-bezier(0.45, 0, 0.55, 1);
-}
-
-.blob-1 {
- top: -10%;
- left: -10%;
- background: rgba(238, 119, 82, 0.4);
-}
-
-.blob-2 {
- bottom: -10%;
- right: -10%;
- background: rgba(35, 166, 213, 0.4);
- animation-delay: -7s;
- width: 600px;
- height: 600px;
-}
-
-.blob-3 {
- top: 40%;
- left: 30%;
- background: rgba(231, 60, 126, 0.3);
- animation-delay: -14s;
- width: 450px;
- height: 450px;
-}
-
-@keyframes move {
- 0% { transform: translate(0, 0) rotate(0deg) scale(1); }
- 33% { transform: translate(150px, 100px) rotate(120deg) scale(1.1); }
- 66% { transform: translate(-50px, 200px) rotate(240deg) scale(0.9); }
- 100% { transform: translate(0, 0) rotate(360deg) scale(1); }
-}
-
-.admin-link {
- font-size: 14px;
- color: #fff;
- text-decoration: none;
- background: rgba(0, 0, 0, 0.2);
- padding: 0.5rem 1rem;
- border-radius: 8px;
- transition: all 0.3s ease;
-}
-
-.admin-link:hover {
- background: rgba(0, 0, 0, 0.4);
- text-decoration: none;
-}
-
-/* Admin Styles */
-.admin-container {
- max-width: 900px;
- margin: 3rem auto;
- padding: 2.5rem;
- background: rgba(255, 255, 255, 0.85);
- backdrop-filter: blur(20px);
- -webkit-backdrop-filter: blur(20px);
- border-radius: 24px;
- box-shadow: 0 20px 50px rgba(0,0,0,0.15);
- border: 1px solid rgba(255, 255, 255, 0.4);
- position: relative;
- z-index: 1;
-}
-
-.admin-container h1 {
- margin-top: 0;
- color: #212529;
- font-weight: 800;
+.btn-outline:hover {
+ background-color: #f1f5f9;
+ color: var(--text-main);
}
.table {
- width: 100%;
- border-collapse: separate;
- border-spacing: 0 8px;
- margin-top: 1.5rem;
+ width: 100%;
+ border-collapse: collapse;
+}
+
+.table th, .table td {
+ padding: 0.75rem 1rem;
+ text-align: left;
+ border-bottom: 1px solid var(--border);
}
.table th {
- background: transparent;
- border: none;
- padding: 1rem;
- color: #6c757d;
- font-weight: 600;
- text-transform: uppercase;
- font-size: 0.75rem;
- letter-spacing: 1px;
+ font-size: 0.75rem;
+ text-transform: uppercase;
+ color: var(--text-muted);
+ font-weight: 600;
}
-.table td {
- background: #fff;
- padding: 1rem;
- border: none;
+.badge {
+ display: inline-flex;
+ padding: 0.25rem 0.5rem;
+ font-size: 0.75rem;
+ font-weight: 600;
+ border-radius: 9999px;
+ text-transform: capitalize;
}
-.table tr td:first-child { border-radius: 12px 0 0 12px; }
-.table tr td:last-child { border-radius: 0 12px 12px 0; }
+.badge-onboarding { background: #dbeafe; color: #1e40af; }
+.badge-active { background: #dcfce7; color: #166534; }
+.badge-inactive { background: #f1f5f9; color: #475569; }
+
+.checklist-item {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ padding: 0.75rem 0;
+ border-bottom: 1px solid var(--border);
+}
+
+.checklist-item:last-child {
+ border-bottom: none;
+}
+
+.checklist-item input[type="checkbox"] {
+ width: 1.25rem;
+ height: 1.25rem;
+ cursor: pointer;
+}
+
+.checklist-item.completed span {
+ text-decoration: line-through;
+ color: var(--text-muted);
+}
.form-group {
- margin-bottom: 1.25rem;
+ margin-bottom: 1.25rem;
}
-.form-group label {
- display: block;
- margin-bottom: 0.5rem;
- font-weight: 600;
- font-size: 0.9rem;
+.form-label {
+ display: block;
+ font-size: 0.875rem;
+ font-weight: 500;
+ margin-bottom: 0.5rem;
}
.form-control {
- width: 100%;
- padding: 0.75rem 1rem;
- border: 1px solid rgba(0, 0, 0, 0.1);
- border-radius: 12px;
- background: #fff;
- transition: all 0.3s ease;
- box-sizing: border-box;
+ width: 100%;
+ padding: 0.5rem 0.75rem;
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ font-size: 0.875rem;
}
.form-control:focus {
- outline: none;
- border-color: #23a6d5;
- box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.1);
-}
\ No newline at end of file
+ outline: none;
+ border-color: var(--primary);
+ box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.1);
+}
+
+.stats-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 1.5rem;
+ margin-bottom: 2rem;
+}
+
+.stat-card {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ padding: 1.5rem;
+}
+
+.stat-label {
+ font-size: 0.75rem;
+ color: var(--text-muted);
+ font-weight: 600;
+ text-transform: uppercase;
+}
+
+.stat-value {
+ font-size: 1.5rem;
+ font-weight: 700;
+ margin-top: 0.25rem;
+}
diff --git a/assets/js/main.js b/assets/js/main.js
index d349598..ed7c2f9 100644
--- a/assets/js/main.js
+++ b/assets/js/main.js
@@ -1,39 +1,32 @@
-document.addEventListener('DOMContentLoaded', () => {
- const chatForm = document.getElementById('chat-form');
- const chatInput = document.getElementById('chat-input');
- const chatMessages = document.getElementById('chat-messages');
-
- const appendMessage = (text, sender) => {
- const msgDiv = document.createElement('div');
- msgDiv.classList.add('message', sender);
- msgDiv.textContent = text;
- chatMessages.appendChild(msgDiv);
- chatMessages.scrollTop = chatMessages.scrollHeight;
- };
-
- chatForm.addEventListener('submit', async (e) => {
- e.preventDefault();
- const message = chatInput.value.trim();
- if (!message) return;
-
- appendMessage(message, 'visitor');
- chatInput.value = '';
-
- try {
- const response = await fetch('api/chat.php', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ message })
- });
- const data = await response.json();
-
- // Artificial delay for realism
- setTimeout(() => {
- appendMessage(data.reply, 'bot');
- }, 500);
- } catch (error) {
- console.error('Error:', error);
- appendMessage("Sorry, something went wrong. Please try again.", 'bot');
+// MSP Connect - Global Interactivity
+document.addEventListener('DOMContentLoaded', function() {
+ // Navbar scroll effect
+ const navbar = document.querySelector('.navbar');
+ window.addEventListener('scroll', () => {
+ if (window.scrollY > 10) {
+ navbar.style.boxShadow = '0 4px 12px rgba(0,0,0,0.05)';
+ } else {
+ navbar.style.boxShadow = 'none';
}
});
-});
+
+ // Fade-in effect for cards
+ const cards = document.querySelectorAll('.card');
+ cards.forEach((card, index) => {
+ card.style.opacity = '0';
+ card.style.transform = 'translateY(10px)';
+ card.style.transition = 'all 0.4s ease-out';
+ setTimeout(() => {
+ card.style.opacity = '1';
+ card.style.transform = 'translateY(0)';
+ }, index * 100);
+ });
+
+ // Tooltip simulation/Simple alerts
+ const buttons = document.querySelectorAll('.btn-outline');
+ buttons.forEach(btn => {
+ if (btn.getAttribute('title')) {
+ // Natural browser title is fine for now
+ }
+ });
+});
\ No newline at end of file
diff --git a/customers.php b/customers.php
new file mode 100644
index 0000000..9d5a566
--- /dev/null
+++ b/customers.php
@@ -0,0 +1,83 @@
+query("SELECT * FROM customers ORDER BY name ASC");
+ $customers = $stmt->fetchAll();
+} catch (PDOException $e) {
+ // Handle error
+}
+?>
+
+
+
+
+
Managed Customers
+
List of all customers and their current status.
+
+
+ Add Customer
+
+
+
+
+
+
+
+ | Customer Name |
+ Email |
+ Status |
+ Onboarding Progress |
+ Actions |
+
+
+
+
+
+ |
+ No customers found. Add your first customer.
+ |
+
+
+
+
+ |
+ |
+ |
+
+ prepare("SELECT COUNT(*) as total, SUM(CASE WHEN is_completed = 1 THEN 1 ELSE 0 END) as completed FROM onboarding_tasks WHERE customer_id = ?");
+ $stmt->execute([$customer['id']]);
+ $row = $stmt->fetch();
+ if ($row && $row['total'] > 0) {
+ $progress = round(($row['completed'] / $row['total']) * 100);
+ }
+ } catch (PDOException $e) {}
+ ?>
+
+ %
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
diff --git a/db/migrations_001.sql b/db/migrations_001.sql
new file mode 100644
index 0000000..89f2a89
--- /dev/null
+++ b/db/migrations_001.sql
@@ -0,0 +1,15 @@
+CREATE TABLE IF NOT EXISTS customers (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(255) NOT NULL,
+ contact_email VARCHAR(255),
+ status ENUM('onboarding', 'active', 'inactive') DEFAULT 'onboarding',
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+CREATE TABLE IF NOT EXISTS onboarding_tasks (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ customer_id INT NOT NULL,
+ task_name VARCHAR(255) NOT NULL,
+ is_completed BOOLEAN DEFAULT FALSE,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
diff --git a/db/setup.php b/db/setup.php
new file mode 100644
index 0000000..f1c2b05
--- /dev/null
+++ b/db/setup.php
@@ -0,0 +1,10 @@
+exec($sql);
+ echo "Database setup successful.";
+} catch (PDOException $e) {
+ echo "Error setting up database: " . $e->getMessage();
+}
diff --git a/footer.php b/footer.php
new file mode 100644
index 0000000..75776cf
--- /dev/null
+++ b/footer.php
@@ -0,0 +1,8 @@
+
+
+