From b4ffcec973013b66e443e8aee2596632de4a27e6 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 12 Dec 2025 09:47:39 +0000 Subject: [PATCH] feat: Implement engineer and contract management - Add Field Engineer Management feature, allowing creation, and assignment of engineers to service requests. - Add Service Contract/AMC Management feature, allowing creation of contracts and linking them to service requests. - Update admin panel to manage engineers and contracts. - Update service request form to include contract selection. --- admin/auth.php | 7 + admin/contracts.php | 131 +++++++++++++ admin/engineers.php | 82 ++++++++ admin/index.php | 120 ++++++++++++ admin/login.php | 54 ++++++ admin/logout.php | 6 + admin/update_status.php | 47 +++++ assets/css/style.css | 54 ++++++ ..._add_scheduled_for_to_service_requests.php | 42 ++++ db/migrations/002_create_engineers_table.php | 25 +++ ...03_add_engineer_id_to_service_requests.php | 18 ++ db/migrations/004_create_contracts_table.php | 28 +++ ...05_add_contract_id_to_service_requests.php | 24 +++ db/setup.php | 41 ++++ footer.php | 10 + header.php | 40 ++++ index.php | 179 +++--------------- request-service.php | 132 +++++++++++++ whatsapp_webhook.php | 109 +++++++++++ 19 files changed, 1001 insertions(+), 148 deletions(-) create mode 100644 admin/auth.php create mode 100644 admin/contracts.php create mode 100644 admin/engineers.php create mode 100644 admin/index.php create mode 100644 admin/login.php create mode 100644 admin/logout.php create mode 100644 admin/update_status.php create mode 100644 assets/css/style.css create mode 100644 db/migrations/001_add_scheduled_for_to_service_requests.php create mode 100644 db/migrations/002_create_engineers_table.php create mode 100644 db/migrations/003_add_engineer_id_to_service_requests.php create mode 100644 db/migrations/004_create_contracts_table.php create mode 100644 db/migrations/005_add_contract_id_to_service_requests.php create mode 100644 db/setup.php create mode 100644 footer.php create mode 100644 header.php create mode 100644 request-service.php create mode 100644 whatsapp_webhook.php diff --git a/admin/auth.php b/admin/auth.php new file mode 100644 index 0000000..0cd150e --- /dev/null +++ b/admin/auth.php @@ -0,0 +1,7 @@ +prepare( + 'INSERT INTO contracts (customer_name, customer_email, customer_phone, contract_title, start_date, end_date) VALUES (?, ?, ?, ?, ?, ?)' + ); + $stmt->execute([$customer_name, $customer_email, $customer_phone, $contract_title, $start_date, $end_date]); + } + // Redirect to avoid form resubmission + header('Location: contracts.php'); + exit; +} + +// Fetch all contracts to display +$pdo = db(); +$contracts = $pdo->query('SELECT * FROM contracts ORDER BY created_at DESC')->fetchAll(PDO::FETCH_ASSOC); + +require_once __DIR__ . '/../header.php'; +?> + +
+
+

Contract Management

+
+ Back to Jobs + Manage Engineers + Logout +
+
+ +
+
+ Add New Contract +
+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+ +

Existing Contracts

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
CustomerContract/AMC TitleValidityContactActions
No contracts found.
+ + - + + + N/A + + +
+ +
+ Edit + Delete +
+
+
+
+ + \ No newline at end of file diff --git a/admin/engineers.php b/admin/engineers.php new file mode 100644 index 0000000..8e12248 --- /dev/null +++ b/admin/engineers.php @@ -0,0 +1,82 @@ +prepare("INSERT INTO engineers (name, phone) VALUES (:name, :phone)"); + $stmt->execute([':name' => $name, ':phone' => $phone]); + } catch (PDOException $e) { + echo "Error: " . $e->getMessage(); + } + } +} + +// Fetch all engineers +try { + $pdo = db(); + $engineers = $pdo->query("SELECT * FROM engineers ORDER BY created_at DESC")->fetchAll(PDO::FETCH_ASSOC); +} catch (PDOException $e) { + die("DB ERROR: " . $e->getMessage()); +} + +?> + +
+
+
+

