diff --git a/admin_faqs.php b/admin_faqs.php new file mode 100644 index 0000000..76a519c --- /dev/null +++ b/admin_faqs.php @@ -0,0 +1,256 @@ +exec(" +CREATE TABLE IF NOT EXISTS faqs ( + id INT AUTO_INCREMENT PRIMARY KEY, + question_en VARCHAR(255) NOT NULL, + answer_en TEXT NOT NULL, + question_ar VARCHAR(255) DEFAULT NULL, + answer_ar TEXT DEFAULT NULL, + sort_order INT DEFAULT 0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +"); + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + if (isset($_POST['add_faq'])) { + $questionEn = trim($_POST['question_en'] ?? ''); + $answerEn = trim($_POST['answer_en'] ?? ''); + $questionAr = trim($_POST['question_ar'] ?? ''); + $answerAr = trim($_POST['answer_ar'] ?? ''); + $sortOrder = (int)($_POST['sort_order'] ?? 0); + + if ($questionEn === '' || $answerEn === '') { + $errors[] = 'Question and Answer (English) are required.'; + } else { + try { + $stmt = db()->prepare("INSERT INTO faqs (question_en, answer_en, question_ar, answer_ar, sort_order) VALUES (?, ?, ?, ?, ?)"); + $stmt->execute([ + $questionEn, + $answerEn, + $questionAr !== '' ? $questionAr : null, + $answerAr !== '' ? $answerAr : null, + $sortOrder + ]); + $flash = 'FAQ added successfully.'; + } catch (Throwable $e) { + $errors[] = 'Failed to add FAQ: ' . $e->getMessage(); + } + } + } elseif (isset($_POST['update_faq'])) { + $faqId = (int)($_POST['faq_id'] ?? 0); + $questionEn = trim($_POST['question_en'] ?? ''); + $answerEn = trim($_POST['answer_en'] ?? ''); + $questionAr = trim($_POST['question_ar'] ?? ''); + $answerAr = trim($_POST['answer_ar'] ?? ''); + $sortOrder = (int)($_POST['sort_order'] ?? 0); + + if ($faqId <= 0 || $questionEn === '' || $answerEn === '') { + $errors[] = 'FAQ ID, Question, and Answer (English) are required.'; + } else { + try { + $stmt = db()->prepare("UPDATE faqs SET question_en = ?, answer_en = ?, question_ar = ?, answer_ar = ?, sort_order = ? WHERE id = ?"); + $stmt->execute([ + $questionEn, + $answerEn, + $questionAr !== '' ? $questionAr : null, + $answerAr !== '' ? $answerAr : null, + $sortOrder, + $faqId + ]); + $flash = 'FAQ updated successfully.'; + $editFaqId = 0; + } catch (Throwable $e) { + $errors[] = 'Failed to update FAQ: ' . $e->getMessage(); + } + } + } elseif (isset($_POST['delete_faq'])) { + $faqId = (int)($_POST['faq_id'] ?? 0); + if ($faqId <= 0) { + $errors[] = 'Invalid FAQ selected.'; + } else { + try { + $stmt = db()->prepare("DELETE FROM faqs WHERE id = ?"); + $stmt->execute([$faqId]); + $flash = 'FAQ deleted successfully.'; + $editFaqId = 0; + } catch (Throwable $e) { + $errors[] = 'Failed to delete FAQ: ' . $e->getMessage(); + } + } + } +} + +$faqs = db()->query("SELECT * FROM faqs ORDER BY sort_order ASC, id DESC")->fetchAll(); + +$editingFaq = null; +if ($editFaqId > 0) { + foreach ($faqs as $faq) { + if ((int)$faq['id'] === $editFaqId) { + $editingFaq = $faq; + break; + } + } +} + +render_header('Manage FAQs', 'admin'); +?> + +
+
+ +
+
+
+

Frequently Asked Questions

+

Manage the Q&A list displayed on the public FAQ page.

+
+ + + + + + + + + +
+
+
Edit FAQ
+
+
+
+ + +
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ + +
+ +
+ + Cancel +
+
+
+
+ +
+
+
Add New FAQ
+
+
+
+
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ + +
+ +
+ +
+
+
+
+ + +
+
+
Current FAQs
+
+
+ +
+ +

No FAQs added yet.

