From 55c4d7eddb9be0a1567e2f7109bcf9533b92ef2d Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 23 Jan 2026 20:06:08 +0000 Subject: [PATCH] Next steps --- db/init.sql | 42 +++++--- setup.php | 295 ++++++++++++++++++++++++++++++++++------------------ 2 files changed, 224 insertions(+), 113 deletions(-) diff --git a/db/init.sql b/db/init.sql index 9a02767..50405f2 100644 --- a/db/init.sql +++ b/db/init.sql @@ -1,41 +1,59 @@ +-- Initialize company setup (companies, statuses, folders) and core user management. +-- Designed for multi-tenant applications where each company has isolated data. + +-- Companies Table: Stores information about each client company. CREATE TABLE IF NOT EXISTS companies ( id INT AUTO_INCREMENT PRIMARY KEY, - name VARCHAR(255) NOT NULL, + name VARCHAR(255) NOT NULL UNIQUE, uprn_required BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); +-- Job Statuses Table: Stores custom job statuses defined by each company. CREATE TABLE IF NOT EXISTS job_statuses ( id INT AUTO_INCREMENT PRIMARY KEY, company_id INT NOT NULL, name VARCHAR(255) NOT NULL, is_default BOOLEAN DEFAULT FALSE, sort_order INT DEFAULT 0, - FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE, + UNIQUE KEY (company_id, name) ); +-- Required Folders Table: Stores mandatory folder structures defined by each company. CREATE TABLE IF NOT EXISTS required_folders ( id INT AUTO_INCREMENT PRIMARY KEY, company_id INT NOT NULL, name VARCHAR(255) NOT NULL, - FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE -); - -CREATE TABLE IF NOT EXISTS clients ( - id INT AUTO_INCREMENT PRIMARY KEY, - company_id INT NOT NULL, - name VARCHAR(255) NOT NULL, - is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE + FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE, + UNIQUE KEY (company_id, name) ); +-- Users Table: Stores user accounts. Each user belongs to a specific company. CREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, company_id INT NOT NULL, name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, - role ENUM('admin', 'standard') DEFAULT 'standard', + role ENUM('admin', 'standard') DEFAULT 'standard', -- Admin can manage company settings, standard users manage jobs. + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE ); + +-- Clients Table: Stores clients for each company. Clients can be added, edited, but not deleted. +CREATE TABLE IF NOT EXISTS clients ( + id INT AUTO_INCREMENT PRIMARY KEY, + company_id INT NOT NULL, + name VARCHAR(255) NOT NULL, + contact_person VARCHAR(255) DEFAULT NULL, + email VARCHAR(255) DEFAULT NULL, + phone VARCHAR(255) DEFAULT NULL, + is_active BOOLEAN DEFAULT TRUE, -- Clients can be marked inactive instead of deleted. + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE, + UNIQUE KEY (company_id, name) -- Ensure client names are unique per company +); \ No newline at end of file diff --git a/setup.php b/setup.php index 1ee7b1c..5853596 100644 --- a/setup.php +++ b/setup.php @@ -2,79 +2,132 @@ declare(strict_types=1); require_once __DIR__ . '/db/config.php'; +session_start(); + $error = ''; $success = ''; +$step = $_GET['step'] ?? 1; +$companyId = $_SESSION['company_id'] ?? null; + +// Redirect to index if setup is complete and companyId is not in session +if ($step > 1 && !$companyId) { + header('Location: index.php'); + exit; +} if ($_SERVER['REQUEST_METHOD'] === 'POST') { - $companyName = $_POST['company_name'] ?? ''; - $uprnRequired = isset($_POST['uprn_required']) ? 1 : 0; - $statuses = $_POST['statuses'] ?? []; - $folders = $_POST['folders'] ?? []; - $defaultStatusIndex = (int)($_POST['default_status'] ?? 0); + if ($step == 1) { + $companyName = $_POST['company_name'] ?? ''; + $uprnRequired = isset($_POST['uprn_required']) ? 1 : 0; + $statuses = $_POST['statuses'] ?? []; + $folders = $_POST['folders'] ?? []; + $defaultStatusIndex = (int)($_POST['default_status'] ?? 0); - if (empty($companyName)) { - $error = "Company name is required."; - } elseif (empty($statuses)) { - $error = "At least one job status is required."; - } elseif (empty($folders)) { - $error = "At least one required folder is required."; - } else { - try { - db()->beginTransaction(); + if (empty($companyName)) { + $error = "Company name is required."; + } elseif (empty(array_filter($statuses, 'trim'))) { + $error = "At least one job status is required."; + } elseif (empty(array_filter($folders, 'trim'))) { + $error = "At least one required folder is required."; + } else { + try { + db()->beginTransaction(); - // 1. Create Company - $stmt = db()->prepare("INSERT INTO companies (name, uprn_required) VALUES (?, ?)"); - $stmt->execute([$companyName, $uprnRequired]); - $companyId = db()->lastInsertId(); + // 1. Create Company + $stmt = db()->prepare("INSERT INTO companies (name, uprn_required) VALUES (?, ?)"); + $stmt->execute([$companyName, $uprnRequired]); + $companyId = db()->lastInsertId(); + $_SESSION['company_id'] = $companyId; // Store company ID in session for next steps - // 2. Insert Statuses - $stmt = db()->prepare("INSERT INTO job_statuses (company_id, name, is_default, sort_order) VALUES (?, ?, ?, ?)"); - foreach ($statuses as $index => $statusName) { - if (trim($statusName) === '') continue; - $isDefault = ($index === $defaultStatusIndex) ? 1 : 0; - $stmt->execute([$companyId, $statusName, $isDefault, $index]); + // 2. Insert Statuses + $stmt = db()->prepare("INSERT INTO job_statuses (company_id, name, is_default, sort_order) VALUES (?, ?, ?, ?)"); + foreach ($statuses as $index => $statusName) { + $statusName = trim($statusName); + if ($statusName === '') continue; + $isDefault = ($index === $defaultStatusIndex) ? 1 : 0; + $stmt->execute([$companyId, $statusName, $isDefault, $index]); + } + + // 3. Insert Folders + $stmt = db()->prepare("INSERT INTO required_folders (company_id, name) VALUES (?, ?)"); + foreach ($folders as $folderName) { + $folderName = trim($folderName); + if ($folderName === '') continue; + $stmt->execute([$companyId, $folderName]); + } + + // 4. Create first Admin user (simplified for demo) + $adminEmail = 'admin@' . strtolower(str_replace(' ', '', $companyName)) . '.com'; + $stmt = db()->prepare("INSERT INTO users (company_id, name, email, password, role) VALUES (?, ?, ?, ?, ?)"); + $stmt->execute([$companyId, 'Admin User', $adminEmail, password_hash('password123', PASSWORD_DEFAULT), 'admin']); + + db()->commit(); + header('Location: setup.php?step=2'); // Redirect to next step + exit; + } catch (Exception $e) { + db()->rollBack(); + $error = "Database error: " . $e->getMessage(); } + } + } elseif ($step == 2) { + // Step 2: Client Setup + $clientNames = $_POST['client_names'] ?? []; + $contactPeople = $_POST['contact_people'] ?? []; + $clientEmails = $_POST['client_emails'] ?? []; + $clientPhones = $_POST['client_phones'] ?? []; - // 3. Insert Folders - $stmt = db()->prepare("INSERT INTO required_folders (company_id, name) VALUES (?, ?)"); - foreach ($folders as $folderName) { - if (trim($folderName) === '') continue; - $stmt->execute([$companyId, $folderName]); + if (empty(array_filter($clientNames, 'trim'))) { + $error = "At least one client is required."; + } else { + try { + db()->beginTransaction(); + + $stmt = db()->prepare("INSERT INTO clients (company_id, name, contact_person, email, phone) VALUES (?, ?, ?, ?, ?)"); + foreach ($clientNames as $index => $clientName) { + $clientName = trim($clientName); + if ($clientName === '') continue; + $contactPerson = trim($contactPeople[$index] ?? ''); + $clientEmail = trim($clientEmails[$index] ?? ''); + $clientPhone = trim($clientPhones[$index] ?? ''); + + $stmt->execute([$companyId, $clientName, $contactPerson, $clientEmail, $clientPhone]); + } + + db()->commit(); + session_destroy(); // Clear session after successful setup + $success = "Company setup successfully! You can now log in."; + header('Refresh: 2; URL=index.php'); // Redirect to index + exit; + } catch (Exception $e) { + db()->rollBack(); + $error = "Database error: " . $e->getMessage(); } - - // 4. Create first Admin user (simplified for demo) - $stmt = db()->prepare("INSERT INTO users (company_id, name, email, password, role) VALUES (?, ?, ?, ?, ?)"); - $stmt->execute([$companyId, 'Admin User', 'admin@' . strtolower(str_replace(' ', '', $companyName)) . '.com', password_hash('password123', PASSWORD_DEFAULT), 'admin']); - - db()->commit(); - $success = "Company setup successfully! You can now log in."; - header('Refresh: 2; URL=index.php'); - } catch (Exception $e) { - db()->rollBack(); - $error = "Database error: " . $e->getMessage(); } } } + +$pageTitle = "Company Onboarding"; ?> - Company Onboarding - RepairsPro + <?= $pageTitle ?> - RepairsPro
-

