276 lines
13 KiB
PHP
276 lines
13 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../db/config.php';
|
|
|
|
if (session_status() === PHP_SESSION_NONE) {
|
|
session_start();
|
|
}
|
|
|
|
$lang = $_GET['lang'] ?? ($_SESSION['lang'] ?? 'en');
|
|
$lang = $lang === 'ar' ? 'ar' : 'en';
|
|
$_SESSION['lang'] = $lang;
|
|
$dir = $lang === 'ar' ? 'rtl' : 'ltr';
|
|
|
|
$translations = [
|
|
'en' => [
|
|
'app_name' => 'CargoLink',
|
|
'nav_home' => 'Overview',
|
|
'nav_shipper' => 'Shipper Desk',
|
|
'nav_owner' => 'Truck Owner Desk',
|
|
'nav_admin' => 'Admin Panel',
|
|
'hero_title' => 'Move cargo faster with verified trucks.',
|
|
'hero_subtitle' => 'Post shipments, collect offers, and pay via Thawani or bank transfer. Built for local and nearby cross-border moves.',
|
|
'hero_tagline' => 'Multilingual Logistics Marketplace',
|
|
'register_shipper' => 'Register as Shipper',
|
|
'register_owner' => 'Register as Truck Owner',
|
|
'cta_shipper' => 'Post a shipment',
|
|
'cta_owner' => 'Find loads',
|
|
'cta_admin' => 'Open admin',
|
|
'stats_shipments' => 'Shipments posted',
|
|
'stats_offers' => 'Active offers',
|
|
'stats_confirmed' => 'Confirmed trips',
|
|
'section_workflow' => 'How it works',
|
|
'recent_shipments' => 'Recent shipments',
|
|
'step_post' => 'Shipper posts cargo details and preferred payment.',
|
|
'step_offer' => 'Truck owners respond with their best rate.',
|
|
'step_confirm' => 'Admin confirms booking and status.',
|
|
'shipper_dashboard' => 'Shipper Dashboard',
|
|
'new_shipment' => 'Create shipment',
|
|
'shipper_name' => 'Shipper name',
|
|
'shipper_company' => 'Company',
|
|
'origin' => 'Origin city',
|
|
'destination' => 'Destination city',
|
|
'cargo' => 'Cargo description',
|
|
'weight' => 'Weight (tons)',
|
|
'pickup_date' => 'Pickup date',
|
|
'delivery_date' => 'Delivery date',
|
|
'payment_method' => 'Payment method',
|
|
'payment_thawani' => 'Thawani online payment',
|
|
'payment_bank' => 'Bank transfer',
|
|
'submit_shipment' => 'Submit shipment',
|
|
'shipments_list' => 'Your latest shipments',
|
|
'status' => 'Status',
|
|
'offer' => 'Best offer',
|
|
'actions' => 'Actions',
|
|
'view' => 'View',
|
|
'owner_dashboard' => 'Truck Owner Dashboard',
|
|
'available_shipments' => 'Available shipments',
|
|
'offer_price' => 'Offer price',
|
|
'offer_owner' => 'Truck owner name',
|
|
'submit_offer' => 'Send offer',
|
|
'admin_dashboard' => 'Admin Dashboard',
|
|
'update_status' => 'Update status',
|
|
'save' => 'Save',
|
|
'shipment_detail' => 'Shipment detail',
|
|
'created_at' => 'Created',
|
|
'best_offer' => 'Best offer',
|
|
'assign_owner' => 'Assigned owner',
|
|
'no_shipments' => 'No shipments yet. Create the first one to get started.',
|
|
'no_offers' => 'No offers yet.',
|
|
'success_shipment' => 'Shipment posted successfully.',
|
|
'success_offer' => 'Offer submitted to the shipper.',
|
|
'success_status' => 'Status updated.',
|
|
'error_required' => 'Please fill in all required fields.',
|
|
'error_invalid' => 'Please enter valid values.',
|
|
'status_posted' => 'Posted',
|
|
'status_offered' => 'Offered',
|
|
'status_confirmed' => 'Confirmed',
|
|
'status_in_transit' => 'In transit',
|
|
'status_delivered' => 'Delivered',
|
|
'footer_note' => 'This is the initial MVP slice. Payments are not yet connected.',
|
|
'marketing_title_1' => 'For Shippers', 'marketing_desc_1' => 'Find the right truck for your cargo quickly and securely. Post your load and get offers instantly.', 'marketing_title_2' => 'For Truck Owners', 'marketing_desc_2' => 'Maximize your earnings and eliminate empty miles. Browse available shipments and offer your rate.', 'motivation_phrase' => 'Empowering the logistics of tomorrow.', 'why_choose_us' => 'Why Choose CargoLink?', 'feature_1_title' => 'Fast Matching', 'feature_1_desc' => 'Connect with available trucks or shipments in minutes.', 'feature_2_title' => 'Secure Payments', 'feature_2_desc' => 'Your transactions are protected with security.', 'feature_3_title' => 'Verified Users', 'feature_3_desc' => 'We verify all truck owners to ensure peace of mind.',
|
|
],
|
|
'ar' => [
|
|
'app_name' => 'CargoLink',
|
|
'nav_home' => 'نظرة عامة',
|
|
'nav_shipper' => 'لوحة الشاحن',
|
|
'nav_owner' => 'لوحة مالك الشاحنة',
|
|
'nav_admin' => 'لوحة الإدارة',
|
|
'hero_title' => 'انقل شحنتك بسرعة مع شاحنات موثوقة.',
|
|
'hero_subtitle' => 'أنشئ شحنة، استلم عروضاً، وادفع عبر ثواني أو التحويل البنكي.',
|
|
'hero_tagline' => 'منصة لوجستية متعددة اللغات',
|
|
'register_shipper' => 'التسجيل كشاحن',
|
|
'register_owner' => 'التسجيل كمالك شاحنة',
|
|
'cta_shipper' => 'إنشاء شحنة',
|
|
'cta_owner' => 'البحث عن الشحنات',
|
|
'cta_admin' => 'الدخول للإدارة',
|
|
'stats_shipments' => 'الشحنات المنشورة',
|
|
'stats_offers' => 'العروض الحالية',
|
|
'stats_confirmed' => 'الرحلات المؤكدة',
|
|
'section_workflow' => 'طريقة العمل',
|
|
'recent_shipments' => 'أحدث الشحنات',
|
|
'step_post' => 'يقوم الشاحن بإدخال تفاصيل الشحنة وطريقة الدفع.',
|
|
'step_offer' => 'يرسل أصحاب الشاحنات أفضل عروضهم.',
|
|
'step_confirm' => 'تؤكد الإدارة الحجز وتحدث الحالة.',
|
|
'shipper_dashboard' => 'لوحة الشاحن',
|
|
'new_shipment' => 'إنشاء شحنة',
|
|
'shipper_name' => 'اسم الشاحن',
|
|
'shipper_company' => 'الشركة',
|
|
'origin' => 'مدينة الانطلاق',
|
|
'destination' => 'مدينة الوصول',
|
|
'cargo' => 'وصف الحمولة',
|
|
'weight' => 'الوزن (طن)',
|
|
'pickup_date' => 'تاريخ الاستلام',
|
|
'delivery_date' => 'تاريخ التسليم',
|
|
'payment_method' => 'طريقة الدفع',
|
|
'payment_thawani' => 'الدفع الإلكتروني عبر ثواني',
|
|
'payment_bank' => 'تحويل بنكي',
|
|
'submit_shipment' => 'إرسال الشحنة',
|
|
'shipments_list' => 'أحدث الشحنات',
|
|
'status' => 'الحالة',
|
|
'offer' => 'أفضل عرض',
|
|
'actions' => 'إجراءات',
|
|
'view' => 'عرض',
|
|
'owner_dashboard' => 'لوحة مالك الشاحنة',
|
|
'available_shipments' => 'الشحنات المتاحة',
|
|
'offer_price' => 'سعر العرض',
|
|
'offer_owner' => 'اسم مالك الشاحنة',
|
|
'submit_offer' => 'إرسال العرض',
|
|
'admin_dashboard' => 'لوحة الإدارة',
|
|
'update_status' => 'تحديث الحالة',
|
|
'save' => 'حفظ',
|
|
'shipment_detail' => 'تفاصيل الشحنة',
|
|
'created_at' => 'تم الإنشاء',
|
|
'best_offer' => 'أفضل عرض',
|
|
'assign_owner' => 'المالك المعتمد',
|
|
'no_shipments' => 'لا توجد شحنات بعد. ابدأ بإنشاء أول شحنة.',
|
|
'no_offers' => 'لا توجد عروض بعد.',
|
|
'success_shipment' => 'تم نشر الشحنة بنجاح.',
|
|
'success_offer' => 'تم إرسال العرض إلى الشاحن.',
|
|
'success_status' => 'تم تحديث الحالة.',
|
|
'error_required' => 'يرجى تعبئة جميع الحقول المطلوبة.',
|
|
'error_invalid' => 'يرجى إدخال قيم صحيحة.',
|
|
'status_posted' => 'منشورة',
|
|
'status_offered' => 'بعرض',
|
|
'status_confirmed' => 'مؤكدة',
|
|
'status_in_transit' => 'قيد النقل',
|
|
'status_delivered' => 'تم التسليم',
|
|
'footer_note' => 'هذه هي النسخة الأولية. الدفع غير متصل بعد.',
|
|
'marketing_title_1' => 'للشاحنين', 'marketing_desc_1' => 'ابحث عن الشاحنة المناسبة لحمولتك بسرعة وأمان.', 'marketing_title_2' => 'لأصحاب الشاحنات', 'marketing_desc_2' => 'عظّم أرباحك وتجنب العودة فارغاً.', 'motivation_phrase' => 'تمكين الخدمات اللوجستية للمستقبل.', 'why_choose_us' => 'لماذا تختار كارجو لينك؟', 'feature_1_title' => 'مطابقة سريعة', 'feature_1_desc' => 'تواصل مع الشاحنات المتاحة في دقائق.', 'feature_2_title' => 'مدفوعات آمنة', 'feature_2_desc' => 'معاملاتك محمية بأعلى معايير الأمان.', 'feature_3_title' => 'مستخدمون موثوقون', 'feature_3_desc' => 'نقوم بالتحقق من جميع أصحاب الشاحنات لضمان راحتك.',
|
|
],
|
|
];
|
|
|
|
function t(string $key): string
|
|
{
|
|
global $translations, $lang;
|
|
return $translations[$lang][$key] ?? $key;
|
|
}
|
|
|
|
function e($value): string
|
|
{
|
|
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
|
|
}
|
|
|
|
function ensure_schema(): void
|
|
{
|
|
$sql = <<<SQL
|
|
CREATE TABLE IF NOT EXISTS shipments (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
shipper_name VARCHAR(120) NOT NULL,
|
|
shipper_company VARCHAR(120) NOT NULL,
|
|
origin_city VARCHAR(120) NOT NULL,
|
|
destination_city VARCHAR(120) NOT NULL,
|
|
cargo_description VARCHAR(255) NOT NULL,
|
|
weight_tons DECIMAL(10,2) NOT NULL,
|
|
pickup_date DATE NOT NULL,
|
|
delivery_date DATE NOT NULL,
|
|
payment_method ENUM('thawani','bank_transfer') NOT NULL DEFAULT 'thawani',
|
|
status ENUM('posted','offered','confirmed','in_transit','delivered') NOT NULL DEFAULT 'posted',
|
|
offer_price DECIMAL(10,2) DEFAULT NULL,
|
|
offer_owner VARCHAR(120) DEFAULT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
SQL;
|
|
db()->exec($sql);
|
|
try { db()->exec("ALTER TABLE users ADD COLUMN status ENUM('pending','active','rejected') NOT NULL DEFAULT 'active'"); } catch (Exception $e) {}
|
|
|
|
|
|
db()->exec(
|
|
"CREATE TABLE IF NOT EXISTS users (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
email VARCHAR(255) NOT NULL UNIQUE,
|
|
password VARCHAR(255) NOT NULL,
|
|
full_name VARCHAR(255) NOT NULL,
|
|
role ENUM('admin','shipper','truck_owner') NOT NULL,
|
|
status ENUM('pending','active','rejected') NOT NULL DEFAULT 'active',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
|
|
);
|
|
|
|
db()->exec(
|
|
"CREATE TABLE IF NOT EXISTS shipper_profiles (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
user_id INT NOT NULL UNIQUE,
|
|
company_name VARCHAR(255) NOT NULL,
|
|
phone VARCHAR(40) NOT NULL,
|
|
country_id INT NULL,
|
|
city_id INT NULL,
|
|
address_line VARCHAR(255) NOT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT fk_shipper_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
|
|
);
|
|
|
|
db()->exec(
|
|
"CREATE TABLE IF NOT EXISTS truck_owner_profiles (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
user_id INT NOT NULL UNIQUE,
|
|
phone VARCHAR(40) NOT NULL,
|
|
country_id INT NULL,
|
|
city_id INT NULL,
|
|
address_line VARCHAR(255) NOT NULL,
|
|
truck_type VARCHAR(120) NOT NULL,
|
|
load_capacity DECIMAL(10,2) NOT NULL,
|
|
plate_no VARCHAR(80) NOT NULL,
|
|
id_card_path TEXT NOT NULL,
|
|
truck_pic_path VARCHAR(255) NOT NULL,
|
|
registration_path TEXT NOT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT fk_owner_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
|
|
);
|
|
}
|
|
|
|
function set_flash(string $type, string $message): void
|
|
{
|
|
$_SESSION['flash'] = ['type' => $type, 'message' => $message];
|
|
}
|
|
|
|
function get_flash(): ?array
|
|
{
|
|
if (!empty($_SESSION['flash'])) {
|
|
$flash = $_SESSION['flash'];
|
|
unset($_SESSION['flash']);
|
|
return $flash;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function url_with_lang(string $path, array $params = []): string
|
|
{
|
|
global $lang;
|
|
$params = array_merge(['lang' => $lang], $params);
|
|
return $path . '?' . http_build_query($params);
|
|
}
|
|
|
|
function current_url_with_lang(string $newLang): string
|
|
{
|
|
$params = $_GET;
|
|
$params['lang'] = $newLang;
|
|
$path = basename($_SERVER['PHP_SELF'] ?? 'index.php');
|
|
return $path . '?' . http_build_query($params);
|
|
}
|
|
|
|
function status_label(string $status): string
|
|
{
|
|
$map = [
|
|
'posted' => t('status_posted'),
|
|
'offered' => t('status_offered'),
|
|
'confirmed' => t('status_confirmed'),
|
|
'in_transit' => t('status_in_transit'),
|
|
'delivered' => t('status_delivered'),
|
|
];
|
|
return $map[$status] ?? $status;
|
|
}
|