+
+ +
+ + + + + + + + + + + + + + + + + + + +
SortQuestion (EN)Answer ExtractActions
+ + + +
+ + +
+
+
+ +
+
+
+
+ + \ No newline at end of file diff --git a/admin_integrations.php b/admin_integrations.php index dc1a4c2..f20a0a6 100644 --- a/admin_integrations.php +++ b/admin_integrations.php @@ -16,6 +16,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { 'wablas_domain' => trim($_POST['wablas_domain'] ?? ''), 'wablas_api_token' => trim($_POST['wablas_api_token'] ?? ''), 'wablas_secret_key' => trim($_POST['wablas_secret_key'] ?? ''), + 'smtp_host' => trim($_POST['smtp_host'] ?? ''), + 'smtp_port' => trim($_POST['smtp_port'] ?? ''), + 'smtp_secure' => trim($_POST['smtp_secure'] ?? ''), + 'smtp_user' => trim($_POST['smtp_user'] ?? ''), + 'smtp_pass' => trim($_POST['smtp_pass'] ?? ''), + 'mail_from' => trim($_POST['mail_from'] ?? ''), + 'mail_from_name' => trim($_POST['mail_from_name'] ?? ''), ]; if (empty($errors)) { @@ -35,6 +42,13 @@ $thawaniEnv = $settings['thawani_environment'] ?? 'test'; $wablasDomain = $settings['wablas_domain'] ?? ''; $wablasToken = $settings['wablas_api_token'] ?? ''; $wablasSecret = $settings['wablas_secret_key'] ?? ''; +$smtpHost = $settings['smtp_host'] ?? ''; +$smtpPort = $settings['smtp_port'] ?? '587'; +$smtpSecure = $settings['smtp_secure'] ?? 'tls'; +$smtpUser = $settings['smtp_user'] ?? ''; +$smtpPass = $settings['smtp_pass'] ?? ''; +$mailFrom = $settings['mail_from'] ?? ''; +$mailFromName = $settings['mail_from_name'] ?? ''; render_header('Integrations', 'admin'); ?> @@ -120,6 +134,58 @@ render_header('Integrations', 'admin'); + +
+

+ + + + SMTP Mail Settings +

+

Configure your SMTP server to send emails and system notifications.

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
diff --git a/db/migrations/add_bank_fields.php b/db/migrations/add_bank_fields.php new file mode 100644 index 0000000..b038ea3 --- /dev/null +++ b/db/migrations/add_bank_fields.php @@ -0,0 +1,12 @@ +exec("ALTER TABLE truck_owner_profiles ADD COLUMN bank_account VARCHAR(100) NULL AFTER plate_no;"); + $pdo->exec("ALTER TABLE truck_owner_profiles ADD COLUMN bank_name VARCHAR(100) NULL AFTER bank_account;"); + $pdo->exec("ALTER TABLE truck_owner_profiles ADD COLUMN bank_branch VARCHAR(100) NULL AFTER bank_name;"); + echo "Columns added.\n"; +} catch (Exception $e) { + echo "Error: " . $e->getMessage() . "\n"; +} + diff --git a/faq.php b/faq.php new file mode 100644 index 0000000..355791d --- /dev/null +++ b/faq.php @@ -0,0 +1,64 @@ +query("SHOW TABLES LIKE 'faqs'")->rowCount() > 0; + + if ($tableExists) { + $faqs = db()->query("SELECT * FROM faqs ORDER BY sort_order ASC, id DESC")->fetchAll(); + } else { + $faqs = []; + } +} catch (Throwable $e) { + $faqs = []; +} + +render_header('Frequently Asked Questions', 'home'); +?> + +
+
+
+

Frequently Asked Questions

+

Find answers to common questions about our platform and services.

+
+
+ +
+
+ +
+ +

No FAQs are currently available. Please check back later.

+
+ +
+ $faq): + $question = $lang === 'ar' && !empty($faq['question_ar']) ? $faq['question_ar'] : $faq['question_en']; + $answer = $lang === 'ar' && !empty($faq['answer_ar']) ? $faq['answer_ar'] : $faq['answer_en']; + $collapseId = 'collapse' . $faq['id']; + $headingId = 'heading' . $faq['id']; + ?> +
+

+ +

+
+
+ +
+
+
+ +
+ +
+
+
+ + \ No newline at end of file diff --git a/includes/app.php b/includes/app.php index ad1ec88..d811604 100644 --- a/includes/app.php +++ b/includes/app.php @@ -223,6 +223,9 @@ SQL; truck_type VARCHAR(120) NOT NULL, load_capacity DECIMAL(10,2) NOT NULL, plate_no VARCHAR(80) NOT NULL, + bank_account VARCHAR(100) NULL, + bank_name VARCHAR(100) NULL, + bank_branch VARCHAR(100) NULL, id_card_path TEXT NOT NULL, truck_pic_path VARCHAR(255) NOT NULL, registration_path TEXT NOT NULL, diff --git a/includes/layout.php b/includes/layout.php index 983bee1..8fad3c6 100644 --- a/includes/layout.php +++ b/includes/layout.php @@ -58,6 +58,11 @@ function render_header(string $title, string $active = ''): void +
+ + + Pages + + +
+ +
+ +
+
+

