update landing page

This commit is contained in:
Flatlogic Bot 2026-04-07 16:39:22 +00:00
parent f1eb13ccae
commit 15a5b584d5
6 changed files with 371 additions and 26 deletions

View File

@ -133,6 +133,13 @@ render_head(
<?= h(t('Plans', 'الخطط')) ?>
</a>
</li>
<?php if (get_logged_in_user()['role'] === 'admin'): ?>
<li>
<a href="<?= h(app_url('admin.php', ['page' => 'users'])) ?>" class="nav-link <?= $page === 'users' ? 'active' : 'link-dark' ?>" <?= $page === 'users' ? 'style="background-color: var(--accent); color: white;"' : '' ?> >
<?= h(t('Platform Users', 'مستخدمي المنصة')) ?>
</a>
</li>
<?php endif; ?>
<li>
<a href="<?= h(app_url('admin.php', ['page' => 'students'])) ?>" class="nav-link <?= $page === 'students' ? 'active' : 'link-dark' ?>" <?= $page === 'students' ? 'style="background-color: var(--accent); color: white;"' : '' ?>>
<?= h(t('Students', 'الطلاب')) ?>
@ -326,6 +333,8 @@ render_head(
<?php require_once __DIR__ . '/admin_classes.php'; ?>
<?php elseif ($page === 'subjects'): ?>
<?php require_once __DIR__ . '/admin_subjects.php'; ?>
<?php elseif ($page === 'users'): ?>
<?php require_once __DIR__ . '/admin_users.php'; ?>
<?php elseif ($page === 'students'): ?>
<?php require_once __DIR__ . '/admin_students.php'; ?>
<?php elseif ($page === 'teachers'): ?>

281
admin_users.php Normal file
View File

@ -0,0 +1,281 @@
<?php
// admin_users.php
require_once __DIR__ . '/includes/app.php';
$current_u = get_logged_in_user();
if ($current_u['role'] !== 'admin') {
die('Access Denied');
}
$action = $_GET['action'] ?? 'list';
$id = (int)($_GET['id'] ?? 0);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$post_action = $_POST['action'] ?? $action;
$post_id = (int)($_POST['id'] ?? $id);
if ($post_action === 'delete' && $post_id > 0) {
// Prevent deleting oneself
if ($post_id !== (int)$_SESSION['user_id']) {
$stmt = db()->prepare("DELETE FROM users WHERE id = ?");
$stmt->execute([$post_id]);
}
header('Location: ' . app_url('admin.php', ['page' => 'users']));
exit;
}
if ($post_action === 'edit' || $post_action === 'add') {
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$phone = trim($_POST['phone'] ?? '');
$role = $_POST['role'] ?? 'user';
if (!in_array($role, ['admin', 'user'])) {
$role = 'user';
}
$raw_password = $_POST['password'] ?? '';
$profile_picture = '';
$existing_password = '';
if ($post_action === 'edit' && $post_id > 0) {
$stmt = db()->prepare("SELECT profile_picture, password FROM users WHERE id = ?");
$stmt->execute([$post_id]);
$existing = $stmt->fetch(PDO::FETCH_ASSOC);
if ($existing) {
$profile_picture = $existing['profile_picture'];
$existing_password = $existing['password'];
}
}
$upload_dir = __DIR__ . '/assets/images/uploads/';
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0777, true);
}
if (!empty($_FILES['photo']['tmp_name'])) {
$filename = 'user_' . time() . '_' . basename($_FILES['photo']['name']);
$target = $upload_dir . $filename;
if (move_uploaded_file($_FILES['photo']['tmp_name'], $target)) {
$profile_picture = 'assets/images/uploads/' . $filename;
}
}
if ($post_action === 'add') {
$final_password = $raw_password ? password_hash($raw_password, PASSWORD_DEFAULT) : '';
} else {
if ($raw_password) {
$final_password = password_hash($raw_password, PASSWORD_DEFAULT);
} else {
$final_password = $existing_password;
}
}
if ($post_action === 'edit' && $post_id > 0) {
$stmt = db()->prepare("UPDATE users SET name=?, email=?, phone=?, role=?, profile_picture=?, password=? WHERE id=?");
$stmt->execute([$name, $email, $phone, $role, $profile_picture, $final_password, $post_id]);
} else {
$stmt = db()->prepare("INSERT INTO users (name, email, phone, role, profile_picture, password) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$name, $email, $phone, $role, $profile_picture, $final_password]);
}
header('Location: ' . app_url('admin.php', ['page' => 'users']));
exit;
}
}
$search = $_GET['search'] ?? '';
$page_num = max(1, (int)($_GET['p'] ?? 1));
$limit = 10;
$offset = ($page_num - 1) * $limit;
$where = "1=1";
$params = [];
if ($search) {
$where .= " AND (name LIKE ? OR email LIKE ?)";
$params[] = "%$search%";
$params[] = "%$search%";
}
$stmt = db()->prepare("SELECT COUNT(*) FROM users WHERE $where");
$stmt->execute($params);
$total = $stmt->fetchColumn();
$total_pages = ceil($total / $limit);
$stmt = db()->prepare("SELECT * FROM users WHERE $where ORDER BY id DESC LIMIT ? OFFSET ?");
$params[] = $limit;
$params[] = $offset;
foreach($params as $k => $v) {
$stmt->bindValue($k+1, $v, is_int($v) ? PDO::PARAM_INT : PDO::PARAM_STR);
}
$stmt->execute();
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<div class="section-header mb-4 d-flex justify-content-between align-items-center">
<div>
<h1 class="section-title mb-2"><?= h(t('Platform Users', 'مستخدمي المنصة')) ?></h1>
<p class="text-secondary mb-0"><?= h(t('Manage administrators and staff.', 'إدارة المسؤولين والموظفين.')) ?></p>
</div>
<button class="btn btn-dark" data-bs-toggle="modal" data-bs-target="#userModal" onclick="editUser(null)">
<?= h(t('Add User', 'إضافة مستخدم')) ?>
</button>
</div>
<div class="panel-card mb-4">
<form method="GET" action="admin.php" class="d-flex gap-2">
<input type="hidden" name="page" value="users">
<input type="text" name="search" class="form-control" placeholder="<?= h(t('Search...', 'بحث...')) ?>" value="<?= h($search) ?>">
<button type="submit" class="btn btn-outline-dark"><?= h(t('Search', 'بحث')) ?></button>
<?php if ($search): ?>
<a href="<?= h(app_url('admin.php', ['page' => 'users'])) ?>" class="btn btn-link text-secondary"><?= h(t('Clear', 'مسح')) ?></a>
<?php endif; ?>
</form>
</div>
<div class="panel-card p-0 overflow-hidden">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="bg-light">
<tr>
<th class="px-4 py-3"><?= h(t('Name', 'الاسم')) ?></th>
<th class="py-3"><?= h(t('Email', 'البريد الإلكتروني')) ?></th>
<th class="py-3"><?= h(t('Phone', 'الهاتف')) ?></th>
<th class="py-3"><?= h(t('Role', 'الدور')) ?></th>
<th class="px-4 py-3 text-end"><?= h(t('Actions', 'الإجراءات')) ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($users as $u): ?>
<tr>
<td class="px-4 py-3">
<div class="d-flex align-items-center gap-3">
<?php if ($u['profile_picture']): ?>
<img src="<?= h(asset_url($u['profile_picture'])) ?>" class="rounded-circle" style="width:40px;height:40px;object-fit:cover;">
<?php else: ?>
<div class="bg-light rounded-circle d-flex align-items-center justify-content-center text-secondary fw-bold" style="width:40px;height:40px;">
<?= h(strtoupper(substr($u['name'], 0, 1))) ?>
</div>
<?php endif; ?>
<div>
<div class="fw-bold text-dark"><?= h($u['name']) ?></div>
</div>
</div>
</td>
<td class="py-3"><?= h($u['email']) ?></td>
<td class="py-3"><?= h($u['phone']) ?></td>
<td class="py-3">
<?php if ($u['role'] === 'admin'): ?>
<span class="badge bg-primary text-white"><?= h(t('Admin', 'مسؤول')) ?></span>
<?php else: ?>
<span class="badge bg-secondary text-white"><?= h(t('User', 'مستخدم')) ?></span>
<?php endif; ?>
</td>
<td class="px-4 py-3 text-end">
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-dark" onclick='editUser(<?= htmlspecialchars(json_encode($u), ENT_QUOTES) ?>)'>
<?= h(t('Edit', 'تعديل')) ?>
</button>
<?php if ((int)$u['id'] !== (int)$_SESSION['user_id']): ?>
<form method="POST" action="admin_users.php" class="d-inline" onsubmit="return confirm('<?= h(t('Delete this user?', 'حذف هذا المستخدم؟')) ?>');">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="<?= h($u['id']) ?>">
<button class="btn btn-outline-danger" type="submit">
<?= h(t('Delete', 'حذف')) ?>
</button>
</form>
<?php endif; ?>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($users)): ?>
<tr><td colspan="5" class="text-center py-4 text-secondary"><?= h(t('No users found.', 'لا يوجد مستخدمين.')) ?></td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<?php if ($total_pages > 1): ?>
<nav class="mt-4">
<ul class="pagination justify-content-center">
<?php for($i=1; $i<=$total_pages; $i++): ?>
<li class="page-item <?= $i === $page_num ? 'active' : '' ?>">
<a class="page-link" href="<?= h(app_url('admin.php', ['page' => 'users', 'p' => $i, 'search' => $search])) ?>"><?= $i ?></a>
</li>
<?php endfor; ?>
</ul>
</nav>
<?php endif; ?>
<!-- User Modal -->
<div class="modal fade" id="userModal" tabindex="-1">
<div class="modal-dialog">
<form class="modal-content" method="POST" action="admin_users.php" enctype="multipart/form-data">
<input type="hidden" name="action" id="userAction" value="add">
<input type="hidden" name="id" id="userId" value="0">
<div class="modal-header">
<h5 class="modal-title" id="userModalTitle"><?= h(t('Add User', 'إضافة مستخدم')) ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label"><?= h(t('Name', 'الاسم')) ?></label>
<input type="text" class="form-control" name="name" id="userName" required>
</div>
<div class="mb-3">
<label class="form-label"><?= h(t('Email', 'البريد الإلكتروني')) ?></label>
<input type="email" class="form-control" name="email" id="userEmail" required>
</div>
<div class="mb-3">
<label class="form-label"><?= h(t('Phone', 'الهاتف')) ?></label>
<input type="text" class="form-control" name="phone" id="userPhone">
</div>
<div class="mb-3">
<label class="form-label"><?= h(t('Role', 'الدور')) ?></label>
<select class="form-select" name="role" id="userRole">
<option value="user"><?= h(t('User', 'مستخدم')) ?></option>
<option value="admin"><?= h(t('Admin', 'مسؤول')) ?></option>
</select>
</div>
<div class="mb-3">
<label class="form-label"><?= h(t('Password', 'كلمة المرور')) ?> <small class="text-secondary" id="userPasswordHelp" style="display:none;">(<?= h(t('Leave blank to keep current', 'اتركه فارغاً للاحتفاظ بالحالي')) ?>)</small></label>
<input type="password" class="form-control" name="password" id="userPassword">
</div>
<div class="mb-3">
<label class="form-label"><?= h(t('Profile Picture', 'صورة الملف الشخصي')) ?></label>
<input type="file" class="form-control" name="photo" accept="image/*">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal"><?= h(t('Cancel', 'إلغاء')) ?></button>
<button type="submit" class="btn btn-dark"><?= h(t('Save', 'حفظ')) ?></button>
</div>
</form>
</div>
</div>
<script>
function editUser(user) {
if (user) {
document.getElementById('userAction').value = 'edit';
document.getElementById('userId').value = user.id;
document.getElementById('userModalTitle').innerText = '<?= h(t('Edit User', 'تعديل المستخدم')) ?>';
document.getElementById('userName').value = user.name;
document.getElementById('userEmail').value = user.email;
document.getElementById('userPhone').value = user.phone || '';
document.getElementById('userRole').value = user.role;
document.getElementById('userPassword').required = false;
document.getElementById('userPasswordHelp').style.display = 'inline';
} else {
document.getElementById('userAction').value = 'add';
document.getElementById('userId').value = '0';
document.getElementById('userModalTitle').innerText = '<?= h(t('Add User', 'إضافة مستخدم')) ?>';
document.getElementById('userName').value = '';
document.getElementById('userEmail').value = '';
document.getElementById('userPhone').value = '';
document.getElementById('userRole').value = 'user';
document.getElementById('userPassword').required = true;
document.getElementById('userPasswordHelp').style.display = 'none';
}
}
</script>

