diff --git a/assets/css/custom.css b/assets/css/custom.css
new file mode 100644
index 0000000..fe38e7a
--- /dev/null
+++ b/assets/css/custom.css
@@ -0,0 +1,203 @@
+/* General Body Styles */
+body {
+ font-family: 'Roboto', sans-serif;
+ color: #4F4F4F;
+ background-color: #FFFFFF;
+ margin: 0;
+ line-height: 1.6;
+}
+
+.container {
+ width: 90%;
+ max-width: 1100px;
+ margin: 0 auto;
+}
+
+/* Typography */
+h1, h2, h3 {
+ font-family: 'Poppins', sans-serif;
+ font-weight: 600;
+ color: #333;
+}
+
+h1 { font-size: 3rem; margin-bottom: 1rem; }
+h2 { font-size: 2.2rem; margin-bottom: 1rem; text-align: center;}
+
+/* Header and Navbar */
+.navbar {
+ background: #FFFFFF;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+ padding: 1rem 0;
+ position: sticky;
+ top: 0;
+ z-index: 1000;
+}
+
+.navbar .container {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.logo {
+ font-family: 'Poppins', sans-serif;
+ font-size: 1.5rem;
+ font-weight: 700;
+ color: #2F80ED;
+ text-decoration: none;
+}
+
+.nav-links {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ align-items: center;
+}
+
+.nav-links li {
+ margin-left: 20px;
+}
+
+.nav-links a {
+ text-decoration: none;
+ color: #4F4F4F;
+ font-weight: 500;
+ transition: color 0.3s ease;
+}
+
+.nav-links a:hover {
+ color: #2F80ED;
+}
+
+/* Buttons */
+.btn {
+ padding: 10px 20px;
+ border-radius: 8px;
+ text-decoration: none;
+ font-weight: 500;
+ transition: all 0.3s ease;
+ display: inline-block;
+ border: none;
+ cursor: pointer;
+}
+
+.btn-primary {
+ background-color: #2F80ED;
+ color: #FFFFFF;
+}
+
+.btn-primary:hover {
+ background-color: #2D67B2;
+}
+
+.btn-secondary {
+ background-color: #F2F2F2;
+ color: #333;
+}
+
+.btn-secondary:hover {
+ background-color: #E0E0E0;
+}
+
+.btn-lg {
+ padding: 15px 30px;
+ font-size: 1.1rem;
+}
+
+/* Hero Section */
+.hero-section {
+ background: linear-gradient(to right, #2F80ED, #56CCF2);
+ color: #FFFFFF;
+ text-align: center;
+ padding: 100px 0;
+}
+
+.hero-section h1 {
+ color: #FFFFFF;
+ font-weight: 700;
+}
+
+.hero-section p {
+ font-size: 1.2rem;
+ margin-bottom: 2rem;
+}
+
+/* Content Sections */
+.content-section {
+ padding: 60px 0;
+}
+
+.bg-light {
+ background-color: #F2F2F2;
+}
+
+/* Service Boxes */
+.service-boxes {
+ display: flex;
+ justify-content: space-around;
+ gap: 20px;
+ margin-top: 40px;
+}
+
+.service-box {
+ background: #FFFFFF;
+ padding: 30px;
+ border-radius: 8px;
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
+ text-align: center;
+ width: 30%;
+}
+
+.service-box h3 {
+ font-size: 1.5rem;
+ margin-bottom: 1rem;
+}
+
+/* Contact Form */
+.contact-form {
+ max-width: 600px;
+ margin: 40px auto 0;
+}
+
+.form-group {
+ margin-bottom: 1.5rem;
+}
+
+.form-control {
+ width: 100%;
+ padding: 12px;
+ border: 1px solid #BDBDBD;
+ border-radius: 8px;
+ font-size: 1rem;
+}
+
+.form-control:focus {
+ outline: none;
+ border-color: #2F80ED;
+}
+
+/* Footer */
+.footer {
+ background: #333;
+ color: #FFFFFF;
+ text-align: center;
+ padding: 20px 0;
+}
+
+/* Form Messages */
+#form-messages .success {
+ color: #27AE60;
+ background: #E9F7EF;
+ padding: 10px;
+ border-radius: 8px;
+ margin-bottom: 1rem;
+}
+
+#form-messages .error {
+ color: #EB5757;
+ background: #FDEEEE;
+ padding: 10px;
+ border-radius: 8px;
+ margin-bottom: 1rem;
+}
\ No newline at end of file
diff --git a/assets/js/main.js b/assets/js/main.js
new file mode 100644
index 0000000..50e340f
--- /dev/null
+++ b/assets/js/main.js
@@ -0,0 +1,30 @@
+document.addEventListener('DOMContentLoaded', function() {
+ const contactForm = document.getElementById('contact-form');
+ const formMessages = document.getElementById('form-messages');
+
+ if (contactForm) {
+ contactForm.addEventListener('submit', function(e) {
+ e.preventDefault();
+
+ const formData = new FormData(contactForm);
+
+ fetch('handle_contact.php', {
+ method: 'POST',
+ body: formData
+ })
+ .then(response => response.json())
+ .then(data => {
+ if (data.success) {
+ formMessages.innerHTML = `
${data.message}
`;
+ contactForm.reset();
+ } else {
+ formMessages.innerHTML = `${data.message}
`;
+ }
+ })
+ .catch(error => {
+ formMessages.innerHTML = `An error occurred while sending your message. Please try again.
`;
+ console.error('Error:', error);
+ });
+ });
+ }
+});
\ No newline at end of file
diff --git a/contact.php b/contact.php
new file mode 100644
index 0000000..a7efceb
--- /dev/null
+++ b/contact.php
@@ -0,0 +1,47 @@
+ false, 'error' => 'Please fill out all fields.']);
+ exit;
+}
+
+if (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
+ echo json_encode(['success' => false, 'error' => 'Invalid email format.']);
+ exit;
+}
+
+$name = $_POST['name'];
+$email = $_POST['email'];
+$message = $_POST['message'];
+
+try {
+ // 1. Save to database
+ $pdo = db();
+ $stmt = $pdo->prepare("INSERT INTO contact_submissions (name, email, message) VALUES (?, ?, ?)");
+ $stmt->execute([$name, $email, $message]);
+
+ // 2. Send email notification
+ // The recipient can be configured via the MAIL_TO environment variable.
+ $mailResult = MailService::sendContactMessage($name, $email, $message, null, 'New Miralok Africa Inquiry');
+
+ if ($mailResult['success']) {
+ echo json_encode(['success' => true]);
+ } else {
+ // Log error, but don't expose it to the client for security.
+ error_log('MailService Error: ' . ($mailResult['error'] ?? 'Unknown error'));
+ // Still return success to the user as the main action (saving the submission) was successful.
+ echo json_encode(['success' => true, 'warning' => 'Could not send email notification.']);
+ }
+
+} catch (PDOException $e) {
+ error_log('Database Error: ' . $e->getMessage());
+ echo json_encode(['success' => false, 'error' => 'A server error occurred. Please try again later.']);
+} catch (Exception $e) {
+ error_log('General Error: ' . $e->getMessage());
+ echo json_encode(['success' => false, 'error' => 'A server error occurred. Please try again later.']);
+}
diff --git a/dashboard.php b/dashboard.php
new file mode 100644
index 0000000..bfa2e01
--- /dev/null
+++ b/dashboard.php
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+ Dashboard - Miralok Africa
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Welcome, !
+
This is your dashboard. From here you will be able to manage your profile, vehicles, and applications.
+
Your role is:
+
+
+
+
+
+
Quick Actions
+
More content and features will be added here soon.
+
+
+
+
+
+
+
+
diff --git a/db/config.php b/db/config.php
index cc9229f..2aa6380 100644
--- a/db/config.php
+++ b/db/config.php
@@ -1,5 +1,4 @@
exec("CREATE TABLE IF NOT EXISTS contact_submissions (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(255) NOT NULL,
+ email VARCHAR(255) NOT NULL,
+ message TEXT NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ );");
+
+ // Organizations Table
+ $pdo->exec("CREATE TABLE IF NOT EXISTS Organization (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(255) NOT NULL,
+ type ENUM('Insurer','Financier','OEM','FleetOperator','Partner') NOT NULL,
+ contactEmail VARCHAR(255),
+ phone VARCHAR(255),
+ address TEXT,
+ website VARCHAR(255),
+ approved BOOLEAN DEFAULT false,
+ createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ );");
+
+ // Users Table
+ $pdo->exec("CREATE TABLE IF NOT EXISTS User (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ firstName VARCHAR(255) NOT NULL,
+ lastName VARCHAR(255),
+ email VARCHAR(255) NOT NULL UNIQUE,
+ password VARCHAR(255) NOT NULL,
+ phone VARCHAR(255),
+ role ENUM('Rider','FleetOperator','Insurer','Financier','OEM','Partner','Support','Admin','Public') NOT NULL,
+ organizationId INT,
+ verified BOOLEAN DEFAULT false,
+ createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (organizationId) REFERENCES Organization(id) ON DELETE SET NULL
+ );");
+
+ // Fleets Table
+ $pdo->exec("CREATE TABLE IF NOT EXISTS Fleet (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(255) NOT NULL,
+ organizationId INT,
+ managerUserId INT,
+ notes TEXT,
+ createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (organizationId) REFERENCES Organization(id) ON DELETE CASCADE,
+ FOREIGN KEY (managerUserId) REFERENCES User(id) ON DELETE SET NULL
+ );");
+
+ // Vehicles Table
+ $pdo->exec("CREATE TABLE IF NOT EXISTS Vehicle (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ vin VARCHAR(255) UNIQUE,
+ model VARCHAR(255) NOT NULL,
+ licensePlate VARCHAR(255),
+ ownerUserId INT,
+ fleetId INT,
+ createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (ownerUserId) REFERENCES User(id) ON DELETE SET NULL,
+ FOREIGN KEY (fleetId) REFERENCES Fleet(id) ON DELETE SET NULL
+ );");
+
+ } catch (PDOException $e) {
+ die("Database initialization failed: " . $e->getMessage());
+ }
+}
+
+// Run the initialization function to ensure tables exist.
+db_init();
diff --git a/db/migrations/001_create_contact_submissions_table.sql b/db/migrations/001_create_contact_submissions_table.sql
new file mode 100644
index 0000000..541e784
--- /dev/null
+++ b/db/migrations/001_create_contact_submissions_table.sql
@@ -0,0 +1,7 @@
+CREATE TABLE IF NOT EXISTS `contact_submissions` (
+ `id` INT AUTO_INCREMENT PRIMARY KEY,
+ `name` VARCHAR(255) NOT NULL,
+ `email` VARCHAR(255) NOT NULL,
+ `message` TEXT NOT NULL,
+ `submitted_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
diff --git a/handle_contact.php b/handle_contact.php
new file mode 100644
index 0000000..398c0e1
--- /dev/null
+++ b/handle_contact.php
@@ -0,0 +1,61 @@
+ false, 'message' => 'An unknown error occurred.'];
+
+if ($_SERVER["REQUEST_METHOD"] == "POST") {
+ $name = trim($_POST['name'] ?? '');
+ $email = trim($_POST['email'] ?? '');
+ $message = trim($_POST['message'] ?? '');
+
+ if (empty($name) || empty($email) || empty($message)) {
+ $response['message'] = 'Please fill in all fields.';
+ echo json_encode($response);
+ exit;
+ }
+
+ if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
+ $response['message'] = 'Invalid email format.';
+ echo json_encode($response);
+ exit;
+ }
+
+ try {
+ $pdo = db();
+ $sql = "INSERT INTO contact_submissions (name, email, message) VALUES (?, ?, ?)";
+ $stmt = $pdo->prepare($sql);
+
+ if ($stmt->execute([$name, $email, $message])) {
+ // Database insert was successful, now send email
+ $mailResult = MailService::sendContactMessage($name, $email, $message);
+
+ if (!empty($mailResult['success'])) {
+ $response['success'] = true;
+ $response['message'] = 'Thank you for your message. We will get back to you shortly.';
+ } else {
+ // Email failed, but data is saved. This might be a configuration issue.
+ $response['success'] = true; // Still a success from user's perspective
+ $response['message'] = 'Thank you for your message. It has been received.';
+ // Log the email error if possible, e.g., error_log("MailService Error: " . $mailResult['error']);
+ }
+ } else {
+ $response['message'] = 'Error: Could not save your message.';
+ }
+ } catch (PDOException $e) {
+ // Log database error, don't show specific SQL errors to user
+ error_log($e->getMessage());
+ $response['message'] = 'A server error occurred. Please try again later.';
+ } catch (Exception $e) {
+ error_log($e->getMessage());
+ $response['message'] = 'An unexpected error occurred. Please try again later.';
+ }
+
+} else {
+ $response['message'] = 'Invalid request method.';
+}
+
+echo json_encode($response);
+?>
\ No newline at end of file
diff --git a/handle_login.php b/handle_login.php
new file mode 100644
index 0000000..794ed95
--- /dev/null
+++ b/handle_login.php
@@ -0,0 +1,40 @@
+ false, 'error' => 'Email and password are required.']);
+ exit;
+}
+
+$email = $_POST['email'];
+$password = $_POST['password'];
+
+try {
+ $pdo = db();
+
+ // 2. Fetch user by email
+ $stmt = $pdo->prepare("SELECT id, firstName, role, password FROM User WHERE email = ?");
+ $stmt->execute([$email]);
+ $user = $stmt->fetch();
+
+ // 3. Verify password
+ if ($user && password_verify($password, $user['password'])) {
+ // 4. Set session variables
+ $_SESSION['user_id'] = $user['id'];
+ $_SESSION['user_name'] = $user['firstName'];
+ $_SESSION['user_role'] = $user['role'];
+
+ echo json_encode(['success' => true, 'redirect' => 'dashboard.php']);
+ } else {
+ echo json_encode(['success' => false, 'error' => 'Invalid email or password.']);
+ }
+
+} catch (PDOException $e) {
+ error_log('Login Error: ' . $e->getMessage());
+ echo json_encode(['success' => false, 'error' => 'A server error occurred. Please try again later.']);
+}
diff --git a/handle_register.php b/handle_register.php
new file mode 100644
index 0000000..28bf5e3
--- /dev/null
+++ b/handle_register.php
@@ -0,0 +1,52 @@
+ false, 'error' => implode(' ', $errors)]);
+ exit;
+}
+
+$firstName = $_POST['firstName'];
+$lastName = $_POST['lastName'];
+$email = $_POST['email'];
+$password = $_POST['password'];
+
+try {
+ $pdo = db();
+
+ // 2. Check if user already exists
+ $stmt = $pdo->prepare("SELECT id FROM User WHERE email = ?");
+ $stmt->execute([$email]);
+ if ($stmt->fetch()) {
+ echo json_encode(['success' => false, 'error' => 'An account with this email already exists.']);
+ exit;
+ }
+
+ // 3. Hash password
+ $hashedPassword = password_hash($password, PASSWORD_DEFAULT);
+
+ // 4. Insert new user with 'Rider' role
+ $stmt = $pdo->prepare(
+ "INSERT INTO User (firstName, lastName, email, password, role) VALUES (?, ?, ?, ?, ?)"
+ );
+ $stmt->execute([$firstName, $lastName, $email, $hashedPassword, 'Rider']);
+
+ // 5. Return success
+ echo json_encode(['success' => true]);
+
+} catch (PDOException $e) {
+ error_log('Registration Error: ' . $e->getMessage());
+ echo json_encode(['success' => false, 'error' => 'A server error occurred during registration. Please try again later.']);
+}
diff --git a/index.php b/index.php
index 7205f3d..a69db6a 100644
--- a/index.php
+++ b/index.php
@@ -1,150 +1,61 @@
-
-$phpVersion = PHP_VERSION;
-$now = date('Y-m-d H:i:s');
-?>
-
-
-
-
-
- New Style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Analyzing your requirements and generating your website…
-
- Loading…
-
-
= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.
-
This page will update automatically as the plan is implemented.
-
Runtime: PHP = htmlspecialchars($phpVersion) ?> — UTC = htmlspecialchars($now) ?>
+
+
+
Connecting the Future of African Mobility
+
A unified online ecosystem for electric vehicle adoption, financing, and insurance.
+
Get Started Today
-
-
- Page updated: = htmlspecialchars($now) ?> (UTC)
-
-
-
+
+
+
+
+
About Miralok Africa
+
Miralok Africa provides online business services that connect riders, fleet operators, insurers, and financiers. We serve as a continental web portal offering verified information, strategic partnerships, and digital workflows that simplify how Africans access, finance, and protect electric mobility assets.
+
+
+
+
+
+
Our Services
+
+
+
Educate
+
We educate the public on EV technology, financing models, and cost-of-ownership.
+
+
+
Connect
+
We connect insurers, OEMs, and fleet managers for transparent collaboration.
+
+
+
Host
+
We host digital information, forms, and partner dashboards to reduce time and cost of adoption.
+
+
+
+
+
+
+
+
Contact Us
+
Have a question or want to partner with us? Send us a message.
+
+
+
+
+
\ No newline at end of file
diff --git a/login.php b/login.php
new file mode 100644
index 0000000..3588e78
--- /dev/null
+++ b/login.php
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
Login - Miralok Africa
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/logout.php b/logout.php
new file mode 100644
index 0000000..ea4deb3
--- /dev/null
+++ b/logout.php
@@ -0,0 +1,22 @@
+
+
+
+
+
+
Register - Miralok Africa
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/shared/footer.php b/shared/footer.php
new file mode 100644
index 0000000..11c50bd
--- /dev/null
+++ b/shared/footer.php
@@ -0,0 +1,9 @@
+
+
+
+