Have Questions?

+

Check out our Frequently Asked Questions to learn more about how our platform works.

+ + View FAQ + +
+
+

Ready to transform your logistics?

Join our platform today to find reliable trucks or secure the best shipments in the market.

diff --git a/mail/config.php b/mail/config.php index 626cca1..b0c0744 100644 --- a/mail/config.php +++ b/mail/config.php @@ -1,15 +1,12 @@ config array for MailService. +// Mail configuration sourced from environment variables, +// falling back to database settings if configured. function env_val(string $key, $default = null) { $v = getenv($key); return ($v === false || $v === null || $v === '') ? $default : $v; } -// Fallback: if critical vars are missing from process env, try to parse executor/.env -// This helps in web/Apache contexts where .env is not exported. -// Supports simple KEY=VALUE lines; ignores quotes and comments. function load_dotenv_if_needed(array $keys): void { $missing = array_filter($keys, fn($k) => getenv($k) === false || getenv($k) === ''); if (empty($missing)) return; @@ -22,9 +19,7 @@ function load_dotenv_if_needed(array $keys): void { if ($line[0] === '#' || trim($line) === '') continue; if (!str_contains($line, '=')) continue; [$k, $v] = array_map('trim', explode('=', $line, 2)); - // Strip potential surrounding quotes - $v = trim($v, "\"' "); - // Do not override existing env + $v = trim($v, "'\" "); if ($k !== '' && (getenv($k) === false || getenv($k) === '')) { putenv("{$k}={$v}"); } @@ -39,15 +34,43 @@ load_dotenv_if_needed([ 'DKIM_DOMAIN','DKIM_SELECTOR','DKIM_PRIVATE_KEY_PATH' ]); -$transport = env_val('MAIL_TRANSPORT', 'smtp'); -$smtp_host = env_val('SMTP_HOST'); -$smtp_port = (int) env_val('SMTP_PORT', 587); -$smtp_secure = env_val('SMTP_SECURE', 'tls'); // tls | ssl | null -$smtp_user = env_val('SMTP_USER'); -$smtp_pass = env_val('SMTP_PASS'); +// Attempt to load settings from DB safely +function get_mail_db_settings(): array { + $settings = []; + $dbPath = __DIR__ . '/../db/config.php'; + if (file_exists($dbPath)) { + require_once $dbPath; + if (function_exists('db')) { + try { + $stmt = db()->query("SELECT setting_key, setting_value FROM settings"); + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $settings[$row['setting_key']] = $row['setting_value']; + } + } catch (Throwable $e) { + // Ignore DB errors if not initialized + } + } + } + return $settings; +} -$from_email = env_val('MAIL_FROM', 'no-reply@localhost'); -$from_name = env_val('MAIL_FROM_NAME', 'App'); +$dbSettings = get_mail_db_settings(); + +$transport = env_val('MAIL_TRANSPORT', 'smtp'); + +$smtp_host = !empty($dbSettings['smtp_host']) ? $dbSettings['smtp_host'] : env_val('SMTP_HOST'); +$smtp_port = !empty($dbSettings['smtp_port']) ? (int)$dbSettings['smtp_port'] : (int) env_val('SMTP_PORT', 587); +$smtp_secure = isset($dbSettings['smtp_secure']) && $dbSettings['smtp_secure'] !== '' ? $dbSettings['smtp_secure'] : env_val('SMTP_SECURE', 'tls'); +if (isset($dbSettings['smtp_secure']) && $dbSettings['smtp_secure'] === 'none') { + $smtp_secure = ''; +} +if ($smtp_secure === 'none') $smtp_secure = ''; // Normalize None to empty for PHPMailer + +$smtp_user = !empty($dbSettings['smtp_user']) ? $dbSettings['smtp_user'] : env_val('SMTP_USER'); +$smtp_pass = !empty($dbSettings['smtp_pass']) ? $dbSettings['smtp_pass'] : env_val('SMTP_PASS'); + +$from_email = !empty($dbSettings['mail_from']) ? $dbSettings['mail_from'] : env_val('MAIL_FROM', 'no-reply@localhost'); +$from_name = !empty($dbSettings['mail_from_name']) ? $dbSettings['mail_from_name'] : env_val('MAIL_FROM_NAME', 'App'); $reply_to = env_val('MAIL_REPLY_TO'); $dkim_domain = env_val('DKIM_DOMAIN'); @@ -56,21 +79,15 @@ $dkim_private_key_path = env_val('DKIM_PRIVATE_KEY_PATH'); return [ 'transport' => $transport, - - // SMTP 'smtp_host' => $smtp_host, 'smtp_port' => $smtp_port, 'smtp_secure' => $smtp_secure, 'smtp_user' => $smtp_user, 'smtp_pass' => $smtp_pass, - - // From / Reply-To 'from_email' => $from_email, 'from_name' => $from_name, 'reply_to' => $reply_to, - - // DKIM (optional) 'dkim_domain' => $dkim_domain, 'dkim_selector' => $dkim_selector, 'dkim_private_key_path' => $dkim_private_key_path, -]; +]; \ No newline at end of file diff --git a/patch.py b/patch.py new file mode 100644 index 0000000..82ece00 --- /dev/null +++ b/patch.py @@ -0,0 +1,100 @@ +import re + +with open('register.php', 'r') as f: + content = f.read() + +# 1 +content = content.replace(" 'plate_no' => '',", " 'plate_no' => '',\n 'bank_account' => '',\n 'bank_name' => '',\n 'bank_branch' => '',") + +# 2 +content = content.replace(" 'plate_no' => trim($_POST['plate_no'] ?? ''),", " 'plate_no' => trim($_POST['plate_no'] ?? ''),\n 'bank_account' => trim($_POST['bank_account'] ?? ''),\n 'bank_name' => trim($_POST['bank_name'] ?? ''),\n 'bank_branch' => trim($_POST['bank_branch'] ?? ''),") + +# 3 +content = content.replace(" $plateNo = trim($_POST['plate_no'] ?? '');", " $plateNo = trim($_POST['plate_no'] ?? '');\n $bankAccount = trim($_POST['bank_account'] ?? '');\n $bankName = trim($_POST['bank_name'] ?? '');\n $bankBranch = trim($_POST['bank_branch'] ?? '');") + +# 4 +s4 = " if ($truckType === '' || $loadCapacity === '' || $plateNo === '') {\n $errors[] = 'Please complete truck details.';\n } elseif (!is_numeric($loadCapacity) || (float)$loadCapacity <= 0) {" +r4 = " if ($truckType === '' || $loadCapacity === '' || $plateNo === '') {\n $errors[] = 'Please complete truck details.';\n } elseif ($bankAccount === '' || $bankName === '' || $bankBranch === '') {\n $errors[] = 'Please complete bank account details.';\n } elseif (!is_numeric($loadCapacity) || (float)$loadCapacity <= 0) {" +content = content.replace(s4, r4) + +# 5 +s5 = " $ownerStmt = $pdo->prepare( + \"INSERT INTO truck_owner_profiles (user_id, phone, country_id, city_id, address_line, truck_type, load_capacity, plate_no, id_card_path, truck_pic_path, registration_path)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\" + ); + $ownerStmt->execute([ + $userId, + $phone, + $countryId, + $cityId, + $addressLine, + $truckType, + $loadCapacity, + $plateNo, + json_encode($idCardPaths, JSON_UNESCAPED_SLASHES), + $truckPic, + json_encode($regPaths, JSON_UNESCAPED_SLASHES), + ]);" +r5 = " $ownerStmt = $pdo->prepare( + \"INSERT INTO truck_owner_profiles (user_id, phone, country_id, city_id, address_line, truck_type, load_capacity, plate_no, bank_account, bank_name, bank_branch, id_card_path, truck_pic_path, registration_path)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\" + ); + $ownerStmt->execute([ + $userId, + $phone, + $countryId, + $cityId, + $addressLine, + $truckType, + $loadCapacity, + $plateNo, + $bankAccount, + $bankName, + $bankBranch, + json_encode($idCardPaths, JSON_UNESCAPED_SLASHES), + $truckPic, + json_encode($regPaths, JSON_UNESCAPED_SLASHES), + ]);" +content = content.replace(s5, r5) + +# 6 +s6 = " 'plate_no' => ''," +r6 = " 'plate_no' => '',\n 'bank_account' => '',\n 'bank_name' => '',\n 'bank_branch' => ''," +# careful not to replace the first one again +parts = content.split(s6) +if len(parts) == 3: + content = parts[0] + s6 + parts[1] + r6 + parts[2] +elif len(parts) == 2: + # meaning the first one was already replaced by s1/r1 (which is the same search string!) + # wait, s1 and s6 are exactly the same? + # No, s1 is " 'plate_no' => ''," and s6 is " 'plate_no' => ''," (more spaces) + content = parts[0] + r6 + parts[1] + + +# 7 +s7 = "
+ + \"> +
" +r7 = "
+ + \"> +
+ +