Company Onboarding

+

Company Onboarding - Step

@@ -82,66 +135,83 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
- + -
- -
- - -
- -
-
- - -
- If enabled, every job must have a unique UPRN. -
- -
- - -
- -

Define the workflow stages for your jobs.

-
-
- - + + +
+ +
-
- - - -
-
- -
-
- - -
- -

These folders will appear on every job automatically.

-
-
- +
+
+ + +
+ If enabled, every job must have a unique UPRN.
-
- - -
-
- -
-
- -
- - +
+ +
+ +

Define the workflow stages for your jobs.

+
+
+ + +
+
+ + + +
+
+ +
+ +
+ +
+ +

These folders will appear on every job automatically.

+
+
+ +
+
+ + +
+
+ +
+ +
+ +
+ + +
+

Add your initial clients. You can add more later.

+
+
+ + + + + +
+
+ + +
+ +
+
+ +
@@ -175,10 +245,33 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { }; } - // Attach remove events to existing buttons - document.querySelectorAll('.remove-btn').forEach(btn => { + function addClientRow() { + const container = document.getElementById('client-container'); + const div = document.createElement('div'); + div.className = 'dynamic-row client-input-group'; + + div.innerHTML = ` + + + + + + `; + container.appendChild(div); + + div.querySelector('.remove-btn').onclick = function() { div.remove(); }; + } + + // Attach remove events to existing buttons (for statuses and folders initially) + document.querySelectorAll('#status-container .remove-btn, #folder-container .remove-btn').forEach(btn => { btn.onclick = function() { btn.parentElement.remove(); }; }); + + // Initial client row for step 2 if no clients are pre-filled + if (document.getElementById('client-container') && document.getElementById('client-container').children.length === 0) { + // addClientRow(); // Only add if step 2 is active and no existing clients (for initial load) + } + - + \ No newline at end of file