38808-vm/outbound.php
2026-03-27 09:55:11 +00:00

538 lines
26 KiB
PHP

<?php
require_once 'includes/header.php';
require_once 'includes/pagination.php';
$error = '';
$success = '';
// Handle CRUD operations
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
if (!canEdit('outbound') && !canAdd('outbound')) {
$error = 'ليس لديك صلاحية للقيام بهذا الإجراء.';
} else {
$action = $_POST['action'];
$id = $_POST['id'] ?? 0;
$ref_no = $_POST['ref_no'] ?? '';
$date_registered = $_POST['date_registered'] ?? date('Y-m-d');
$due_date = !empty($_POST['due_date']) ? $_POST['due_date'] : null;
$sender = $_POST['sender'] ?? '';
$recipient = $_POST['recipient'] ?? '';
$subject = $_POST['subject'] ?? '';
$description = $_POST['description'] ?? '';
$status_id = !empty($_POST['status_id']) ? $_POST['status_id'] : null;
$assigned_to = !empty($_POST['assigned_to']) ? $_POST['assigned_to'] : null;
if ($action === 'add' || $action === 'edit') {
try {
db()->beginTransaction();
if ($action === 'add') {
if (!canAdd('outbound')) throw new Exception('ليس لديك صلاحية الإضافة.');
$stmt = db()->prepare("INSERT INTO outbound_mail (ref_no, date_registered, due_date, sender, recipient, subject, description, status_id, assigned_to, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$ref_no, $date_registered, $due_date, $sender, $recipient, $subject, $description, $status_id, $assigned_to, $_SESSION['user_id']]);
$id = db()->lastInsertId();
$success = 'تم إضافة البريد الصادر بنجاح.';
} else {
if (!canEdit('outbound')) throw new Exception('ليس لديك صلاحية التعديل.');
$stmt = db()->prepare("UPDATE outbound_mail SET ref_no = ?, date_registered = ?, due_date = ?, sender = ?, recipient = ?, subject = ?, description = ?, status_id = ?, assigned_to = ? WHERE id = ?");
$stmt->execute([$ref_no, $date_registered, $due_date, $sender, $recipient, $subject, $description, $status_id, $assigned_to, $id]);
$success = 'تم تحديث بيانات البريد الصادر بنجاح.';
}
// Handle file uploads
if (isset($_FILES['attachments']) && !empty($_FILES['attachments']['name'][0])) {
$upload_dir = 'uploads/attachments/';
if (!is_dir($upload_dir)) mkdir($upload_dir, 0777, true);
for ($i = 0; $i < count($_FILES['attachments']['name']); $i++) {
if ($_FILES['attachments']['error'][$i] === 0) {
$filename = time() . '_' . $_FILES['attachments']['name'][$i];
$filepath = $upload_dir . $filename;
if (move_uploaded_file($_FILES['attachments']['tmp_name'][$i], $filepath)) {
$stmt = db()->prepare("INSERT INTO outbound_attachments (mail_id, display_name, file_path, file_name, file_size) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$id, $_FILES['attachments']['name'][$i], $filepath, $_FILES['attachments']['name'][$i], $_FILES['attachments']['size'][$i]]);
}
}
}
}
db()->commit();
} catch (Exception $e) {
db()->rollBack();
$error = 'خطأ: ' . $e->getMessage();
}
} elseif ($action === 'delete') {
if (!canDelete('outbound')) {
$error = 'ليس لديك صلاحية الحذف.';
} else {
$stmt = db()->prepare("DELETE FROM outbound_mail WHERE id = ?");
$stmt->execute([$id]);
$success = 'تم حذف البريد الصادر بنجاح.';
}
}
}
}
// Fetch stats
$total_stmt = db()->query("SELECT COUNT(*) FROM outbound_mail");
$total_outbound = $total_stmt->fetchColumn();
$completed_stmt = db()->prepare("SELECT COUNT(*) FROM outbound_mail WHERE status_id IN (SELECT id FROM mailbox_statuses WHERE name LIKE '%مكتمل%' OR name LIKE '%منتهي%')");
$completed_stmt->execute();
$completed_outbound = $completed_stmt->fetchColumn();
// Search and Filter
$where = "WHERE 1=1";
$params = [];
if (isset($_GET['search']) && !empty($_GET['search'])) {
$where .= " AND (m.ref_no LIKE ? OR m.subject LIKE ? OR m.sender LIKE ? OR m.recipient LIKE ?)";
$search = "%" . $_GET['search'] . "%";
$params = array_merge($params, [$search, $search, $search, $search]);
}
if (isset($_GET['status_id']) && !empty($_GET['status_id'])) {
$where .= " AND m.status_id = ?";
$params[] = $_GET['status_id'];
}
// Pagination
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
if ($page < 1) $page = 1;
$limit = 10;
$offset = ($page - 1) * $limit;
// Count filtered
$countQuery = "SELECT COUNT(*) FROM outbound_mail m $where";
$countStmt = db()->prepare($countQuery);
$countStmt->execute($params);
$totalFiltered = $countStmt->fetchColumn();
$query = "SELECT m.*, s.name as status_name, s.color as status_color, u.full_name as assigned_to_name,
(SELECT GROUP_CONCAT(display_name SEPARATOR '|||') FROM outbound_attachments WHERE mail_id = m.id) as attachment_names
FROM outbound_mail m
LEFT JOIN mailbox_statuses s ON m.status_id = s.id
LEFT JOIN users u ON m.assigned_to = u.id
$where
ORDER BY m.date_registered DESC, m.id DESC
LIMIT $limit OFFSET $offset";
$stmt = db()->prepare($query);
$stmt->execute($params);
$mails = $stmt->fetchAll();
$statuses = db()->query("SELECT * FROM mailbox_statuses ORDER BY id ASC")->fetchAll();
$users = db()->query("SELECT id, full_name, username FROM users ORDER BY full_name ASC")->fetchAll();
$default_status_id = db()->query("SELECT id FROM mailbox_statuses WHERE is_default = 1 LIMIT 1")->fetchColumn() ?: ($statuses[0]['id'] ?? null);
$deepLinkData = null;
if (isset($_GET['id'])) {
$dlStmt = db()->prepare("SELECT m.*, (SELECT GROUP_CONCAT(display_name SEPARATOR '|||') FROM outbound_attachments WHERE mail_id = m.id) as attachment_names FROM outbound_mail m WHERE m.id = ?");
$dlStmt->execute([$_GET['id']]);
$deepLinkData = $dlStmt->fetch();
}
?>
<div class="container-fluid py-4">
<div class="row mb-4">
<div class="col-md-8">
<h2 class="fw-bold mb-1"><i class="fas fa-upload text-primary me-2"></i> البريد الصادر</h2>
<p class="text-muted">إدارة المراسلات الصادرة إلى الجهات الخارجية وتتبع حالاتها.</p>
</div>
<div class="col-md-4 text-md-end d-flex align-items-center justify-content-md-end gap-2">
<?php if (canAdd('outbound')): ?>
<button class="btn btn-primary px-4 py-2" onclick="openMailModal('add')">
<i class="fas fa-plus me-1"></i> إضافة صادر جديد
</button>
<?php endif; ?>
</div>
</div>
<!-- Stats Cards -->
<div class="row g-3 mb-4">
<div class="col-md-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body">
<div class="d-flex align-items-center mb-3">
<div class="rounded-circle bg-primary bg-opacity-10 p-3 me-3">
<i class="fas fa-paper-plane text-primary fa-lg"></i>
</div>
<div>
<h6 class="card-subtitle text-muted mb-1 small">إجمالي الصادر</h6>
<h3 class="card-title mb-0 fw-bold"><?= $total_outbound ?></h3>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body">
<div class="d-flex align-items-center mb-3">
<div class="rounded-circle bg-success bg-opacity-10 p-3 me-3">
<i class="fas fa-check-circle text-success fa-lg"></i>
</div>
<div>
<h6 class="card-subtitle text-muted mb-1 small">مكتمل / مرسل</h6>
<h3 class="card-title mb-0 fw-bold"><?= $completed_outbound ?></h3>
</div>
</div>
</div>
</div>
</div>
</div>
<?php if ($error): ?>
<div class="alert alert-danger shadow-sm border-0 mb-4"><?= $error ?></div>
<?php endif; ?>
<?php if ($success): ?>
<div class="alert alert-success shadow-sm border-0 mb-4"><?= $success ?></div>
<?php endif; ?>
<!-- Filter Bar -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-body p-3">
<form method="GET" class="row g-2 align-items-center">
<div class="col-md-5">
<div class="input-group">
<span class="input-group-text bg-white border-end-0"><i class="fas fa-search text-muted"></i></span>
<input type="text" name="search" class="form-control border-start-0" placeholder="بحث برقم القيد، الموضوع، أو الجهة..." value="<?= htmlspecialchars($_GET['search'] ?? '') ?>">
</div>
</div>
<div class="col-md-4">
<select name="status_id" class="form-select" onchange="this.form.submit()">
<option value="">جميع الحالات</option>
<?php foreach ($statuses as $status): ?>
<option value="<?= $status['id'] ?>" <?= (isset($_GET['status_id']) && $_GET['status_id'] == $status['id']) ? 'selected' : '' ?>><?= $status['name'] ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-3 text-end">
<button type="submit" class="btn btn-light w-100">تصفية</button>
</div>
</form>
</div>
</div>
<!-- Mails Table -->
<div class="card border-0 shadow-sm overflow-hidden">
<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="text-center pe-4">الإجراءات</th>
</tr>
</thead>
<tbody>
<?php if (empty($mails)): ?>
<tr>
<td colspan="6" class="text-center py-5 text-muted">
<i class="fas fa-paper-plane fa-3x mb-3 opacity-20"></i>
<p>لا يوجد بريد صادر حالياً.</p>
</td>
</tr>
<?php endif; ?>
<?php foreach ($mails as $mail): ?>
<tr>
<td class="ps-4"><span class="fw-bold text-primary"><?= htmlspecialchars($mail['ref_no']) ?></span></td>
<td><?= date('Y-m-d', strtotime($mail['date_registered'])) ?></td>
<td>
<div class="fw-semibold text-truncate" style="max-width: 300px;"><?= htmlspecialchars($mail['subject']) ?></div>
<?php if ($mail['attachment_names']): ?>
<span class="badge bg-light text-muted fw-normal" style="font-size: 0.65rem;">
<i class="fas fa-paperclip me-1"></i> <?= count(explode('|||', $mail['attachment_names'])) ?> مرفقات
</span>
<?php endif; ?>
</td>
<td><?= htmlspecialchars($mail['recipient']) ?></td>
<td>
<span class="badge rounded-pill" style="background-color: <?= $mail['status_color'] ?>20; color: <?= $mail['status_color'] ?>;">
<i class="fas fa-circle me-1 small"></i> <?= htmlspecialchars($mail['status_name']) ?>
</span>
</td>
<td class="text-center pe-4">
<div class="btn-group shadow-sm rounded">
<a href="view_mail.php?id=<?= $mail['id'] ?>&type=outbound" class="btn btn-sm btn-white text-primary border" title="عرض">
<i class="fas fa-eye"></i>
</a>
<a href="print_outbound.php?id=<?= $mail['id'] ?>" target="_blank" class="btn btn-sm btn-white text-secondary border" title="طباعة">
<i class="fas fa-print"></i>
</a>
<?php if (canEdit('outbound')): ?>
<button class="btn btn-sm btn-white text-warning border" onclick='openMailModal("edit", <?= json_encode($mail) ?>)' title="تعديل">
<i class="fas fa-edit"></i>
</button>
<?php endif; ?>
<?php if (canDelete('outbound')): ?>
<form method="POST" class="d-inline" onsubmit="return confirm('هل أنت متأكد من حذف هذا البريد؟');">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="<?= $mail['id'] ?>">
<button type="submit" class="btn btn-sm btn-white text-danger border rounded-0" title="حذف">
<i class="fas fa-trash"></i>
</button>
</form>
<?php endif; ?>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- Pagination -->
<?php renderPagination($page, $totalFiltered, $limit); ?>
</div>
</div>
<!-- Add/Edit 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 border-0 shadow">
<div class="modal-header bg-primary text-white py-3">
<h5 class="modal-title fw-bold" id="mailModalLabel">بريد صادر جديد</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="mailForm" method="POST" enctype="multipart/form-data">
<div class="modal-body p-4">
<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 small fw-bold">رقم القيد</label>
<input type="text" name="ref_no" id="modalRefNo" class="form-control" readonly required>
</div>
<div class="col-md-6">
<label class="form-label small fw-bold">تاريخ التسجيل</label>
<input type="date" name="date_registered" id="modalDateRegistered" class="form-control" required>
</div>
<div class="col-md-6">
<label class="form-label small fw-bold">الجهة الصادر منها (الداخلية)</label>
<input type="text" name="sender" id="modalSender" class="form-control" required placeholder="مثال: الشؤون القانونية">
</div>
<div class="col-md-6">
<label class="form-label small fw-bold">الجهة المستلمة (الخارجية)</label>
<input type="text" name="recipient" id="modalRecipient" class="form-control" required placeholder="مثال: البنك المركزي">
</div>
<div class="col-md-12">
<label class="form-label small fw-bold">الموضوع</label>
<input type="text" name="subject" id="modalSubject" class="form-control" required>
</div>
<div class="col-md-12">
<label class="form-label small fw-bold">محتوى الخطاب / الوصف</label>
<textarea name="description" id="description_editor" class="form-control" rows="5"></textarea>
</div>
<div class="col-md-6">
<label class="form-label small fw-bold">تاريخ الاستحقاق (اختياري)</label>
<input type="date" name="due_date" id="modalDueDate" class="form-control">
</div>
<div class="col-md-6">
<label class="form-label small fw-bold">الحالة</label>
<select name="status_id" id="modalStatusId" class="form-select" required>
<?php foreach ($statuses as $status): ?>
<option value="<?= $status['id'] ?>"><?= $status['name'] ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-12">
<label class="form-label small fw-bold">إسناد للمتابعة (اختياري)</label>
<select name="assigned_to" id="modalAssignedTo" class="form-select">
<option value="">--- اختر مستخدم ---</option>
<?php foreach ($users as $user): ?>
<option value="<?= $user['id'] ?>"><?= htmlspecialchars($user['full_name'] ?: $user['username']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-12 mt-4">
<div class="bg-light p-3 rounded">
<label class="form-label small fw-bold mb-2 d-block">المرفقات</label>
<input type="file" name="attachments[]" id="modalAttachmentsInput" class="form-control" multiple>
<div id="modalExistingAttachments" class="mt-2"></div>
<div id="modalSelectedAttachments" class="mt-1"></div>
</div>
</div>
</div>
</div>
<div class="modal-footer bg-light border-0">
<button type="button" class="btn btn-white border px-4" data-bs-dismiss="modal">إلغاء</button>
<button type="submit" class="btn btn-primary px-4">حفظ الصادر</button>
</div>
</form>
</div>
</div>
</div>
<style>
/* Custom Table Styles */
.table thead th {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.05rem;
font-weight: 700;
color: #6c757d;
border-top: none;
padding: 1rem 0.5rem;
}
.btn-white {
background-color: #fff;
color: #333;
}
.btn-white:hover {
background-color: #f8f9fa;
}
.badge {
padding: 0.5em 0.8em;
font-weight: 500;
}
.input-group-text {
color: #adb5bd;
}
</style>
<script>
let mailModal;
function initEditors() {
if (typeof tinymce === 'undefined') {
console.error('TinyMCE not loaded');
return Promise.resolve();
}
return tinymce.init({
selector: '#description_editor',
language: 'ar', language_url: 'https://cdn.jsdelivr.net/npm/tinymce-i18n@23.10.9/langs6/ar.js',
directionality: 'rtl',
height: 300,
plugins: 'advlist autolink lists link image charmap preview anchor searchreplace visualblocks code fullscreen insertdatetime media table help wordcount',
toolbar: 'undo redo | fontfamily fontsize | bold italic underline strikethrough | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat | help',
font_size_formats: '8pt 10pt 12pt 14pt 18pt 24pt 36pt',
promotion: false,
branding: false
});
}
function openMailModal(action, data = null) {
if (!mailModal) {
const modalEl = document.getElementById('mailModal');
if (typeof bootstrap !== 'undefined') {
mailModal = new bootstrap.Modal(modalEl);
} else {
console.error('Bootstrap not loaded');
return;
}
}
const label = document.getElementById('mailModalLabel');
const modalAction = document.getElementById('modalAction');
const modalId = document.getElementById('modalId');
const existingAttachmentsDiv = document.getElementById('modalExistingAttachments');
const selectedAttachmentsDiv = document.getElementById('modalSelectedAttachments');
const attachmentsInput = document.getElementById('modalAttachmentsInput');
const fields = {
ref_no: document.getElementById('modalRefNo'),
date_registered: document.getElementById('modalDateRegistered'),
due_date: document.getElementById('modalDueDate'),
sender: document.getElementById('modalSender'),
recipient: document.getElementById('modalRecipient'),
subject: document.getElementById('modalSubject'),
status_id: document.getElementById('modalStatusId'),
assigned_to: document.getElementById('modalAssignedTo')
};
modalAction.value = action;
existingAttachmentsDiv.innerHTML = '';
selectedAttachmentsDiv.innerHTML = '';
if (attachmentsInput) attachmentsInput.value = '';
if (action === 'add') {
label.textContent = 'إضافة بريد صادر جديد';
modalId.value = '0';
Object.keys(fields).forEach(key => {
if (fields[key]) {
if (key === 'date_registered') fields[key].value = '<?= date('Y-m-d') ?>';
else if (key === 'status_id') fields[key].value = '<?= $default_status_id ?>';
else if (key === 'ref_no') fields[key].value = '<?= generateRefNo('outbound') ?>';
else fields[key].value = '';
}
});
if (typeof tinymce !== 'undefined' && tinymce.get('description_editor')) {
tinymce.get('description_editor').setContent('');
} else {
document.getElementById('description_editor').value = '';
}
} else {
label.textContent = 'تعديل البريد الصادر';
modalId.value = data.id;
Object.keys(fields).forEach(key => {
if (fields[key]) fields[key].value = data[key] || '';
});
if (typeof tinymce !== 'undefined' && tinymce.get('description_editor')) {
tinymce.get('description_editor').setContent(data.description || '');
} else {
document.getElementById('description_editor').value = data.description || '';
}
// Display existing attachments
if (data.attachment_names) {
const names = data.attachment_names.split('|||');
let html = '<div class="mt-2"><p class="mb-1 fw-bold small">المرفقات الحالية:</p><ul class="list-unstyled small">';
names.forEach(name => {
html += `<li><i class="fas fa-file-alt me-1 text-muted"></i> ${name}</li>`;
});
html += '</ul></div>';
existingAttachmentsDiv.innerHTML = html;
}
}
mailModal.show();
}
document.addEventListener('DOMContentLoaded', function() {
initEditors().finally(() => {
<?php if ($deepLinkData): ?>
openMailModal('edit', <?= json_encode($deepLinkData) ?>);
<?php elseif ($error && isset($_POST['action'])): ?>
const errorData = <?= json_encode($_POST) ?>;
openMailModal(errorData.action, errorData);
<?php endif; ?>
});
// Handle file selection display
const attachmentsInput = document.getElementById('modalAttachmentsInput');
if (attachmentsInput) {
attachmentsInput.addEventListener('change', function() {
const fileList = this.files;
const selectedAttachmentsDiv = document.getElementById('modalSelectedAttachments');
selectedAttachmentsDiv.innerHTML = '';
if (fileList.length > 0) {
let html = '<div class="mt-2"><p class="mb-1 fw-bold small text-primary">المرفقات المختارة للرفع:</p><ul class="list-unstyled small">';
for (let i = 0; i < fileList.length; i++) {
const fileSize = (fileList[i].size / 1024).toFixed(1);
html += `<li><i class="fas fa-file-upload me-1 text-primary"></i> ${fileList[i].name} <span class="text-muted">(${fileSize} KB)</span></li>`;
}
html += '</ul></div>';
selectedAttachmentsDiv.innerHTML = html;
}
});
}
document.getElementById('mailForm').addEventListener('submit', function() {
if (typeof tinymce !== 'undefined') {
tinymce.triggerSave();
}
});
});
</script>
<?php require_once 'includes/footer.php'; ?>