Engineer Management

+

Back to Job Management

+ +
+
Add New Engineer
+
+
+
+ + +
+
+ + +
+ +
+
+
+ +
+
All Engineers
+
+ + + + + + + + + + + + + + + + + +
NamePhoneRegistered On
+
+
+
+
+
+ + diff --git a/admin/index.php b/admin/index.php new file mode 100644 index 0000000..245715d --- /dev/null +++ b/admin/index.php @@ -0,0 +1,120 @@ +query(" + SELECT + sr.*, + c.contract_title, + c.customer_name AS contract_customer_name + FROM service_requests sr + LEFT JOIN contracts c ON sr.contract_id = c.id + ORDER BY sr.created_at DESC +"); +$requests = $stmt->fetchAll(); + +// Fetch all engineers +$stmt = db()->query("SELECT * FROM engineers ORDER BY name ASC"); +$engineers = $stmt->fetchAll(); + +require_once '../header.php'; +?> + +
+
+

Admin Dashboard

+
+Manage Engineers + Manage Contracts + Logout +
+
+ +

Job Management

+ +
+
+ 0): ?> +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IDCustomerContractJob TitleJob DescriptionAssigned EngineerScheduled ForStatusSubmittedAction
+ + + + + +
+ +
+ + N/A + +
+ + + + + + + + + +
+ +
+
+ +

No service requests yet.

+ +
+
+
+ + \ No newline at end of file diff --git a/admin/login.php b/admin/login.php new file mode 100644 index 0000000..42a14c0 --- /dev/null +++ b/admin/login.php @@ -0,0 +1,54 @@ +prepare("SELECT * FROM users WHERE username = ?"); + $stmt->execute([$username]); + $user = $stmt->fetch(); + + if ($user && password_verify($password, $user['password_hash'])) { + $_SESSION['user_id'] = $user['id']; + $_SESSION['username'] = $user['username']; + header('Location: index.php'); + exit; + } else { + $error = 'Invalid username or password.'; + } + } +} + +require_once '../header.php'; +?> + +
+
+
+

Admin Login

