From d49002c4b1e6b1391ee4c9a488b76af3d36e9026 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sun, 1 Mar 2026 22:44:18 +0000 Subject: [PATCH] Initial Setup with MSP & Customers --- add_customer.php | 84 +++++++ api/update_customer_status.php | 24 ++ api/update_task.php | 24 ++ assets/css/custom.css | 427 +++++++++++++-------------------- assets/js/main.js | 67 +++--- customers.php | 83 +++++++ db/migrations_001.sql | 15 ++ db/setup.php | 10 + footer.php | 8 + header.php | 54 +++++ index.php | 280 +++++++++++---------- onboarding.php | 163 +++++++++++++ 12 files changed, 801 insertions(+), 438 deletions(-) create mode 100644 add_customer.php create mode 100644 api/update_customer_status.php create mode 100644 api/update_task.php create mode 100644 customers.php create mode 100644 db/migrations_001.sql create mode 100644 db/setup.php create mode 100644 footer.php create mode 100644 header.php create mode 100644 onboarding.php 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.

+
+ + +
+ +
+ + + +
+ +
+ + +
+
+
+ + +
+
+ + +
+
+ + Cancel +
+
+
+ +
+ + 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 NameEmailStatusOnboarding ProgressActions
+ 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 @@ + + + + diff --git a/header.php b/header.php new file mode 100644 index 0000000..ee876a0 --- /dev/null +++ b/header.php @@ -0,0 +1,54 @@ + + + + + + + MSP Connect | Customer Success Platform + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/index.php b/index.php index 7205f3d..a0d7d17 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,138 @@ 0, + 'active_onboarding' => 0, + 'qbrs_scheduled' => 4, + 'open_tickets' => 12 +]; + +try { + $stmt = db()->query("SELECT COUNT(*) FROM customers"); + $stats['total_customers'] = $stmt->fetchColumn(); + + $stmt = db()->query("SELECT COUNT(*) FROM customers WHERE status = 'onboarding'"); + $stats['active_onboarding'] = $stmt->fetchColumn(); +} catch (PDOException $e) { + // Handle error gracefully +} + +// Fetch recent customers +$recent_customers = []; +try { + $stmt = db()->query("SELECT * FROM customers ORDER BY created_at DESC LIMIT 5"); + $recent_customers = $stmt->fetchAll(); +} catch (PDOException $e) { + // Handle error +} ?> - - - - - - New Style - - - - - - - - - - - - - - - - - - - - - -
-
-

Analyzing your requirements and generating your website…

-
- Loading… -
-

AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

+ +
+
+

MSP Operations Dashboard

+

Welcome back, Admin. Here's what's happening today.

-
- - - + +
+
+
Total Customers
+
+
+
+
Active Onboarding
+
+
+
+
QBRs This Quarter
+
+
+
+
Open Support Tickets
+
+
+
+ +
+
+ Recent Customers + View All +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Customer NameEmail AddressStatusAdded OnAction
+ + No customers added yet. Add your first customer. +
+ + + Onboarding + + + Details + +
+
+ +
+
+
Quick Actions
+
+ + Onboard New Customer + + + +
+
+
+
Integration Status
+
+
+ ConnectWise PSA + Connected +
+
+ Microsoft 365 + Connected +
+
+ Datto RMM + Configure +
+
+
+
+ + + \ No newline at end of file diff --git a/onboarding.php b/onboarding.php new file mode 100644 index 0000000..177e2fd --- /dev/null +++ b/onboarding.php @@ -0,0 +1,163 @@ +

Customer Not Found

Back to Customer List"; + require_once __DIR__ . '/footer.php'; + exit; +} + +// Fetch customer +$customer = null; +try { + $stmt = db()->prepare("SELECT * FROM customers WHERE id = ?"); + $stmt->execute([$customer_id]); + $customer = $stmt->fetch(); +} catch (PDOException $e) {} + +if (!$customer) { + echo "

Customer Not Found

Back to Customer List
"; + require_once __DIR__ . '/footer.php'; + exit; +} + +// Fetch tasks +$tasks = []; +try { + $stmt = db()->prepare("SELECT * FROM onboarding_tasks WHERE customer_id = ? ORDER BY id ASC"); + $stmt->execute([$customer_id]); + $tasks = $stmt->fetchAll(); +} catch (PDOException $e) {} + +// Calculate progress +$total_tasks = count($tasks); +$completed_tasks = count(array_filter($tasks, function($t) { return $t['is_completed']; })); +$progress = $total_tasks > 0 ? round(($completed_tasks / $total_tasks) * 100) : 0; +?> + +
+
+
+

Onboarding Checklist:

+

Manage and track setup tasks for this customer.

+
+ + Back to List + +
+ +
+
+
+
Overall Onboarding Progress
+
%
+
+
+
+
+
+
+ +
+
Tasks & Milestones
+
+ +
+ data-task-id=""> + + + + +
+ +
+
+ +
+ + + + +
+
+ + + +