View File

@ -467,3 +467,54 @@ footer {
.modal-header.bg-dark-blue .section-title {
color: #ffffff !important;
}
/* Modern Additions */
.hero-section {
background: linear-gradient(135deg, #f8f9fc 0%, #e2e8f0 100%);
position: relative;
overflow: hidden;
}
.hero-section::before {
content: "";
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(37,99,235,0.06) 0%, rgba(255,255,255,0) 70%);
z-index: 0;
}
.hero-section > .container {
position: relative;
z-index: 1;
}
.hero-section {
border-bottom: 0 !important;
}
.plan-card, .subject-card, .workflow-card, .card {
transition: transform 0.2s ease, box-shadow 0.2s ease;
border: 1px solid rgba(0,0,0,0.06) !important;
background: rgba(255, 255, 255, 0.9);
}
.plan-card:hover, .subject-card:hover, .workflow-card:hover, .card:hover {
transform: translateY(-6px);
box-shadow: 0 20px 40px rgba(0,0,0,0.08) !important;
}
.metric-card {
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(10px);
border: 1px solid rgba(255,255,255,0.8);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.metric-card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(0,0,0,0.05);
}
.btn {
transition: all 0.2s ease;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
}

View File

@ -652,12 +652,32 @@ function render_nav(string $active = ''): void
<a class="btn btn-outline-dark <?= current_lang() === 'en' ? 'active' : '' ?>" href="<?= h(page_lang_link('en')) ?>">EN</a>
<a class="btn btn-outline-dark <?= current_lang() === 'ar' ? 'active' : '' ?>" href="<?= h(page_lang_link('ar')) ?>">AR</a>
</div>
<div class="d-flex gap-2 ms-2">
<div class="d-flex gap-2 ms-2 align-items-center">
<?php if (!empty($_SESSION['user_id'])): ?>
<a href="<?= h(app_url('profile.php')) ?>" class="btn btn-outline-primary btn-sm px-3"><?= h(t('Profile', 'حسابي')) ?></a>
<a href="<?= h(app_url('logout.php')) ?>" class="btn btn-outline-danger btn-sm px-3"><?= h(t('Logout', 'خروج')) ?></a>
<?php
$stmt = db()->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
?>
<div class="dropdown">
<a class="btn btn-light dropdown-toggle d-flex align-items-center gap-2 border-0 rounded-pill px-2 py-1 shadow-sm" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false" style="background: rgba(255,255,255,0.85); backdrop-filter: blur(10px);">
<?php if (!empty($user['profile_picture'])): ?>
<img src="<?= h(asset_url($user['profile_picture'])) ?>" alt="Profile" class="rounded-circle shadow-sm" width="32" height="32" style="object-fit: cover;">
<?php else: ?>
<div class="rounded-circle bg-primary text-white d-flex justify-content-center align-items-center shadow-sm" style="width:32px; height:32px; font-size:14px; font-weight:600;">
<?= h(strtoupper(mb_substr($user['name'] ?? 'U', 0, 1, 'UTF-8'))) ?>
</div>
<?php endif; ?>
<span class="d-none d-lg-inline small fw-bold text-dark px-1"><?= h($user['name'] ?? 'User') ?></span>
</a>
<ul class="dropdown-menu dropdown-menu-end shadow border-0 mt-2" style="border-radius: 12px; min-width: 180px;">
<li><a class="dropdown-item py-2 fw-medium" href="<?= h(app_url('profile.php')) ?>"><?= h(t('My Profile', 'حسابي')) ?></a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item py-2 text-danger fw-medium" href="<?= h(app_url('logout.php')) ?>"><?= h(t('Logout', 'تسجيل خروج')) ?></a></li>
</ul>
</div>
<?php else: ?>
<a href="<?= h(app_url('login.php')) ?>" class="btn btn-outline-dark btn-sm px-3"><?= h(t('Login', 'دخول')) ?></a>
<a href="<?= h(app_url('login.php')) ?>" class="btn btn-dark btn-sm px-4 rounded-pill shadow-sm fw-bold"><?= h(t('Login', 'تسجيل الدخول')) ?></a>
<?php endif; ?>
</div>
</div>

View File

@ -20,8 +20,8 @@ $metrics = ['subjects' => count($subjects), 'teachers' => db()->query("SELECT CO
<h1 class="display-title mt-3 mb-3"><?= h(landing_setting('hero_title', 'Subscriptions, multilingual classrooms, and live Google Meet learning in one precise workspace.', 'الاشتراكات والفصول متعددة اللغات والتعلم المباشر عبر Google Meet في مساحة واحدة دقيقة.')) ?></h1>
<p class="lead text-secondary mb-4"><?= h(landing_setting('hero_desc', 'Launch a polished e-learning experience for students, teachers, and admins with English/Arabic support, Thawani billing flows, and Wablas-ready WhatsApp notifications.', 'أطلق تجربة تعليم إلكتروني مصقولة للطلاب والمعلمين والإدارة مع دعم الإنجليزية والعربية وتدفقات دفع ثواني وإشعارات واتساب جاهزة عبر وابلاس.')) ?></p>
<div class="d-flex flex-wrap gap-2 mb-4">
<a class="btn btn-dark btn-lg" href="<?= h(app_url('pricing.php')) ?>"><?= h(t('Start subscription flow', 'ابدأ مسار الاشتراك')) ?></a>
<a class="btn btn-outline-dark btn-lg" href="<?= h(app_url('catalog.php')) ?>"><?= h(t('Browse subjects', 'تصفح المواد')) ?></a>
<a class="btn btn-primary btn-lg rounded-pill px-4 shadow-sm" href="<?= h(app_url('pricing.php')) ?>"><?= h(t('Start subscription flow', 'ابدأ مسار الاشتراك')) ?></a>
<a class="btn btn-light btn-lg rounded-pill px-4 shadow-sm text-primary fw-bold" href="<?= h(app_url('catalog.php')) ?>"><?= h(t('Browse subjects', 'تصفح المواد')) ?></a>
</div>
<div class="row row-cols-1 row-cols-sm-3 g-3 small-stat-grid">
<div class="col">
@ -37,7 +37,7 @@ $metrics = ['subjects' => count($subjects), 'teachers' => db()->query("SELECT CO
</div>
<div class="col-lg-5">
<?php if ($hero_img = landing_setting('hero_image', '')): ?>
<img src="<?= h($hero_img) ?>" class="img-fluid rounded shadow-lg w-100" style="object-fit: cover; max-height: 500px;" alt="Hero Image">
<img src="<?= h($hero_img) ?>" class="img-fluid rounded-4 shadow-lg w-100" style="object-fit: cover; max-height: 500px;" alt="Hero Image">
<?php else: ?>
<?php endif; ?>
</div>
@ -78,7 +78,7 @@ $metrics = ['subjects' => count($subjects), 'teachers' => db()->query("SELECT CO
<div class="card-body d-flex flex-column p-4">
<div class="d-flex justify-content-between align-items-start mb-3">
<h3 class="h5 mb-0 fw-bold"><?= h(current_lang() === 'ar' ? $course['name_ar'] : $course['name_en']) ?></h3>
<span class="badge bg-primary text-white rounded-pill fs-6 px-3 py-2"><?= h(format_price((float)$course['price'])) ?></span>
<span class="badge bg-primary bg-gradient text-white rounded-pill fs-6 px-3 py-2 shadow-sm"><?= h(format_price((float)$course['price'])) ?></span>
</div>
<p class="text-secondary mb-4 flex-grow-1"><?= h(current_lang() === 'ar' ? $course['description_ar'] : $course['description_en']) ?></p>
<div class="d-grid mt-auto">
@ -95,7 +95,7 @@ $metrics = ['subjects' => count($subjects), 'teachers' => db()->query("SELECT CO
?>
<button class="btn btn-secondary" disabled><?= h($isClosed ? t('Registration Closed', 'التسجيل مغلق') : t('Class Full', 'مكتمل العدد')) ?></button>
<?php else: ?>
<a class="btn btn-dark" href="<?= h(app_url('checkout.php', ['course_id' => $course['id']])) ?>"><?= h(t('Enroll Now', 'سجل الآن')) ?></a>
<a class="btn btn-primary rounded-pill px-3 shadow-sm" href="<?= h(app_url('checkout.php', ['course_id' => $course['id']])) ?>"><?= h(t('Enroll Now', 'سجل الآن')) ?></a>
<?php endif; ?>
</div>
</div>
@ -212,7 +212,7 @@ $metrics = ['subjects' => count($subjects), 'teachers' => db()->query("SELECT CO
<li><?= h($feature) ?></li>
<?php endforeach; ?>
</ul>
<a class="btn <?= $plan['key'] === 'plus' ? 'btn-dark' : 'btn-outline-dark' ?> w-100" href="<?= h(app_url('checkout.php', ['plan' => $plan['key']])) ?>"><?= h(t('Choose plan', 'اختر الخطة')) ?></a>
<a class="btn <?= $plan['key'] === 'plus' ? 'btn-primary shadow-sm' : 'btn-outline-primary bg-white' ?> w-100 rounded-pill fw-bold" href="<?= h(app_url('checkout.php', ['plan' => $plan['key']])) ?>"><?= h(t('Choose plan', 'اختر الخطة')) ?></a>
</div>
</div>
<?php endforeach; ?>

View File

@ -1,16 +0,0 @@
<?php
$content = file_get_contents('includes/app.php');
$nav_old = '<a class="navbar-brand fw-semibold" href="<?= h(app_url(\'index.php\')) ?>"><?= h(app_name()) ?></a>';
$nav_new = <<<'EOD'
<a class="navbar-brand fw-semibold d-flex align-items-center" href="<?= h(app_url('index.php')) ?>">
<?php $prof = get_platform_profile(); if (!empty($prof['logo_path'])): ?>
<img src="<?= h(asset_url($prof['logo_path'])) ?>" alt="Logo" style="height: 32px; margin-right: 8px; border-radius: 4px;">
<?php endif; ?>
<span><?= h(app_name()) ?></span>
</a>
EOD;
$content = str_replace($nav_old, $nav_new, $content);
file_put_contents('includes/app.php', $content);
echo "Patched navbar\n";