Autosave: 20260227-151712
This commit is contained in:
parent
674dcf8e61
commit
5d112ecb1b
52
db/migrations/001_initial_schema.sql
Normal file
52
db/migrations/001_initial_schema.sql
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
-- Migration: Initial Schema
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
username VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
password VARCHAR(255) NOT NULL,
|
||||||
|
full_name VARCHAR(100),
|
||||||
|
role ENUM('admin', 'clerk', 'staff') DEFAULT 'staff',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS mailbox (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
type ENUM('inbound', 'outbound') NOT NULL,
|
||||||
|
ref_no VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
date_registered DATE NOT NULL,
|
||||||
|
sender VARCHAR(255),
|
||||||
|
recipient VARCHAR(255),
|
||||||
|
subject VARCHAR(255) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
status ENUM('received', 'in_progress', 'closed') DEFAULT 'received',
|
||||||
|
assigned_to INT,
|
||||||
|
created_by INT,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (assigned_to) REFERENCES users(id) ON DELETE SET NULL,
|
||||||
|
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS attachments (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
mail_id INT NOT NULL,
|
||||||
|
file_path VARCHAR(255) NOT NULL,
|
||||||
|
file_name VARCHAR(255) NOT NULL,
|
||||||
|
file_size INT,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (mail_id) REFERENCES mailbox(id) ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS comments (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
mail_id INT NOT NULL,
|
||||||
|
user_id INT,
|
||||||
|
comment TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (mail_id) REFERENCES mailbox(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- Initial Admin User (password: admin123)
|
||||||
|
INSERT INTO users (username, password, full_name, role)
|
||||||
|
VALUES ('admin', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'مدير النظام', 'admin')
|
||||||
|
ON DUPLICATE KEY UPDATE username=username;
|
||||||
21
db/migrations/migrate.php
Normal file
21
db/migrations/migrate.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../config.php';
|
||||||
|
|
||||||
|
function runMigrations() {
|
||||||
|
$pdo = db();
|
||||||
|
$migrationsDir = __DIR__;
|
||||||
|
$files = glob($migrationsDir . '/*.sql');
|
||||||
|
sort($files);
|
||||||
|
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$sql = file_get_contents($file);
|
||||||
|
try {
|
||||||
|
$pdo->exec($sql);
|
||||||
|
echo "Successfully applied migration: " . basename($file) . PHP_EOL;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo "Error applying migration " . basename($file) . ": " . $e->getMessage() . PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runMigrations();
|
||||||
301
inbound.php
Normal file
301
inbound.php
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/includes/header.php';
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
$success = '';
|
||||||
|
|
||||||
|
// Handle actions
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$action = $_POST['action'] ?? '';
|
||||||
|
$type = 'inbound';
|
||||||
|
$ref_no = $_POST['ref_no'] ?? '';
|
||||||
|
$date_registered = $_POST['date_registered'] ?? date('Y-m-d');
|
||||||
|
$sender = $_POST['sender'] ?? '';
|
||||||
|
$recipient = $_POST['recipient'] ?? '';
|
||||||
|
$subject = $_POST['subject'] ?? '';
|
||||||
|
$description = $_POST['description'] ?? '';
|
||||||
|
$status = $_POST['status'] ?? 'received';
|
||||||
|
$assigned_to = !empty($_POST['assigned_to']) ? $_POST['assigned_to'] : null;
|
||||||
|
$id = $_POST['id'] ?? 0;
|
||||||
|
|
||||||
|
if ($ref_no && $subject) {
|
||||||
|
try {
|
||||||
|
if ($action === 'add') {
|
||||||
|
$stmt = db()->prepare("INSERT INTO mailbox (type, ref_no, date_registered, sender, recipient, subject, description, status, assigned_to, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
|
$stmt->execute([$type, $ref_no, $date_registered, $sender, $recipient, $subject, $description, $status, $assigned_to, $_SESSION['user_id']]);
|
||||||
|
$success = 'تمت إضافة البريد بنجاح';
|
||||||
|
} elseif ($action === 'edit') {
|
||||||
|
$stmt = db()->prepare("UPDATE mailbox SET ref_no = ?, date_registered = ?, sender = ?, recipient = ?, subject = ?, description = ?, status = ?, assigned_to = ? WHERE id = ? AND type = 'inbound'");
|
||||||
|
$stmt->execute([$ref_no, $date_registered, $sender, $recipient, $subject, $description, $status, $assigned_to, $id]);
|
||||||
|
$success = 'تم تحديث البيانات بنجاح';
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
if ($e->getCode() == 23000) {
|
||||||
|
$error = 'رقم القيد مستخدم مسبقاً';
|
||||||
|
} else {
|
||||||
|
$error = 'حدث خطأ: ' . $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$error = 'يرجى ملء الحقول المطلوبة (رقم القيد، الموضوع)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete action
|
||||||
|
if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) {
|
||||||
|
$id = $_GET['id'];
|
||||||
|
$stmt = db()->prepare("DELETE FROM mailbox WHERE id = ? AND type = 'inbound'");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$success = 'تم حذف البريد بنجاح';
|
||||||
|
}
|
||||||
|
|
||||||
|
$search = $_GET['search'] ?? '';
|
||||||
|
$query = "SELECT * FROM mailbox WHERE type = 'inbound'";
|
||||||
|
$params = [];
|
||||||
|
if ($search) {
|
||||||
|
$query .= " AND (ref_no LIKE ? OR sender LIKE ? OR subject LIKE ?)";
|
||||||
|
$params = ["%$search%", "%$search%", "%$search%"];
|
||||||
|
}
|
||||||
|
$query .= " ORDER BY created_at DESC";
|
||||||
|
$stmt = db()->prepare($query);
|
||||||
|
$stmt->execute($params);
|
||||||
|
$mails = $stmt->fetchAll();
|
||||||
|
|
||||||
|
$users_list = db()->query("SELECT id, full_name FROM users ORDER BY full_name")->fetchAll();
|
||||||
|
|
||||||
|
// Handle Deep Link for Edit
|
||||||
|
$deepLinkData = null;
|
||||||
|
if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id'])) {
|
||||||
|
$stmt = db()->prepare("SELECT * FROM mailbox WHERE id = ? AND type = 'inbound'");
|
||||||
|
$stmt->execute([$_GET['id']]);
|
||||||
|
$deepLinkData = $stmt->fetch();
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||||
|
<h1 class="h2">البريد الوارد</h1>
|
||||||
|
<button type="button" class="btn btn-primary" onclick="openMailModal('add')">
|
||||||
|
<i class="fas fa-plus-circle me-1"></i> إضافة جديد
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($success): ?>
|
||||||
|
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||||
|
<?= $success ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||||
|
<?= $error ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="card shadow-sm border-0 mb-4">
|
||||||
|
<div class="card-header bg-white py-3">
|
||||||
|
<form class="row g-2">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<input type="text" name="search" class="form-control" placeholder="بحث برقم القيد أو الموضوع أو المرسل..." value="<?= htmlspecialchars($search) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button type="submit" class="btn btn-secondary">بحث</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover align-middle mb-0">
|
||||||
|
<thead class="bg-light">
|
||||||
|
<tr>
|
||||||
|
<th class="ps-4">رقم القيد</th>
|
||||||
|
<th>التاريخ</th>
|
||||||
|
<th>الموضوع</th>
|
||||||
|
<th>المرسل</th>
|
||||||
|
<th>الحالة</th>
|
||||||
|
<th class="pe-4 text-center">الإجراءات</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if ($mails): foreach ($mails as $mail): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="ps-4 fw-bold text-primary"><?= $mail['ref_no'] ?></td>
|
||||||
|
<td><?= $mail['date_registered'] ?></td>
|
||||||
|
<td><?= htmlspecialchars($mail['subject']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($mail['sender']) ?></td>
|
||||||
|
<td>
|
||||||
|
<?php if ($mail['status'] === 'received'): ?>
|
||||||
|
<span class="badge bg-secondary">تم الاستلام</span>
|
||||||
|
<?php elseif ($mail['status'] === 'in_progress'): ?>
|
||||||
|
<span class="badge bg-info text-dark">قيد المعالجة</span>
|
||||||
|
<?php elseif ($mail['status'] === 'closed'): ?>
|
||||||
|
<span class="badge bg-success">مكتمل</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<td class="pe-4 text-center">
|
||||||
|
<a href="view_mail.php?id=<?= $mail['id'] ?>" class="btn btn-sm btn-outline-info" title="عرض التفاصيل"><i class="fas fa-eye"></i></a>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-primary"
|
||||||
|
onclick='openMailModal("edit", <?= json_encode($mail) ?>)' title="تعديل">
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
</button>
|
||||||
|
<a href="javascript:void(0)" onclick="confirmDelete(<?= $mail['id'] ?>)" class="btn btn-sm btn-outline-danger" title="حذف"><i class="fas fa-trash"></i></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; else: ?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="6" class="text-center py-4 text-muted">لا يوجد بريد وارد مسجل حالياً</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mail Modal -->
|
||||||
|
<div class="modal fade" id="mailModal" tabindex="-1" aria-labelledby="mailModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="mailModalLabel">إضافة بريد وارد جديد</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<form id="mailForm" method="POST">
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="hidden" name="action" id="modalAction" value="add">
|
||||||
|
<input type="hidden" name="id" id="modalId" value="0">
|
||||||
|
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">رقم القيد <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" name="ref_no" id="modalRefNo" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">تاريخ التسجيل</label>
|
||||||
|
<input type="date" name="date_registered" id="modalDateRegistered" class="form-control">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">المرسل</label>
|
||||||
|
<input type="text" name="sender" id="modalSender" class="form-control">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">المستلم الداخلي (القسم/الموظف)</label>
|
||||||
|
<input type="text" name="recipient" id="modalRecipient" class="form-control">
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="form-label">الموضوع <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" name="subject" id="modalSubject" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="form-label">الوصف / ملاحظات</label>
|
||||||
|
<textarea name="description" id="modalDescription" class="form-control" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">الحالة</label>
|
||||||
|
<select name="status" id="modalStatus" class="form-select">
|
||||||
|
<option value="received">تم الاستلام</option>
|
||||||
|
<option value="in_progress">قيد المعالجة</option>
|
||||||
|
<option value="closed">مكتمل / مغلق</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">الموظف المسؤول</label>
|
||||||
|
<select name="assigned_to" id="modalAssignedTo" class="form-select">
|
||||||
|
<option value="">-- اختر موظف --</option>
|
||||||
|
<?php foreach ($users_list as $u): ?>
|
||||||
|
<option value="<?= $u['id'] ?>"><?= htmlspecialchars($u['full_name']) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
|
||||||
|
<button type="submit" class="btn btn-primary">حفظ البيانات</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const mailModal = new bootstrap.Modal(document.getElementById('mailModal'));
|
||||||
|
|
||||||
|
function openMailModal(action, data = null) {
|
||||||
|
const label = document.getElementById('mailModalLabel');
|
||||||
|
const modalAction = document.getElementById('modalAction');
|
||||||
|
const modalId = document.getElementById('modalId');
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
ref_no: document.getElementById('modalRefNo'),
|
||||||
|
date_registered: document.getElementById('modalDateRegistered'),
|
||||||
|
sender: document.getElementById('modalSender'),
|
||||||
|
recipient: document.getElementById('modalRecipient'),
|
||||||
|
subject: document.getElementById('modalSubject'),
|
||||||
|
description: document.getElementById('modalDescription'),
|
||||||
|
status: document.getElementById('modalStatus'),
|
||||||
|
assigned_to: document.getElementById('modalAssignedTo')
|
||||||
|
};
|
||||||
|
|
||||||
|
modalAction.value = action;
|
||||||
|
|
||||||
|
if (action === 'add') {
|
||||||
|
label.textContent = 'إضافة بريد وارد جديد';
|
||||||
|
modalId.value = '0';
|
||||||
|
Object.keys(fields).forEach(key => {
|
||||||
|
if (key === 'date_registered') fields[key].value = '<?= date('Y-m-d') ?>';
|
||||||
|
else if (key === 'status') fields[key].value = 'received';
|
||||||
|
else fields[key].value = '';
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
label.textContent = 'تعديل البريد الوارد';
|
||||||
|
modalId.value = data.id;
|
||||||
|
Object.keys(fields).forEach(key => {
|
||||||
|
fields[key].value = data[key] || '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mailModal.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
<?php if ($deepLinkData): ?>
|
||||||
|
openMailModal('edit', <?= json_encode($deepLinkData) ?>);
|
||||||
|
<?php elseif ($error && isset($_POST['action'])): ?>
|
||||||
|
const data = {
|
||||||
|
id: '<?= $_POST['id'] ?? 0 ?>',
|
||||||
|
ref_no: '<?= addslashes($_POST['ref_no'] ?? '') ?>',
|
||||||
|
date_registered: '<?= $_POST['date_registered'] ?? date('Y-m-d') ?>',
|
||||||
|
sender: '<?= addslashes($_POST['sender'] ?? '') ?>',
|
||||||
|
recipient: '<?= addslashes($_POST['recipient'] ?? '') ?>',
|
||||||
|
subject: '<?= addslashes($_POST['subject'] ?? '') ?>',
|
||||||
|
description: '<?= addslashes($_POST['description'] ?? '') ?>',
|
||||||
|
status: '<?= $_POST['status'] ?? 'received' ?>',
|
||||||
|
assigned_to: '<?= $_POST['assigned_to'] ?? '' ?>'
|
||||||
|
};
|
||||||
|
openMailModal('<?= $_POST['action'] ?>', data);
|
||||||
|
<?php elseif (isset($_GET['action']) && $_GET['action'] === 'add'): ?>
|
||||||
|
openMailModal('add');
|
||||||
|
<?php endif; ?>
|
||||||
|
});
|
||||||
|
|
||||||
|
function confirmDelete(id) {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'هل أنت متأكد؟',
|
||||||
|
text: "لا يمكن التراجع عن عملية الحذف!",
|
||||||
|
icon: 'warning',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: '#d33',
|
||||||
|
cancelButtonColor: '#3085d6',
|
||||||
|
confirmButtonText: 'نعم، احذف!',
|
||||||
|
cancelButtonText: 'إلغاء'
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
window.location.href = 'inbound.php?action=delete&id=' + id;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||||
13
includes/footer.php
Normal file
13
includes/footer.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bootstrap 5 JS Bundle -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<!-- SweetAlert2 -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||||
|
<script>
|
||||||
|
// Global JS functions if needed
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
113
includes/header.php
Normal file
113
includes/header.php
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/../db/config.php';
|
||||||
|
|
||||||
|
function isLoggedIn() {
|
||||||
|
return isset($_SESSION['user_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAdmin() {
|
||||||
|
return isset($_SESSION['user_role']) && $_SESSION['user_role'] === 'admin';
|
||||||
|
}
|
||||||
|
|
||||||
|
function redirect($path) {
|
||||||
|
header("Location: $path");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLoggedIn() && basename($_SERVER['PHP_SELF']) !== 'login.php') {
|
||||||
|
redirect('login.php');
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ar" dir="rtl">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>بريد الجمعية الخيرية</title>
|
||||||
|
<!-- Bootstrap 5 RTL CSS -->
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.rtl.min.css">
|
||||||
|
<!-- Google Fonts: Cairo -->
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Cairo:wght@400;600;700&display=swap" rel="stylesheet">
|
||||||
|
<!-- Font Awesome -->
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Cairo', sans-serif;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
.sidebar {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||||
|
padding-top: 2rem;
|
||||||
|
}
|
||||||
|
.nav-link {
|
||||||
|
color: #333;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0.8rem 1.5rem;
|
||||||
|
}
|
||||||
|
.nav-link:hover, .nav-link.active {
|
||||||
|
background-color: #f0f7ff;
|
||||||
|
color: #0d6efd;
|
||||||
|
border-left: 4px solid #0d6efd;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
.btn-primary {
|
||||||
|
background-color: #0d6efd;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.status-received { background-color: #e9ecef; color: #495057; }
|
||||||
|
.status-in_progress { background-color: #cff4fc; color: #055160; }
|
||||||
|
.status-closed { background-color: #d1e7dd; color: #0f5132; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<?php if (isLoggedIn()): ?>
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<nav class="col-md-3 col-lg-2 d-md-block sidebar collapse">
|
||||||
|
<div class="position-sticky">
|
||||||
|
<div class="text-center mb-4">
|
||||||
|
<h5 class="fw-bold">بريد الجمعية</h5>
|
||||||
|
</div>
|
||||||
|
<ul class="nav flex-column">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'index.php' ? 'active' : '' ?>" href="index.php">
|
||||||
|
<i class="fas fa-home me-2"></i> لوحة التحكم
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'inbound.php' ? 'active' : '' ?>" href="inbound.php">
|
||||||
|
<i class="fas fa-download me-2"></i> البريد الوارد
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'outbound.php' ? 'active' : '' ?>" href="outbound.php">
|
||||||
|
<i class="fas fa-upload me-2"></i> البريد الصادر
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<?php if (isAdmin()): ?>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link <?= basename($_SERVER['PHP_SELF']) == 'users.php' ? 'active' : '' ?>" href="users.php">
|
||||||
|
<i class="fas fa-users me-2"></i> إدارة المستخدمين
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
<li class="nav-item mt-5">
|
||||||
|
<a class="nav-link text-danger" href="logout.php">
|
||||||
|
<i class="fas fa-sign-out-alt me-2"></i> تسجيل الخروج
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<main class="<?= isLoggedIn() ? 'col-md-9 ms-sm-auto col-lg-10' : 'col-12' ?> px-md-4 py-4">
|
||||||
272
index.php
272
index.php
@ -1,150 +1,130 @@
|
|||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
require_once __DIR__ . '/includes/header.php';
|
||||||
@ini_set('display_errors', '1');
|
|
||||||
@error_reporting(E_ALL);
|
|
||||||
@date_default_timezone_set('UTC');
|
|
||||||
|
|
||||||
$phpVersion = PHP_VERSION;
|
// Stats
|
||||||
$now = date('Y-m-d H:i:s');
|
$total_inbound = db()->query("SELECT COUNT(*) FROM mailbox WHERE type = 'inbound'")->fetchColumn();
|
||||||
|
$total_outbound = db()->query("SELECT COUNT(*) FROM mailbox WHERE type = 'outbound'")->fetchColumn();
|
||||||
|
$in_progress = db()->query("SELECT COUNT(*) FROM mailbox WHERE status = 'in_progress'")->fetchColumn();
|
||||||
|
$recent_mail = db()->query("SELECT * FROM mailbox ORDER BY created_at DESC LIMIT 5")->fetchAll();
|
||||||
|
|
||||||
|
function getStatusBadge($status) {
|
||||||
|
switch ($status) {
|
||||||
|
case 'received': return '<span class="badge bg-secondary">تم الاستلام</span>';
|
||||||
|
case 'in_progress': return '<span class="badge bg-info text-dark">قيد المعالجة</span>';
|
||||||
|
case 'closed': return '<span class="badge bg-success">مكتمل</span>';
|
||||||
|
default: return '<span class="badge bg-dark">غير معروف</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||||
<head>
|
<h1 class="h2">لوحة التحكم</h1>
|
||||||
<meta charset="utf-8" />
|
<div class="btn-toolbar mb-2 mb-md-0">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<div class="btn-group me-2">
|
||||||
<title>New Style</title>
|
<a href="inbound.php?action=add" class="btn btn-sm btn-outline-primary">إضافة بريد وارد</a>
|
||||||
<?php
|
<a href="outbound.php?action=add" class="btn btn-sm btn-outline-secondary">إضافة بريد صادر</a>
|
||||||
// Read project preview data from environment
|
</div>
|
||||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
|
||||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|
||||||
?>
|
|
||||||
<?php if ($projectDescription): ?>
|
|
||||||
<!-- Meta description -->
|
|
||||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
|
||||||
<!-- Open Graph meta tags -->
|
|
||||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
|
||||||
<!-- Twitter meta tags -->
|
|
||||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if ($projectImageUrl): ?>
|
|
||||||
<!-- Open Graph image -->
|
|
||||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
|
||||||
<!-- Twitter image -->
|
|
||||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
|
||||||
<?php endif; ?>
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--bg-color-start: #6a11cb;
|
|
||||||
--bg-color-end: #2575fc;
|
|
||||||
--text-color: #ffffff;
|
|
||||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
|
||||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
|
||||||
color: var(--text-color);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
min-height: 100vh;
|
|
||||||
text-align: center;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
body::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
|
||||||
animation: bg-pan 20s linear infinite;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
@keyframes bg-pan {
|
|
||||||
0% { background-position: 0% 0%; }
|
|
||||||
100% { background-position: 100% 100%; }
|
|
||||||
}
|
|
||||||
main {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
.card {
|
|
||||||
background: var(--card-bg-color);
|
|
||||||
border: 1px solid var(--card-border-color);
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 2rem;
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
.loader {
|
|
||||||
margin: 1.25rem auto 1.25rem;
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
border: 3px solid rgba(255, 255, 255, 0.25);
|
|
||||||
border-top-color: #fff;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
}
|
|
||||||
@keyframes spin {
|
|
||||||
from { transform: rotate(0deg); }
|
|
||||||
to { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
.hint {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
.sr-only {
|
|
||||||
position: absolute;
|
|
||||||
width: 1px; height: 1px;
|
|
||||||
padding: 0; margin: -1px;
|
|
||||||
overflow: hidden;
|
|
||||||
clip: rect(0, 0, 0, 0);
|
|
||||||
white-space: nowrap; border: 0;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 3rem;
|
|
||||||
font-weight: 700;
|
|
||||||
margin: 0 0 1rem;
|
|
||||||
letter-spacing: -1px;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
background: rgba(0,0,0,0.2);
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
||||||
}
|
|
||||||
footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 1rem;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main>
|
|
||||||
<div class="card">
|
|
||||||
<h1>Analyzing your requirements and generating your website…</h1>
|
|
||||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
|
||||||
<span class="sr-only">Loading…</span>
|
|
||||||
</div>
|
|
||||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
|
||||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
|
||||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
<footer>
|
|
||||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
<!-- Stats Cards -->
|
||||||
</footer>
|
<div class="row g-4 mb-4">
|
||||||
</body>
|
<div class="col-md-3">
|
||||||
</html>
|
<div class="card h-100 p-3 shadow-sm border-0 border-start border-primary border-4">
|
||||||
|
<div class="d-flex align-items-center justify-content-between">
|
||||||
|
<div>
|
||||||
|
<h6 class="text-muted mb-1">البريد الوارد</h6>
|
||||||
|
<h3 class="fw-bold mb-0"><?= $total_inbound ?></h3>
|
||||||
|
</div>
|
||||||
|
<div class="bg-primary bg-opacity-10 p-3 rounded-circle">
|
||||||
|
<i class="fas fa-download text-primary fs-4"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card h-100 p-3 shadow-sm border-0 border-start border-success border-4">
|
||||||
|
<div class="d-flex align-items-center justify-content-between">
|
||||||
|
<div>
|
||||||
|
<h6 class="text-muted mb-1">البريد الصادر</h6>
|
||||||
|
<h3 class="fw-bold mb-0"><?= $total_outbound ?></h3>
|
||||||
|
</div>
|
||||||
|
<div class="bg-success bg-opacity-10 p-3 rounded-circle">
|
||||||
|
<i class="fas fa-upload text-success fs-4"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card h-100 p-3 shadow-sm border-0 border-start border-info border-4">
|
||||||
|
<div class="d-flex align-items-center justify-content-between">
|
||||||
|
<div>
|
||||||
|
<h6 class="text-muted mb-1">قيد المعالجة</h6>
|
||||||
|
<h3 class="fw-bold mb-0"><?= $in_progress ?></h3>
|
||||||
|
</div>
|
||||||
|
<div class="bg-info bg-opacity-10 p-3 rounded-circle">
|
||||||
|
<i class="fas fa-clock text-info fs-4"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card h-100 p-3 shadow-sm border-0 border-start border-warning border-4">
|
||||||
|
<div class="d-flex align-items-center justify-content-between">
|
||||||
|
<div>
|
||||||
|
<h6 class="text-muted mb-1">المستخدمين</h6>
|
||||||
|
<h3 class="fw-bold mb-0"><?= db()->query("SELECT COUNT(*) FROM users")->fetchColumn() ?></h3>
|
||||||
|
</div>
|
||||||
|
<div class="bg-warning bg-opacity-10 p-3 rounded-circle">
|
||||||
|
<i class="fas fa-users text-warning fs-4"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Recent Mail -->
|
||||||
|
<div class="card shadow-sm border-0 mb-4">
|
||||||
|
<div class="card-header bg-white py-3">
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="mb-0 fw-bold">البريد المضاف حديثاً</h5>
|
||||||
|
<a href="inbound.php" class="btn btn-sm btn-link">عرض الكل</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover align-middle mb-0">
|
||||||
|
<thead class="bg-light">
|
||||||
|
<tr>
|
||||||
|
<th class="ps-4">رقم القيد</th>
|
||||||
|
<th>النوع</th>
|
||||||
|
<th>الموضوع</th>
|
||||||
|
<th>المرسل/المستلم</th>
|
||||||
|
<th>الحالة</th>
|
||||||
|
<th class="pe-4 text-center">التاريخ</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if ($recent_mail): ?>
|
||||||
|
<?php foreach ($recent_mail as $mail): ?>
|
||||||
|
<tr style="cursor: pointer;" onclick="window.location='view_mail.php?id=<?= $mail['id'] ?>'">
|
||||||
|
<td class="ps-4 fw-bold text-primary"><?= $mail['ref_no'] ?></td>
|
||||||
|
<td><?= $mail['type'] == 'inbound' ? 'وارد' : 'صادر' ?></td>
|
||||||
|
<td><?= htmlspecialchars($mail['subject']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($mail['sender'] ?: $mail['recipient']) ?></td>
|
||||||
|
<td><?= getStatusBadge($mail['status']) ?></td>
|
||||||
|
<td class="pe-4 text-center"><?= date('Y-m-d', strtotime($mail['date_registered'])) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="6" class="text-center py-4 text-muted">لا يوجد بريد مسجل حالياً</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||||
|
|||||||
63
login.php
Normal file
63
login.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/includes/header.php';
|
||||||
|
|
||||||
|
if (isLoggedIn()) {
|
||||||
|
redirect('index.php');
|
||||||
|
}
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$username = trim($_POST['username'] ?? '');
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
|
||||||
|
if ($username && $password) {
|
||||||
|
$stmt = db()->prepare("SELECT * FROM users WHERE username = ?");
|
||||||
|
$stmt->execute([$username]);
|
||||||
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($user && password_verify($password, $user['password'])) {
|
||||||
|
$_SESSION['user_id'] = $user['id'];
|
||||||
|
$_SESSION['username'] = $user['username'];
|
||||||
|
$_SESSION['full_name'] = $user['full_name'];
|
||||||
|
$_SESSION['user_role'] = $user['role'];
|
||||||
|
redirect('index.php');
|
||||||
|
} else {
|
||||||
|
$error = 'اسم المستخدم أو كلمة المرور غير صحيحة';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$error = 'يرجى إدخال جميع الحقول المطلوبة';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="row justify-content-center align-items-center" style="min-height: 80vh;">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card p-4">
|
||||||
|
<div class="text-center mb-4">
|
||||||
|
<h3 class="fw-bold">تسجيل الدخول</h3>
|
||||||
|
<p class="text-muted">بريد الجمعية الخيرية</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-danger text-center"><?= $error ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form method="POST">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username" class="form-label">اسم المستخدم</label>
|
||||||
|
<input type="text" class="form-control" id="username" name="username" required autocomplete="username">
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="password" class="form-label">كلمة المرور</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password" required autocomplete="current-password">
|
||||||
|
</div>
|
||||||
|
<div class="d-grid">
|
||||||
|
<button type="submit" class="btn btn-primary btn-lg">دخول</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||||
6
logout.php
Normal file
6
logout.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
session_unset();
|
||||||
|
session_destroy();
|
||||||
|
header("Location: login.php");
|
||||||
|
exit;
|
||||||
361
outbound.php
Normal file
361
outbound.php
Normal file
@ -0,0 +1,361 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/includes/header.php';
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
$success = '';
|
||||||
|
|
||||||
|
// Handle actions
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$action = $_POST['action'] ?? '';
|
||||||
|
$type = 'outbound';
|
||||||
|
$ref_no = $_POST['ref_no'] ?? '';
|
||||||
|
$date_registered = $_POST['date_registered'] ?? date('Y-m-d');
|
||||||
|
$sender = $_POST['sender'] ?? '';
|
||||||
|
$recipient = $_POST['recipient'] ?? '';
|
||||||
|
$subject = $_POST['subject'] ?? '';
|
||||||
|
$description = $_POST['description'] ?? '';
|
||||||
|
$status = $_POST['status'] ?? 'received';
|
||||||
|
$assigned_to = !empty($_POST['assigned_to']) ? $_POST['assigned_to'] : null;
|
||||||
|
$id = $_POST['id'] ?? 0;
|
||||||
|
|
||||||
|
if ($ref_no && $subject) {
|
||||||
|
try {
|
||||||
|
db()->beginTransaction();
|
||||||
|
if ($action === 'add') {
|
||||||
|
$stmt = db()->prepare("INSERT INTO mailbox (type, ref_no, date_registered, sender, recipient, subject, description, status, assigned_to, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
|
$stmt->execute([$type, $ref_no, $date_registered, $sender, $recipient, $subject, $description, $status, $assigned_to, $_SESSION['user_id']]);
|
||||||
|
$mail_id = db()->lastInsertId();
|
||||||
|
$success = 'تمت إضافة البريد الصادر بنجاح';
|
||||||
|
} elseif ($action === 'edit') {
|
||||||
|
$mail_id = $id;
|
||||||
|
$stmt = db()->prepare("UPDATE mailbox SET ref_no = ?, date_registered = ?, sender = ?, recipient = ?, subject = ?, description = ?, status = ?, assigned_to = ? WHERE id = ? AND type = 'outbound'");
|
||||||
|
$stmt->execute([$ref_no, $date_registered, $sender, $recipient, $subject, $description, $status, $assigned_to, $mail_id]);
|
||||||
|
$success = 'تم تحديث البيانات بنجاح';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Attachments
|
||||||
|
if (!empty($_FILES['attachments']['name'][0])) {
|
||||||
|
$upload_dir = 'uploads/attachments/';
|
||||||
|
if (!is_dir($upload_dir)) mkdir($upload_dir, 0777, true);
|
||||||
|
|
||||||
|
foreach ($_FILES['attachments']['name'] as $key => $name) {
|
||||||
|
if ($_FILES['attachments']['error'][$key] === 0) {
|
||||||
|
$file_name = time() . '_' . basename($name);
|
||||||
|
$target_path = $upload_dir . $file_name;
|
||||||
|
if (move_uploaded_file($_FILES['attachments']['tmp_name'][$key], $target_path)) {
|
||||||
|
$stmt = db()->prepare("INSERT INTO attachments (mail_id, file_path, file_name, file_size) VALUES (?, ?, ?, ?)");
|
||||||
|
$stmt->execute([$mail_id, $target_path, $name, $_FILES['attachments']['size'][$key]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db()->commit();
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
db()->rollBack();
|
||||||
|
if ($e->getCode() == 23000) {
|
||||||
|
$error = 'رقم القيد مستخدم مسبقاً';
|
||||||
|
} else {
|
||||||
|
$error = 'حدث خطأ: ' . $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$error = 'يرجى ملء الحقول المطلوبة (رقم القيد، الموضوع)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete action
|
||||||
|
if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) {
|
||||||
|
$id = $_GET['id'];
|
||||||
|
$stmt = db()->prepare("DELETE FROM mailbox WHERE id = ? AND type = 'outbound'");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$success = 'تم حذف البريد بنجاح';
|
||||||
|
}
|
||||||
|
|
||||||
|
$search = $_GET['search'] ?? '';
|
||||||
|
$query = "SELECT * FROM mailbox WHERE type = 'outbound'";
|
||||||
|
$params = [];
|
||||||
|
if ($search) {
|
||||||
|
$query .= " AND (ref_no LIKE ? OR recipient LIKE ? OR subject LIKE ?)";
|
||||||
|
$params = ["%$search%", "%$search%", "%$search%"];
|
||||||
|
}
|
||||||
|
$query .= " ORDER BY created_at DESC";
|
||||||
|
$stmt = db()->prepare($query);
|
||||||
|
$stmt->execute($params);
|
||||||
|
$mails = $stmt->fetchAll();
|
||||||
|
|
||||||
|
$users_list = db()->query("SELECT id, full_name FROM users ORDER BY full_name")->fetchAll();
|
||||||
|
|
||||||
|
// Handle Deep Link for Edit
|
||||||
|
$deepLinkData = null;
|
||||||
|
if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id'])) {
|
||||||
|
$stmt = db()->prepare("SELECT * FROM mailbox WHERE id = ? AND type = 'outbound'");
|
||||||
|
$stmt->execute([$_GET['id']]);
|
||||||
|
$deepLinkData = $stmt->fetch();
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||||
|
<h1 class="h2">البريد الصادر</h1>
|
||||||
|
<button type="button" class="btn btn-primary" onclick="openMailModal('add')">
|
||||||
|
<i class="fas fa-plus-circle me-1"></i> إضافة جديد
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($success): ?>
|
||||||
|
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||||
|
<?= $success ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||||
|
<?= $error ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="card shadow-sm border-0 mb-4">
|
||||||
|
<div class="card-header bg-white py-3">
|
||||||
|
<form class="row g-2">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<input type="text" name="search" class="form-control" placeholder="بحث برقم القيد أو الموضوع أو المستلم..." value="<?= htmlspecialchars($search) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="col-auto">
|
||||||
|
<button type="submit" class="btn btn-secondary">بحث</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover align-middle mb-0">
|
||||||
|
<thead class="bg-light">
|
||||||
|
<tr>
|
||||||
|
<th class="ps-4">رقم القيد</th>
|
||||||
|
<th>التاريخ</th>
|
||||||
|
<th>الموضوع</th>
|
||||||
|
<th>المستلم الخارجى</th>
|
||||||
|
<th>الحالة</th>
|
||||||
|
<th class="pe-4 text-center">الإجراءات</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if ($mails): foreach ($mails as $mail): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="ps-4 fw-bold text-primary"><?= $mail['ref_no'] ?></td>
|
||||||
|
<td><?= $mail['date_registered'] ?></td>
|
||||||
|
<td><?= mb_strimwidth(strip_tags($mail['subject']), 0, 100, "...") ?></td>
|
||||||
|
<td><?= htmlspecialchars($mail['recipient']) ?></td>
|
||||||
|
<td>
|
||||||
|
<?php if ($mail['status'] === 'received'): ?>
|
||||||
|
<span class="badge bg-secondary">تم الاستلام</span>
|
||||||
|
<?php elseif ($mail['status'] === 'in_progress'): ?>
|
||||||
|
<span class="badge bg-info text-dark">قيد المعالجة</span>
|
||||||
|
<?php elseif ($mail['status'] === 'closed'): ?>
|
||||||
|
<span class="badge bg-success">مكتمل</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<td class="pe-4 text-center">
|
||||||
|
<a href="view_mail.php?id=<?= $mail['id'] ?>" class="btn btn-sm btn-outline-info" title="عرض التفاصيل"><i class="fas fa-eye"></i></a>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-primary"
|
||||||
|
onclick='openMailModal("edit", <?= json_encode($mail) ?>)' title="تعديل">
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
</button>
|
||||||
|
<a href="javascript:void(0)" onclick="confirmDelete(<?= $mail['id'] ?>)" class="btn btn-sm btn-outline-danger" title="حذف"><i class="fas fa-trash"></i></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; else: ?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="6" class="text-center py-4 text-muted">لا يوجد بريد صادر مسجل حالياً</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mail Modal -->
|
||||||
|
<div class="modal fade" id="mailModal" tabindex="-1" aria-labelledby="mailModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-xl">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="mailModalLabel">إضافة بريد صادر جديد</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<form id="mailForm" method="POST" enctype="multipart/form-data">
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="hidden" name="action" id="modalAction" value="add">
|
||||||
|
<input type="hidden" name="id" id="modalId" value="0">
|
||||||
|
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">رقم القيد <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" name="ref_no" id="modalRefNo" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">تاريخ التسجيل</label>
|
||||||
|
<input type="date" name="date_registered" id="modalDateRegistered" class="form-control">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">المستلم الخارجي (الجهة المستلمة)</label>
|
||||||
|
<input type="text" name="recipient" id="modalRecipient" class="form-control">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">المرسل الداخلي (القسم/الموظف)</label>
|
||||||
|
<input type="text" name="sender" id="modalSender" class="form-control">
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="form-label">الموضوع <span class="text-danger">*</span></label>
|
||||||
|
<textarea name="subject" id="subject_editor" class="form-control" rows="2"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="form-label">الوصف / ملاحظات</label>
|
||||||
|
<textarea name="description" id="description_editor" class="form-control" rows="5"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="form-label">المرفقات</label>
|
||||||
|
<input type="file" name="attachments[]" class="form-control" multiple>
|
||||||
|
<div class="form-text text-muted">يمكنك اختيار ملفات متعددة.</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">الحالة</label>
|
||||||
|
<select name="status" id="modalStatus" class="form-select">
|
||||||
|
<option value="received">تم الاستلام</option>
|
||||||
|
<option value="in_progress">قيد المعالجة</option>
|
||||||
|
<option value="closed">مكتمل / مغلق</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">الموظف المسؤول</label>
|
||||||
|
<select name="assigned_to" id="modalAssignedTo" class="form-select">
|
||||||
|
<option value="">-- اختر موظف --</option>
|
||||||
|
<?php foreach ($users_list as $u): ?>
|
||||||
|
<option value="<?= $u['id'] ?>"><?= htmlspecialchars($u['full_name']) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
|
||||||
|
<button type="submit" class="btn btn-primary">حفظ البيانات</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.ckeditor.com/ckeditor5/36.0.1/classic/ckeditor.js"></script>
|
||||||
|
<script>
|
||||||
|
let subjectEditor, descriptionEditor;
|
||||||
|
|
||||||
|
function initEditors() {
|
||||||
|
return Promise.all([
|
||||||
|
ClassicEditor.create(document.querySelector('#subject_editor'), {
|
||||||
|
language: { content: 'ar', ui: 'ar' },
|
||||||
|
toolbar: ['heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'undo', 'redo']
|
||||||
|
}).then(editor => { subjectEditor = editor; }),
|
||||||
|
|
||||||
|
ClassicEditor.create(document.querySelector('#description_editor'), {
|
||||||
|
language: { content: 'ar', ui: 'ar' },
|
||||||
|
toolbar: ['heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', '|', 'undo', 'redo']
|
||||||
|
}).then(editor => { descriptionEditor = editor; })
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mailModalElement = document.getElementById('mailModal');
|
||||||
|
const mailModal = new bootstrap.Modal(mailModalElement);
|
||||||
|
|
||||||
|
function openMailModal(action, data = null) {
|
||||||
|
const label = document.getElementById('mailModalLabel');
|
||||||
|
const modalAction = document.getElementById('modalAction');
|
||||||
|
const modalId = document.getElementById('modalId');
|
||||||
|
|
||||||
|
const fields = {
|
||||||
|
ref_no: document.getElementById('modalRefNo'),
|
||||||
|
date_registered: document.getElementById('modalDateRegistered'),
|
||||||
|
sender: document.getElementById('modalSender'),
|
||||||
|
recipient: document.getElementById('modalRecipient'),
|
||||||
|
status: document.getElementById('modalStatus'),
|
||||||
|
assigned_to: document.getElementById('modalAssignedTo')
|
||||||
|
};
|
||||||
|
|
||||||
|
modalAction.value = action;
|
||||||
|
|
||||||
|
if (action === 'add') {
|
||||||
|
label.textContent = 'إضافة بريد صادر جديد';
|
||||||
|
modalId.value = '0';
|
||||||
|
Object.keys(fields).forEach(key => {
|
||||||
|
if (key === 'date_registered') fields[key].value = '<?= date('Y-m-d') ?>';
|
||||||
|
else if (key === 'status') fields[key].value = 'received';
|
||||||
|
else fields[key].value = '';
|
||||||
|
});
|
||||||
|
if (subjectEditor) subjectEditor.setData('');
|
||||||
|
if (descriptionEditor) descriptionEditor.setData('');
|
||||||
|
} else {
|
||||||
|
label.textContent = 'تعديل البريد الصادر';
|
||||||
|
modalId.value = data.id;
|
||||||
|
Object.keys(fields).forEach(key => {
|
||||||
|
fields[key].value = data[key] || '';
|
||||||
|
});
|
||||||
|
if (subjectEditor) subjectEditor.setData(data.subject || '');
|
||||||
|
if (descriptionEditor) descriptionEditor.setData(data.description || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
mailModal.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
initEditors().then(() => {
|
||||||
|
// Deep link or error handling
|
||||||
|
<?php if ($deepLinkData): ?>
|
||||||
|
openMailModal('edit', <?= json_encode($deepLinkData) ?>);
|
||||||
|
<?php elseif ($error && isset($_POST['action'])): ?>
|
||||||
|
const data = {
|
||||||
|
id: '<?= $_POST['id'] ?? 0 ?>',
|
||||||
|
ref_no: '<?= addslashes($_POST['ref_no'] ?? '') ?>',
|
||||||
|
date_registered: '<?= $_POST['date_registered'] ?? date('Y-m-d') ?>',
|
||||||
|
sender: '<?= addslashes($_POST['sender'] ?? '') ?>',
|
||||||
|
recipient: '<?= addslashes($_POST['recipient'] ?? '') ?>',
|
||||||
|
subject: `<?= addslashes($_POST['subject'] ?? '') ?>`,
|
||||||
|
description: `<?= addslashes($_POST['description'] ?? '') ?>`,
|
||||||
|
status: '<?= $_POST['status'] ?? 'received' ?>',
|
||||||
|
assigned_to: '<?= $_POST['assigned_to'] ?? '' ?>'
|
||||||
|
};
|
||||||
|
openMailModal('<?= $_POST['action'] ?>', data);
|
||||||
|
<?php elseif (isset($_GET['action']) && $_GET['action'] === 'add'): ?>
|
||||||
|
openMailModal('add');
|
||||||
|
<?php endif; ?>
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function confirmDelete(id) {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'هل أنت متأكد؟',
|
||||||
|
text: "لا يمكن التراجع عن عملية الحذف!",
|
||||||
|
icon: 'warning',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: '#d33',
|
||||||
|
cancelButtonColor: '#3085d6',
|
||||||
|
confirmButtonText: 'نعم، احذف!',
|
||||||
|
cancelButtonText: 'إلغاء'
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
window.location.href = 'outbound.php?action=delete&id=' + id;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.ck-editor__editable_inline {
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
|
#description_editor + .ck-editor .ck-editor__editable_inline {
|
||||||
|
min-height: 250px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||||
261
users.php
Normal file
261
users.php
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/includes/header.php';
|
||||||
|
|
||||||
|
if (!isAdmin()) {
|
||||||
|
echo '<div class="alert alert-danger mt-4">غير مصرح لك بالوصول لهذه الصفحة.</div>';
|
||||||
|
require_once __DIR__ . '/includes/footer.php';
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
$success = '';
|
||||||
|
|
||||||
|
// Handle actions
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$action = $_POST['action'] ?? '';
|
||||||
|
$username = $_POST['username'] ?? '';
|
||||||
|
$full_name = $_POST['full_name'] ?? '';
|
||||||
|
$role = $_POST['role'] ?? 'staff';
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
$id = $_POST['id'] ?? 0;
|
||||||
|
|
||||||
|
if ($username && $full_name) {
|
||||||
|
try {
|
||||||
|
if ($action === 'add') {
|
||||||
|
if (!$password) {
|
||||||
|
$error = 'يرجى إدخال كلمة المرور للمستخدم الجديد';
|
||||||
|
} else {
|
||||||
|
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
$stmt = db()->prepare("INSERT INTO users (username, full_name, role, password) VALUES (?, ?, ?, ?)");
|
||||||
|
$stmt->execute([$username, $full_name, $role, $hashed_password]);
|
||||||
|
$success = 'تمت إضافة المستخدم بنجاح';
|
||||||
|
}
|
||||||
|
} elseif ($action === 'edit') {
|
||||||
|
if ($password) {
|
||||||
|
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
$stmt = db()->prepare("UPDATE users SET username = ?, full_name = ?, role = ?, password = ? WHERE id = ?");
|
||||||
|
$stmt->execute([$username, $full_name, $role, $hashed_password, $id]);
|
||||||
|
} else {
|
||||||
|
$stmt = db()->prepare("UPDATE users SET username = ?, full_name = ?, role = ? WHERE id = ?");
|
||||||
|
$stmt->execute([$username, $full_name, $role, $id]);
|
||||||
|
}
|
||||||
|
$success = 'تم تحديث بيانات المستخدم بنجاح';
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
if ($e->getCode() == 23000) {
|
||||||
|
$error = 'اسم المستخدم موجود مسبقاً';
|
||||||
|
} else {
|
||||||
|
$error = 'حدث خطأ: ' . $e->getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$error = 'يرجى ملء الحقول المطلوبة';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete action
|
||||||
|
if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) {
|
||||||
|
$id = $_GET['id'];
|
||||||
|
if ($id == $_SESSION['user_id']) {
|
||||||
|
$error = 'لا يمكنك حذف حسابك الحالي';
|
||||||
|
} else {
|
||||||
|
$stmt = db()->prepare("DELETE FROM users WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$success = 'تم حذف المستخدم بنجاح';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$users = db()->query("SELECT * FROM users ORDER BY created_at DESC")->fetchAll();
|
||||||
|
|
||||||
|
// Handle Deep Link for Edit
|
||||||
|
$deepLinkData = null;
|
||||||
|
if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id'])) {
|
||||||
|
$stmt = db()->prepare("SELECT * FROM users WHERE id = ?");
|
||||||
|
$stmt->execute([$_GET['id']]);
|
||||||
|
$deepLinkData = $stmt->fetch();
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||||
|
<h1 class="h2">إدارة المستخدمين</h1>
|
||||||
|
<button type="button" class="btn btn-primary" onclick="openUserModal('add')">
|
||||||
|
<i class="fas fa-user-plus me-1"></i> إضافة مستخدم
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($success): ?>
|
||||||
|
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||||
|
<?= $success ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||||
|
<?= $error ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="card shadow-sm border-0">
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover align-middle mb-0">
|
||||||
|
<thead class="bg-light">
|
||||||
|
<tr>
|
||||||
|
<th class="ps-4">اسم المستخدم</th>
|
||||||
|
<th>الاسم الكامل</th>
|
||||||
|
<th>الصلاحية</th>
|
||||||
|
<th>تاريخ الإنشاء</th>
|
||||||
|
<th class="pe-4 text-center">الإجراءات</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($users as $user): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="ps-4 fw-bold"><?= htmlspecialchars($user['username']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($user['full_name']) ?></td>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
$badge = 'bg-secondary';
|
||||||
|
if ($user['role'] === 'admin') $badge = 'bg-danger';
|
||||||
|
if ($user['role'] === 'clerk') $badge = 'bg-primary';
|
||||||
|
?>
|
||||||
|
<span class="badge <?= $badge ?>"><?= $user['role'] ?></span>
|
||||||
|
</td>
|
||||||
|
<td><?= date('Y-m-d', strtotime($user['created_at'])) ?></td>
|
||||||
|
<td class="pe-4 text-center">
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-primary"
|
||||||
|
onclick='openUserModal("edit", <?= json_encode($user) ?>)'>
|
||||||
|
<i class="fas fa-edit"></i>
|
||||||
|
</button>
|
||||||
|
<?php if ($user['id'] != $_SESSION['user_id']): ?>
|
||||||
|
<a href="javascript:void(0)" onclick="confirmDelete(<?= $user['id'] ?>)" class="btn btn-sm btn-outline-danger"><i class="fas fa-trash"></i></a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- User Modal -->
|
||||||
|
<div class="modal fade" id="userModal" tabindex="-1" aria-labelledby="userModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="userModalLabel">إضافة مستخدم جديد</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<form id="userForm" method="POST">
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="hidden" name="action" id="modalAction" value="add">
|
||||||
|
<input type="hidden" name="id" id="modalUserId" value="0">
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">اسم المستخدم <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" name="username" id="modalUsername" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">الاسم الكامل <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" name="full_name" id="modalFullName" class="form-control" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">الصلاحية</label>
|
||||||
|
<select name="role" id="modalRole" class="form-select">
|
||||||
|
<option value="staff">موظف (Staff)</option>
|
||||||
|
<option value="clerk">مدخل بيانات (Clerk)</option>
|
||||||
|
<option value="admin">مدير (Admin)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" id="passwordLabel">كلمة المرور <span class="text-danger">*</span></label>
|
||||||
|
<input type="password" name="password" id="modalPassword" class="form-control">
|
||||||
|
<small class="text-muted" id="passwordHelp" style="display:none;">اتركه فارغاً للمحافظة على الحالية</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">إلغاء</button>
|
||||||
|
<button type="submit" class="btn btn-primary">حفظ البيانات</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const userModal = new bootstrap.Modal(document.getElementById('userModal'));
|
||||||
|
|
||||||
|
function openUserModal(action, data = null) {
|
||||||
|
const label = document.getElementById('userModalLabel');
|
||||||
|
const modalAction = document.getElementById('modalAction');
|
||||||
|
const modalUserId = document.getElementById('modalUserId');
|
||||||
|
const modalUsername = document.getElementById('modalUsername');
|
||||||
|
const modalFullName = document.getElementById('modalFullName');
|
||||||
|
const modalRole = document.getElementById('modalRole');
|
||||||
|
const modalPassword = document.getElementById('modalPassword');
|
||||||
|
const passwordLabel = document.getElementById('passwordLabel');
|
||||||
|
const passwordHelp = document.getElementById('passwordHelp');
|
||||||
|
|
||||||
|
modalAction.value = action;
|
||||||
|
|
||||||
|
if (action === 'add') {
|
||||||
|
label.textContent = 'إضافة مستخدم جديد';
|
||||||
|
modalUserId.value = '0';
|
||||||
|
modalUsername.value = '';
|
||||||
|
modalFullName.value = '';
|
||||||
|
modalRole.value = 'staff';
|
||||||
|
modalPassword.required = true;
|
||||||
|
passwordLabel.innerHTML = 'كلمة المرور <span class="text-danger">*</span>';
|
||||||
|
passwordHelp.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
label.textContent = 'تعديل بيانات المستخدم';
|
||||||
|
modalUserId.value = data.id;
|
||||||
|
modalUsername.value = data.username;
|
||||||
|
modalFullName.value = data.full_name;
|
||||||
|
modalRole.value = data.role;
|
||||||
|
modalPassword.required = false;
|
||||||
|
passwordLabel.innerHTML = 'كلمة المرور';
|
||||||
|
passwordHelp.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
userModal.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
<?php if ($deepLinkData): ?>
|
||||||
|
openUserModal('edit', <?= json_encode($deepLinkData) ?>);
|
||||||
|
<?php elseif ($error && isset($_POST['action'])): ?>
|
||||||
|
const data = {
|
||||||
|
id: '<?= $_POST['id'] ?? 0 ?>',
|
||||||
|
username: '<?= addslashes($_POST['username'] ?? '') ?>',
|
||||||
|
full_name: '<?= addslashes($_POST['full_name'] ?? '') ?>',
|
||||||
|
role: '<?= $_POST['role'] ?? 'staff' ?>'
|
||||||
|
};
|
||||||
|
openUserModal('<?= $_POST['action'] ?>', data);
|
||||||
|
<?php elseif (isset($_GET['action']) && $_GET['action'] === 'add'): ?>
|
||||||
|
openUserModal('add');
|
||||||
|
<?php endif; ?>
|
||||||
|
});
|
||||||
|
|
||||||
|
function confirmDelete(id) {
|
||||||
|
Swal.fire({
|
||||||
|
title: 'هل أنت متأكد؟',
|
||||||
|
text: "سيتم حذف المستخدم بشكل نهائي!",
|
||||||
|
icon: 'warning',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: '#d33',
|
||||||
|
cancelButtonColor: '#3085d6',
|
||||||
|
confirmButtonText: 'نعم، احذف!',
|
||||||
|
cancelButtonText: 'إلغاء'
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
window.location.href = 'users.php?action=delete&id=' + id;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||||
211
view_mail.php
Normal file
211
view_mail.php
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/includes/header.php';
|
||||||
|
|
||||||
|
$id = $_GET['id'] ?? 0;
|
||||||
|
if (!$id) redirect('index.php');
|
||||||
|
|
||||||
|
$stmt = db()->prepare("SELECT m.*, u1.full_name as assigned_name, u2.full_name as creator_name
|
||||||
|
FROM mailbox m
|
||||||
|
LEFT JOIN users u1 ON m.assigned_to = u1.id
|
||||||
|
LEFT JOIN users u2 ON m.created_by = u2.id
|
||||||
|
WHERE m.id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$mail = $stmt->fetch();
|
||||||
|
|
||||||
|
if (!$mail) redirect('index.php');
|
||||||
|
|
||||||
|
$success = '';
|
||||||
|
$error = '';
|
||||||
|
|
||||||
|
// Handle Comment submission
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_comment'])) {
|
||||||
|
$comment = $_POST['comment'] ?? '';
|
||||||
|
if ($comment) {
|
||||||
|
$stmt = db()->prepare("INSERT INTO comments (mail_id, user_id, comment) VALUES (?, ?, ?)");
|
||||||
|
$stmt->execute([$id, $_SESSION['user_id'], $comment]);
|
||||||
|
$success = 'تم إضافة التعليق بنجاح';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Attachment upload
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['attachment'])) {
|
||||||
|
$file = $_FILES['attachment'];
|
||||||
|
if ($file['error'] === 0) {
|
||||||
|
$upload_dir = 'uploads/attachments/';
|
||||||
|
if (!is_dir($upload_dir)) mkdir($upload_dir, 0777, true);
|
||||||
|
|
||||||
|
$file_name = time() . '_' . basename($file['name']);
|
||||||
|
$target_path = $upload_dir . $file_name;
|
||||||
|
|
||||||
|
if (move_uploaded_file($file['tmp_name'], $target_path)) {
|
||||||
|
$stmt = db()->prepare("INSERT INTO attachments (mail_id, file_path, file_name, file_size) VALUES (?, ?, ?, ?)");
|
||||||
|
$stmt->execute([$id, $target_path, $file['name'], $file['size']]);
|
||||||
|
$success = 'تم رفع الملف بنجاح';
|
||||||
|
} else {
|
||||||
|
$error = 'فشل في رفع الملف';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$comments = db()->prepare("SELECT c.*, u.full_name FROM comments c LEFT JOIN users u ON c.user_id = u.id WHERE c.mail_id = ? ORDER BY c.created_at DESC");
|
||||||
|
$comments->execute([$id]);
|
||||||
|
$mail_comments = $comments->fetchAll();
|
||||||
|
|
||||||
|
$attachments = db()->prepare("SELECT * FROM attachments WHERE mail_id = ? ORDER BY created_at DESC");
|
||||||
|
$attachments->execute([$id]);
|
||||||
|
$mail_attachments = $attachments->fetchAll();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
|
||||||
|
<h1 class="h2">تفاصيل <?= $mail['type'] == 'inbound' ? 'البريد الوارد' : 'البريد الصادر' ?></h1>
|
||||||
|
<div class="btn-group">
|
||||||
|
<a href="<?= $mail['type'] ?>.php" class="btn btn-outline-secondary">عودة للقائمة</a>
|
||||||
|
<a href="<?= $mail['type'] ?>.php?action=edit&id=<?= $mail['id'] ?>" class="btn btn-outline-primary">تعديل البيانات</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($success): ?>
|
||||||
|
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||||
|
<?= $success ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<!-- Mail Details -->
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card shadow-sm border-0 mb-4">
|
||||||
|
<div class="card-header bg-white py-3">
|
||||||
|
<h5 class="mb-0 fw-bold">المعلومات الأساسية</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="text-muted small">رقم القيد</label>
|
||||||
|
<p class="fw-bold fs-5 text-primary"><?= $mail['ref_no'] ?></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="text-muted small">تاريخ التسجيل</label>
|
||||||
|
<p class="fw-bold"><?= $mail['date_registered'] ?></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="text-muted small">الحالة</label>
|
||||||
|
<p>
|
||||||
|
<?php if ($mail['status'] === 'received'): ?>
|
||||||
|
<span class="badge bg-secondary">تم الاستلام</span>
|
||||||
|
<?php elseif ($mail['status'] === 'in_progress'): ?>
|
||||||
|
<span class="badge bg-info text-dark">قيد المعالجة</span>
|
||||||
|
<?php elseif ($mail['status'] === 'closed'): ?>
|
||||||
|
<span class="badge bg-success">مكتمل</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="text-muted small">الموضوع</label>
|
||||||
|
<div class="fw-bold">
|
||||||
|
<?php
|
||||||
|
if ($mail['type'] == 'outbound') {
|
||||||
|
echo $mail['subject'];
|
||||||
|
} else {
|
||||||
|
echo htmlspecialchars($mail['subject']);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="text-muted small"><?= $mail['type'] == 'inbound' ? 'المرسل' : 'المرسل الداخلي' ?></label>
|
||||||
|
<p><?= htmlspecialchars($mail['sender'] ?: 'غير محدد') ?></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="text-muted small"><?= $mail['type'] == 'inbound' ? 'المستلم الداخلي' : 'الجهة المستلمة' ?></label>
|
||||||
|
<p><?= htmlspecialchars($mail['recipient'] ?: 'غير محدد') ?></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="text-muted small">الوصف / ملاحظات</label>
|
||||||
|
<div class="bg-light p-3 rounded border">
|
||||||
|
<?php
|
||||||
|
if ($mail['type'] == 'outbound') {
|
||||||
|
echo $mail['description'] ?: '<span class="text-muted">لا يوجد وصف إضافي</span>';
|
||||||
|
} else {
|
||||||
|
echo nl2br(htmlspecialchars($mail['description'] ?: 'لا يوجد وصف إضافي'));
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="text-muted small">الموظف المسؤول</label>
|
||||||
|
<p><?= htmlspecialchars($mail['assigned_name'] ?: 'غير معين') ?></p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 text-end">
|
||||||
|
<label class="text-muted small">بواسطة / التاريخ</label>
|
||||||
|
<p class="text-muted small"><?= htmlspecialchars($mail['creator_name']) ?> | <?= $mail['created_at'] ?></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Comments -->
|
||||||
|
<div class="card shadow-sm border-0 mb-4">
|
||||||
|
<div class="card-header bg-white py-3">
|
||||||
|
<h5 class="mb-0 fw-bold">التعليقات والمتابعة</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="POST" class="mb-4">
|
||||||
|
<div class="mb-2">
|
||||||
|
<textarea name="comment" class="form-control" rows="2" placeholder="أضف تعليقاً أو ملاحظة متابعة..." required></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="submit" name="add_comment" class="btn btn-sm btn-primary">إرسال تعليق</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="comment-list">
|
||||||
|
<?php if ($mail_comments): foreach ($mail_comments as $c): ?>
|
||||||
|
<div class="border-bottom pb-2 mb-2">
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<span class="fw-bold text-primary"><?= htmlspecialchars($c['full_name']) ?></span>
|
||||||
|
<span class="text-muted small"><?= $c['created_at'] ?></span>
|
||||||
|
</div>
|
||||||
|
<p class="mb-0 small"><?= nl2br(htmlspecialchars($c['comment'])) ?></p>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; else: ?>
|
||||||
|
<p class="text-center text-muted small">لا توجد تعليقات بعد</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Attachments Side -->
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card shadow-sm border-0 mb-4">
|
||||||
|
<div class="card-header bg-white py-3">
|
||||||
|
<h5 class="mb-0 fw-bold">المرفقات</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="POST" enctype="multipart/form-data" class="mb-4">
|
||||||
|
<div class="mb-2">
|
||||||
|
<input type="file" name="attachment" class="form-control form-control-sm" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-sm btn-secondary w-100">رفع ملف</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="list-group list-group-flush">
|
||||||
|
<?php if ($mail_attachments): foreach ($mail_attachments as $a): ?>
|
||||||
|
<div class="list-group-item px-0">
|
||||||
|
<div class="d-flex w-100 justify-content-between align-items-center">
|
||||||
|
<div class="text-truncate" style="max-width: 200px;">
|
||||||
|
<i class="fas fa-file me-2 text-muted"></i>
|
||||||
|
<a href="<?= $a['file_path'] ?>" target="_blank" class="text-decoration-none small"><?= htmlspecialchars($a['file_name']) ?></a>
|
||||||
|
</div>
|
||||||
|
<span class="text-muted small"><?= round($a['file_size'] / 1024, 1) ?> KB</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; else: ?>
|
||||||
|
<p class="text-center text-muted small py-3">لا توجد مرفقات</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||||
Loading…
x
Reference in New Issue
Block a user