Bank Details for Payouts

+
+ + \" required> +
+
+ + \" required> +
+
+ + \" required> +
" +content = content.replace(s7, r7) + +with open('register.php', 'w') as f: + f.write(content) + +print("Replaced in register.php") diff --git a/tmp_edit.php b/tmp_edit.php new file mode 100644 index 0000000..db9d736 --- /dev/null +++ b/tmp_edit.php @@ -0,0 +1,16 @@ + '',"; +$r1 = " 'plate_no' => '',\n 'bank_account' => '',\n 'bank_name' => '',\n 'bank_branch' => '',"; + +$s2 = " 'plate_no' => trim(\" +$_POST['plate_no'] ?? ''\"),"; +$r2 = " 'plate_no' => trim(\" +$_POST['plate_no'] ?? ''\"),\n 'bank_account' => trim(\" +$_POST['bank_account'] ?? ''\"),\n 'bank_name' => trim(\" +$_POST['bank_name'] ?? ''\"),\n 'bank_branch' => trim(\" +$_POST['bank_branch'] ?? ''\"),"; + +$s3 = " $plateNo = trim(\" +$_POST['plate_no'] ?? ''\");"; +$r3 = " $plateNo = trim(\" +$_POST['plate_no'] ?? ''\");\n $bankAccount = trim(\" +$_POST['bank_account'] ?? ''\");\n $bankName = trim(\" +$_POST['bank_name'] ?? ''\");\n $bankBranch = trim(\" +$_POST['bank_branch'] ?? ''\");"; + +$s4 = " if ($truckType === '' || $loadCapacity === '' || $plateNo === '') {\n $errors[] = 'Please complete truck details.';\n } elseif (!is_numeric($loadCapacity) || (float)$loadCapacity <= 0) {"; +$r4 = " if ($truckType === '' || $loadCapacity === '' || $plateNo === '') {\n $errors[] = 'Please complete truck details.';\n } elseif ($bankAccount === '' || $bankName === '' || $bankBranch === '') {\n $errors[] = 'Please complete bank account details.';\n } elseif (!is_numeric($loadCapacity) || (float)$loadCapacity <= 0) {"; + +$s5 = <<prepare( + "INSERT INTO truck_owner_profiles (user_id, phone, country_id, city_id, address_line, truck_type, load_capacity, plate_no, id_card_path, truck_pic_path, registration_path)" + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + ); + $ownerStmt->execute([ + $userId, + $phone, + $countryId, + $cityId, + $addressLine, + $truckType, + $loadCapacity, + $plateNo, + json_encode($idCardPaths, JSON_UNESCAPED_SLASHES), + $truckPic, + json_encode($regPaths, JSON_UNESCAPED_SLASHES), + ]); +PHP; +$r5 = <<prepare( + "INSERT INTO truck_owner_profiles (user_id, phone, country_id, city_id, address_line, truck_type, load_capacity, plate_no, bank_account, bank_name, bank_branch, id_card_path, truck_pic_path, registration_path)" + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + ); + $ownerStmt->execute([ + $userId, + $phone, + $countryId, + $cityId, + $addressLine, + $truckType, + $loadCapacity, + $plateNo, + $bankAccount, + $bankName, + $bankBranch, + json_encode($idCardPaths, JSON_UNESCAPED_SLASHES), + $truckPic, + json_encode($regPaths, JSON_UNESCAPED_SLASHES), + ]); +PHP; + +$s7 = << + + +
+HTML; + +$r7 = << + + +
+ +

Bank Details for Payouts

+
+ + +
+
+ + +
+
+ + +
+HTML; + +$content = str_replace($s1, $r1, $content); +$content = str_replace($s2, $r2, $content); +$content = str_replace($s3, $r3, $content); +$content = str_replace($s4, $r4, $content); +$content = str_replace($s5, $r5, $content); +$content = str_replace($s7, $r7, $content); + +file_put_contents('register.php', $content); +echo "Replaced in register.php\n"; +