+ +
+ +
+
+ + +
+
+ + +
+ +
+
+
+
+ + diff --git a/admin/logout.php b/admin/logout.php new file mode 100644 index 0000000..95db42c --- /dev/null +++ b/admin/logout.php @@ -0,0 +1,6 @@ + $job_data) { + // If we are not updating all, only process the job that was singled out + if (!$update_all && $id != $update_single_id) { + continue; + } + + $job_title = $job_data['job_title'] ?? 'Service Request'; + $scheduled_for = !empty($job_data['scheduled_for']) ? date('Y-m-d H:i:s', strtotime($job_data['scheduled_for'])) : null; + $status = $job_data['status'] ?? 'New'; + $engineer_id = !empty($job_data['engineer_id']) ? (int)$job_data['engineer_id'] : null; + + // Validate status + if (in_array($status, $allowed_statuses)) { + try { + $stmt = db()->prepare( + "UPDATE service_requests + SET job_title = ?, scheduled_for = ?, status = ?, engineer_id = ? + WHERE id = ?" + ); + $stmt->execute([$job_title, $scheduled_for, $status, $engineer_id, $id]); + } catch (PDOException $e) { + // Optional: Handle database errors + // For now, we will ignore and continue + } + } + + // If we were only updating one, we can stop now + if (!$update_all) { + break; + } + } +} + +// Redirect back to the admin dashboard +header('Location: index.php'); +exit; \ No newline at end of file diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100644 index 0000000..fe542ac --- /dev/null +++ b/assets/css/style.css @@ -0,0 +1,54 @@ +/* Custom Styles for Water Purifier Service CRM */ + +:root { + --primary-color: #20c997; /* Teal */ + --secondary-color: #343a40; /* Dark Grey */ + --light-grey: #f8f9fa; + --white: #ffffff; +} + +body { + font-family: 'system-ui', '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'Helvetica Neue', 'Arial', 'sans-serif'; +} + +.navbar-brand i { + color: var(--primary-color); +} + +.btn-primary { + background-color: var(--primary-color); + border-color: var(--primary-color); +} + +.btn-primary:hover { + background-color: #1aa07e; + border-color: #1aa07e; +} + +.form-control:focus { + border-color: var(--primary-color); + box-shadow: 0 0 0 0.25rem rgba(32, 201, 151, 0.25); +} + +.hero-section { + background: var(--light-grey); + padding: 4rem 0; + text-align: center; + border-radius: .5rem; +} + +.hero-section h1 { + font-weight: 700; + color: var(--secondary-color); +} + +.hero-section .lead { + max-width: 600px; + margin: 1.5rem auto; +} + +.card { + border: 1px solid #dee2e6; + border-radius: .5rem; + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); +} diff --git a/db/migrations/001_add_scheduled_for_to_service_requests.php b/db/migrations/001_add_scheduled_for_to_service_requests.php new file mode 100644 index 0000000..54a710d --- /dev/null +++ b/db/migrations/001_add_scheduled_for_to_service_requests.php @@ -0,0 +1,42 @@ +prepare("SHOW COLUMNS FROM `$table` LIKE ?"); + $stmt->execute([$column]); + return $stmt->fetch() !== false; + } + + // Add scheduled_for column if it doesn't exist + if (!columnExists($pdo, 'service_requests', 'scheduled_for')) { + $pdo->exec("ALTER TABLE service_requests ADD COLUMN scheduled_for DATETIME DEFAULT NULL"); + echo "Column 'scheduled_for' added.\n"; + } else { + echo "Column 'scheduled_for' already exists.\n"; + } + + // Add job_title column if it doesn't exist + if (!columnExists($pdo, 'service_requests', 'job_title')) { + $pdo->exec("ALTER TABLE service_requests ADD COLUMN job_title VARCHAR(255) NOT NULL DEFAULT 'Service Request'"); + echo "Column 'job_title' added.\n"; + } else { + echo "Column 'job_title' already exists.\n"; + } + + // Rename service_type to job_description if service_type exists and job_description does not + if (columnExists($pdo, 'service_requests', 'service_type') && !columnExists($pdo, 'service_requests', 'job_description')) { + $pdo->exec("ALTER TABLE service_requests CHANGE service_type job_description TEXT"); + echo "Column 'service_type' renamed to 'job_description'.\n"; + } else { + echo "Column 'service_type' not found or 'job_description' already exists.\n"; + } + + echo "Migration completed successfully."; + +} catch (PDOException $e) { + die("Migration failed: " . $e->getMessage()); +} \ No newline at end of file diff --git a/db/migrations/002_create_engineers_table.php b/db/migrations/002_create_engineers_table.php new file mode 100644 index 0000000..2961136 --- /dev/null +++ b/db/migrations/002_create_engineers_table.php @@ -0,0 +1,25 @@ +query("SHOW TABLES LIKE 'engineers'"); + if ($stmt->rowCount() == 0) { + $pdo->exec(" + CREATE TABLE engineers ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + phone VARCHAR(255) NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + "); + echo "Table 'engineers' created successfully." . PHP_EOL; + } else { + echo "Table 'engineers' already exists." . PHP_EOL; + } + +} catch (PDOException $e) { + die("DB ERROR: " . $e->getMessage()); +} diff --git a/db/migrations/003_add_engineer_id_to_service_requests.php b/db/migrations/003_add_engineer_id_to_service_requests.php new file mode 100644 index 0000000..063041d --- /dev/null +++ b/db/migrations/003_add_engineer_id_to_service_requests.php @@ -0,0 +1,18 @@ +query("SHOW COLUMNS FROM service_requests LIKE 'engineer_id'"); + if ($stmt->rowCount() == 0) { + $pdo->exec("ALTER TABLE service_requests ADD COLUMN engineer_id INT NULL"); + echo "Column 'engineer_id' added to 'service_requests' table successfully." . PHP_EOL; + } else { + echo "Column 'engineer_id' already exists in 'service_requests' table." . PHP_EOL; + } + +} catch (PDOException $e) { + die("DB ERROR: " . $e->getMessage()); +} diff --git a/db/migrations/004_create_contracts_table.php b/db/migrations/004_create_contracts_table.php new file mode 100644 index 0000000..2b84bbf --- /dev/null +++ b/db/migrations/004_create_contracts_table.php @@ -0,0 +1,28 @@ +query("SHOW TABLES LIKE 'contracts'"); + if ($stmt->rowCount() == 0) { + $sql = " + CREATE TABLE contracts ( + id INT AUTO_INCREMENT PRIMARY KEY, + customer_name VARCHAR(255) NOT NULL, + customer_email VARCHAR(255), + customer_phone VARCHAR(50), + contract_title VARCHAR(255) NOT NULL, + start_date DATE, + end_date DATE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ); + "; + $pdo->exec($sql); + echo "Table 'contracts' created successfully." . PHP_EOL; + } else { + echo "Table 'contracts' already exists." . PHP_EOL; + } +} catch (PDOException $e) { + die("DB ERROR: " . $e->getMessage()); +} diff --git a/db/migrations/005_add_contract_id_to_service_requests.php b/db/migrations/005_add_contract_id_to_service_requests.php new file mode 100644 index 0000000..8aaaf1a --- /dev/null +++ b/db/migrations/005_add_contract_id_to_service_requests.php @@ -0,0 +1,24 @@ +query("SHOW COLUMNS FROM `service_requests` LIKE 'contract_id'"); + if ($stmt->rowCount() == 0) { + $sql = " + ALTER TABLE service_requests + ADD COLUMN contract_id INT NULL, + ADD CONSTRAINT fk_contract_id + FOREIGN KEY (contract_id) + REFERENCES contracts(id) + ON DELETE SET NULL; + "; + $pdo->exec($sql); + echo "Column 'contract_id' added to 'service_requests' table." . PHP_EOL; + } else { + echo "Column 'contract_id' already exists in 'service_requests' table." . PHP_EOL; + } +} catch (PDOException $e) { + die("DB ERROR: " . $e->getMessage()); +} diff --git a/db/setup.php b/db/setup.php new file mode 100644 index 0000000..e21844a --- /dev/null +++ b/db/setup.php @@ -0,0 +1,41 @@ +exec(" + CREATE TABLE IF NOT EXISTS `service_requests` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(255) NOT NULL, + `phone` VARCHAR(255) NOT NULL, + `address` TEXT NOT NULL, + `service_type` VARCHAR(255) NOT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ); + "); + + // Create users table for admin authentication + $pdo->exec(" + CREATE TABLE IF NOT EXISTS `users` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `username` VARCHAR(255) NOT NULL UNIQUE, + `password_hash` VARCHAR(255) NOT NULL + ); + "); + + // Add a default admin user if one doesn't exist + $stmt = $pdo->prepare("SELECT id FROM users WHERE username = 'admin'"); + $stmt->execute(); + if ($stmt->fetch() === false) { + $pdo->prepare("INSERT INTO users (username, password_hash) VALUES (?, ?)") + ->execute(['admin', password_hash('password', PASSWORD_DEFAULT)]); + } + + echo "Database setup completed successfully.\n"; + +} catch (PDOException $e) { + die("Database setup failed: " . $e->getMessage() . "\n"); +} + diff --git a/footer.php b/footer.php new file mode 100644 index 0000000..e5c6da9 --- /dev/null +++ b/footer.php @@ -0,0 +1,10 @@ + + + + + + + diff --git a/header.php b/header.php new file mode 100644 index 0000000..bbe6454 --- /dev/null +++ b/header.php @@ -0,0 +1,40 @@ + + + + + + <?php echo htmlspecialchars($_SERVER['PROJECT_NAME'] ?? 'Water Purifier Service'); ?> + + + + + + + + + + +
diff --git a/index.php b/index.php index 7205f3d..d50fade 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,33 @@ - -$phpVersion = PHP_VERSION; -$now = date('Y-m-d H:i:s'); -?> - - - - - - 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

+
+

Reliable Water Purifier Service

+

Your one-stop solution for installation, repair, and maintenance. Keep your water safe and pure.

+ Request a Service Today +
+ +
+
+
+ +

Expert Repair

+

Our certified technicians can fix any issue with your water purifier, ensuring optimal performance.

+
-
-
- Page updated: (UTC) -
- - +
+
+ +

AMC Plans

+

Enjoy peace of mind with our Annual Maintenance Contracts, including regular check-ups and filter changes.

+
+
+
+
+ +

Genuine Parts

+

We use only genuine, high-quality spare parts and filters to guarantee the longevity of your appliance.

+
+
+ + + \ No newline at end of file diff --git a/request-service.php b/request-service.php new file mode 100644 index 0000000..a3875fe --- /dev/null +++ b/request-service.php @@ -0,0 +1,132 @@ +query('SELECT id, contract_title, customer_name FROM contracts ORDER BY customer_name, contract_title'); +$contracts = $contracts_stmt->fetchAll(PDO::FETCH_ASSOC); + +$success_message = ''; +$error_message = ''; +$form_data = array_fill_keys(['name', 'phone', 'email', 'address', 'service_type', 'preferred_date', 'description', 'contract_id'], ''); + +if ($_SERVER["REQUEST_METHOD"] == "POST") { + // Sanitize and retrieve form data + $name = trim($_POST['name'] ?? ''); + $phone = trim($_POST['phone'] ?? ''); + $email = trim($_POST['email'] ?? ''); + $address = trim($_POST['address'] ?? ''); + $service_type = trim($_POST['service_type'] ?? ''); + $preferred_date = trim($_POST['preferred_date'] ?? ''); + $description = trim($_POST['description'] ?? ''); + $contract_id = trim($_POST['contract_id'] ?? ''); + + // Store submitted data to re-populate the form on error + $form_data = compact('name', 'phone', 'email', 'address', 'service_type', 'preferred_date', 'description', 'contract_id'); + + // Server-side validation + if (empty($name) || empty($phone) || empty($address) || empty($service_type)) { + $error_message = 'Please fill in all required fields: Name, Phone, Address, and Service Type.'; + } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL) && !empty($email)) { + $error_message = 'Please provide a valid email address.'; + } else { + try { + $pdo = db(); + $sql = "INSERT INTO service_requests (name, phone, email, address, service_type, preferred_date, description, contract_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + $stmt = $pdo->prepare($sql); + + // Handle empty date and contract + $date_to_insert = !empty($preferred_date) ? $preferred_date : null; + $contract_to_insert = !empty($contract_id) ? $contract_id : null; + + $stmt->execute([$name, $phone, $email, $address, $service_type, $date_to_insert, $description, $contract_to_insert]); + + $success_message = "Thank you! Your service request has been submitted successfully. We will contact you shortly."; + // Clear form data on success + $form_data = array_fill_keys(array_keys($form_data), ''); + + } catch (PDOException $e) { + error_log("Service Request Error: " . $e->getMessage()); + $error_message = 'Sorry, there was an error submitting your request. Please try again later.'; + } + } +} + +include 'header.php'; +?> + +
+
+
+
+

Submit a Service Request

+ + +
+ +
+ + + +
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+
+
+ + diff --git a/whatsapp_webhook.php b/whatsapp_webhook.php new file mode 100644 index 0000000..46bf326 --- /dev/null +++ b/whatsapp_webhook.php @@ -0,0 +1,109 @@ +prepare("INSERT INTO service_requests (name, email, description, status) VALUES (?, ?, ?, 'New')"); + $stmt->execute([ + $session_data['name'], + $session_data['email'], + $session_data['description'] + ]); + send_whatsapp_reply($from, "Thank you! Your service request has been booked. We will contact you shortly."); + // End of conversation, so delete the session file + unlink($session_file); + + } catch (PDOException $e) { + error_log("Database Error: " . $e->getMessage()); + send_whatsapp_reply($from, "Sorry, there was a problem booking your service. Please try again later."); + } + + // Reset state for the next conversation + $session_data = ['state' => 'new']; + file_put_contents($session_file, json_encode($session_data)); + break; + + default: + send_whatsapp_reply($from, "Sorry, I'm not sure how to respond. Let's start over. What is your full name?"); + // Reset state + $session_data = ['state' => 'waiting_for_name']; + file_put_contents($session_file, json_encode($session_data)); + break; +} + +echo "OK";