One Hat Cyber Team
Your IP :
104.23.197.103
Server IP :
104.21.51.23
Server :
Linux 128-201-239-36.cprapid.com 3.10.0-1160.41.1.el7.x86_64 #1 SMP Tue Aug 31 14:52:47 UTC 2021 x86_64
Server Software :
Apache
PHP Version :
7.4.33
Buat File
|
Buat Folder
Eksekusi
Dir :
~
/
home
/
juscatamarca
/
www
/
campusjxj
/
public
/
admin
/
Edit File:
courses.php
<?php declare(strict_types=1); require_once dirname(__DIR__, 2) . '/config/app.php'; require_once ROOT_PATH . '/helpers/functions.php'; require_once ROOT_PATH . '/helpers/auth.php'; require_once ROOT_PATH . '/helpers/admin_courses.php'; require_role('admin'); $pageTitle = 'Cursos'; $navbarTitle = 'Gestión de cursos'; $currentPage = 'courses'; $validStatuses = ['draft', 'published', 'archived']; $errors = []; $formData = [ 'id' => '0', 'title' => '', 'description' => '', 'geographic_department_ids' => [], 'start_date' => '', 'end_date' => '', 'status' => 'draft', ]; $flashSuccess = get_flash('success'); $flashError = get_flash('error'); $editId = (int) ($_GET['edit'] ?? 0); if ($editId > 0 && $_SERVER['REQUEST_METHOD'] !== 'POST') { $editingCourse = get_admin_course_by_id($editId); if ($editingCourse === null) { set_flash('error', 'El curso seleccionado no existe.'); redirect('admin/courses.php'); } $formData = [ 'id' => (string) ($editingCourse['id'] ?? 0), 'title' => (string) ($editingCourse['title'] ?? ''), 'description' => (string) ($editingCourse['description'] ?? ''), 'geographic_department_ids' => array_filter( array_map( 'intval', explode(',', (string) ($editingCourse['geographic_department_ids_csv'] ?? '')) ), static function (int $id): bool { return $id > 0; } ), 'start_date' => (string) ($editingCourse['start_date'] ?? ''), 'end_date' => (string) ($editingCourse['end_date'] ?? ''), 'status' => (string) ($editingCourse['status'] ?? 'draft'), ]; if (empty($formData['geographic_department_ids']) && !empty($editingCourse['geographic_department_id'])) { $formData['geographic_department_ids'] = [(int) $editingCourse['geographic_department_id']]; } } if ($_SERVER['REQUEST_METHOD'] === 'POST') { $action = (string) ($_POST['action'] ?? 'create'); $courseId = (int) ($_POST['id'] ?? 0); if ($action === 'delete') { if ($courseId <= 0 || !admin_course_exists($courseId)) { set_flash('error', 'El curso seleccionado no existe.'); redirect('admin/courses.php'); } try { delete_admin_course($courseId); set_flash('success', 'Curso eliminado correctamente.'); redirect('admin/courses.php'); } catch (Throwable $e) { set_flash('error', 'No se pudo eliminar el curso.'); redirect('admin/courses.php'); } } $formData = [ 'id' => (string) $courseId, 'title' => trim((string) ($_POST['title'] ?? '')), 'description' => trim((string) ($_POST['description'] ?? '')), 'geographic_department_ids' => array_values( array_filter( array_map('intval', (array) ($_POST['geographic_department_ids'] ?? [])), static function (int $id): bool { return $id > 0; } ) ), 'start_date' => trim((string) ($_POST['start_date'] ?? '')), 'end_date' => trim((string) ($_POST['end_date'] ?? '')), 'status' => trim((string) ($_POST['status'] ?? 'draft')), ]; if ($formData['title'] === '') { $errors[] = 'El título del curso es obligatorio.'; } if (strlen($formData['title']) > 255) { $errors[] = 'El título no puede superar 255 caracteres.'; } foreach ($formData['geographic_department_ids'] as $geoDepartmentId) { if (!admin_geo_department_exists((int) $geoDepartmentId)) { $errors[] = 'Uno o más departamentos de Catamarca seleccionados no son válidos.'; break; } } if (!in_array($formData['status'], $validStatuses, true)) { $errors[] = 'El estado seleccionado no es válido.'; } if ($formData['start_date'] !== '' && !strtotime($formData['start_date'])) { $errors[] = 'La fecha de inicio no tiene un formato válido.'; } if ($formData['end_date'] !== '' && !strtotime($formData['end_date'])) { $errors[] = 'La fecha de fin no tiene un formato válido.'; } if (empty($errors)) { $payload = [ 'title' => $formData['title'], 'description' => $formData['description'], 'geographic_department_ids' => $formData['geographic_department_ids'], 'start_date' => $formData['start_date'], 'end_date' => $formData['end_date'], 'status' => $formData['status'], ]; try { if ($action === 'update' && $courseId > 0) { update_admin_course($courseId, $payload); set_flash('success', 'Curso actualizado correctamente.'); redirect('admin/courses.php'); } create_admin_course($payload); set_flash('success', 'Curso creado correctamente.'); redirect('admin/courses.php'); } catch (Throwable $e) { $errors[] = 'No se pudo guardar el curso en este momento.'; } } } $search = trim((string) ($_GET['q'] ?? '')); $statusFilter = trim((string) ($_GET['status'] ?? 'all')); $statusParam = in_array($statusFilter, $validStatuses, true) ? $statusFilter : null; $courseRows = []; $listError = null; $geoDepartmentOptions = []; try { $courseRows = get_admin_courses($search !== '' ? $search : null, $statusParam); $geoDepartmentOptions = get_geo_department_options(); } catch (Throwable $e) { $listError = 'No se pudo cargar la información de cursos.'; } $isEditMode = (int) $formData['id'] > 0; include ROOT_PATH . '/includes/layout/header.php'; ?> <?php include ROOT_PATH . '/includes/layout/sidebar_admin.php'; ?> <div class="main-panel"> <?php include ROOT_PATH . '/includes/layout/navbar.php'; ?> <main class="content-area"> <section class="hero-panel mb-4"> <div> <p class="hero-tag mb-2">Administración Académica</p> <h2 class="h3 fw-bold mb-2">Gestión de Cursos</h2> <p class="mb-0 text-muted">Crea y administra los cursos vinculados a los departamentos de Catamarca.</p> </div> </section> <?php if ($flashSuccess): ?> <div class="alert alert-success" role="alert"><?= e($flashSuccess) ?></div> <?php endif; ?> <?php if ($flashError): ?> <div class="alert alert-danger" role="alert"><?= e($flashError) ?></div> <?php endif; ?> <?php if (!empty($errors)): ?> <div class="alert alert-danger" role="alert"> <strong>Revisa el formulario:</strong> <ul class="mb-0 mt-2"> <?php foreach ($errors as $error): ?> <li><?= e($error) ?></li> <?php endforeach; ?> </ul> </div> <?php endif; ?> <div class="card card-campus mb-4"> <div class="card-header-campus"> <h3 class="h6 mb-0"><?= $isEditMode ? 'Editar curso' : 'Crear nuevo curso' ?></h3> <?php if ($isEditMode): ?> <a href="<?= e(base_url('admin/courses.php')) ?>" class="btn btn-sm btn-outline-success">Cancelar edición</a> <?php endif; ?> </div> <div class="card-body"> <form class="row g-3" method="post" novalidate> <input type="hidden" name="action" value="<?= $isEditMode ? 'update' : 'create' ?>"> <input type="hidden" name="id" value="<?= e($formData['id']) ?>"> <div class="col-md-6"> <label class="form-label">Título</label> <input type="text" class="form-control" name="title" placeholder="Ej: Introducción a Excel" maxlength="255" required value="<?= e($formData['title']) ?>"> </div> <div class="col-md-6"> <label class="form-label">Departamentos de Catamarca</label> <div class="course-geo-picker"> <div class="d-flex gap-2 mb-2"> <input type="text" class="form-control" id="geoDepartmentSearch" placeholder="Buscar departamento..."> <button type="button" class="btn btn-outline-success" id="geoDepartmentClear">Limpiar</button> </div> <div class="course-geo-toolbar mb-2"> <small class="text-muted" id="geoDepartmentCount">0 departamentos seleccionados</small> </div> <div class="course-geo-list" id="geoDepartmentList"> <?php foreach ($geoDepartmentOptions as $geoDept): ?> <?php $isSelectedGeo = in_array((int) $geoDept['id'], $formData['geographic_department_ids'], true); ?> <label class="course-geo-item <?= $isSelectedGeo ? 'selected' : '' ?>"> <input class="form-check-input" type="checkbox" name="geographic_department_ids[]" value="<?= e((string) $geoDept['id']) ?>" <?= $isSelectedGeo ? 'checked' : '' ?> > <span class="course-geo-name"><?= e((string) $geoDept['name']) ?></span> </label> <?php endforeach; ?> </div> </div> <small class="text-muted">Seleccioná con clic simple, sin usar Ctrl/Cmd.</small> </div> <div class="col-md-6"> <label class="form-label">Estado</label> <select class="form-select" name="status" required> <option value="draft" <?= $formData['status'] === 'draft' ? 'selected' : '' ?>>Borrador</option> <option value="published" <?= $formData['status'] === 'published' ? 'selected' : '' ?>>Publicado</option> <option value="archived" <?= $formData['status'] === 'archived' ? 'selected' : '' ?>>Archivado</option> </select> </div> <div class="col-12"> <label class="form-label">Descripción</label> <textarea class="form-control" rows="3" name="description" placeholder="Descripción general del curso..."><?= e($formData['description']) ?></textarea> </div> <div class="col-md-6"> <label class="form-label">Fecha de inicio (opcional)</label> <input type="date" class="form-control" name="start_date" value="<?= e($formData['start_date']) ?>"> </div> <div class="col-md-6"> <label class="form-label">Fecha de fin (opcional)</label> <input type="date" class="form-control" name="end_date" value="<?= e($formData['end_date']) ?>"> </div> <div class="col-12 text-end"> <button type="submit" class="btn btn-campus"> <i class="fa-solid fa-floppy-disk me-1"></i><?= $isEditMode ? 'Actualizar curso' : 'Crear curso' ?> </button> </div> </form> </div> </div> <div class="card card-campus"> <div class="card-header-campus"> <h3 class="h6 mb-0">Listado de cursos</h3> </div> <div class="card-body border-bottom"> <form method="get" class="row g-2 align-items-end"> <div class="col-12 col-md-7"> <label class="form-label mb-1">Buscar</label> <input type="text" class="form-control" name="q" value="<?= e($search) ?>" placeholder="Título de curso o departamento"> </div> <div class="col-12 col-md-3"> <label class="form-label mb-1">Estado</label> <select class="form-select" name="status"> <option value="all" <?= $statusFilter === 'all' ? 'selected' : '' ?>>Todos</option> <option value="draft" <?= $statusFilter === 'draft' ? 'selected' : '' ?>>Borrador</option> <option value="published" <?= $statusFilter === 'published' ? 'selected' : '' ?>>Publicado</option> <option value="archived" <?= $statusFilter === 'archived' ? 'selected' : '' ?>>Archivado</option> </select> </div> <div class="col-12 col-md-2 d-flex gap-2"> <button type="submit" class="btn btn-campus flex-grow-1">Filtrar</button> <a href="<?= e(base_url('admin/courses.php')) ?>" class="btn btn-outline-success">Limpiar</a> </div> </form> </div> <?php if ($listError !== null): ?> <div class="card-body"> <div class="alert alert-danger mb-0" role="alert"><?= e($listError) ?></div> </div> <?php elseif (empty($courseRows)): ?> <div class="card-body text-center py-5"> <i class="fa-solid fa-inbox mb-3" style="font-size: 2rem; color: var(--campus-gray-500); opacity: 0.5"></i> <p class="mb-0 text-muted">No hay cursos cargados para los filtros seleccionados.</p> </div> <?php else: ?> <div class="table-responsive"> <table class="table align-middle mb-0"> <thead> <tr> <th>Título</th> <th>Departamento</th> <th>Clases</th> <th>Estado</th> <th class="text-end">Acciones</th> </tr> </thead> <tbody> <?php foreach ($courseRows as $course): ?> <?php $statusBadge = admin_course_status_badge((string) ($course['status'] ?? '')); ?> <tr> <td><?= e((string) ($course['title'] ?? '')) ?></td> <td><?= e((string) ($course['geographic_department_names'] ?? $course['geographic_department_name'] ?? 'Sin asignar')) ?></td> <td><?= e((string) ($course['class_count'] ?? 0)) ?></td> <td><span class="badge <?= e($statusBadge['class']) ?>"><?= e($statusBadge['label']) ?></span></td> <td class="text-end"> <a href="<?= e(base_url('admin/courses.php?edit=' . (int) $course['id'])) ?>" class="btn btn-outline-success btn-sm"> <i class="fa-solid fa-pen-to-square me-1"></i>Editar </a> <form method="post" style="display: inline;" onsubmit="return confirm('¿Estás seguro de que deseas eliminar este curso? Esta acción no se puede deshacer.');"> <input type="hidden" name="action" value="delete"> <input type="hidden" name="id" value="<?= (int) $course['id'] ?>"> <button type="submit" class="btn btn-outline-danger btn-sm"> <i class="fa-solid fa-trash me-1"></i>Eliminar </button> </form> </td> </tr> <?php endforeach; ?> </tbody> </table> </div> <?php endif; ?> </div> </main> </div> <script> (() => { const searchInput = document.getElementById('geoDepartmentSearch'); const clearButton = document.getElementById('geoDepartmentClear'); const list = document.getElementById('geoDepartmentList'); const count = document.getElementById('geoDepartmentCount'); if (!searchInput || !clearButton || !list || !count) { return; } const items = Array.from(list.querySelectorAll('.course-geo-item')); const checkboxes = Array.from(list.querySelectorAll('input[type="checkbox"]')); const refreshSelectionState = () => { let selectedCount = 0; items.forEach((item) => { const checkbox = item.querySelector('input[type="checkbox"]'); const isChecked = checkbox && checkbox.checked; item.classList.toggle('selected', Boolean(isChecked)); if (isChecked) { selectedCount += 1; } }); count.textContent = selectedCount === 1 ? '1 departamento seleccionado' : `${selectedCount} departamentos seleccionados`; clearButton.disabled = selectedCount === 0; }; const filterItems = () => { const query = searchInput.value.trim().toLowerCase(); items.forEach((item) => { const nameElement = item.querySelector('.course-geo-name'); const name = nameElement ? nameElement.textContent.toLowerCase() : ''; item.style.display = query === '' || name.includes(query) ? 'flex' : 'none'; }); }; checkboxes.forEach((checkbox) => { checkbox.addEventListener('change', refreshSelectionState); }); searchInput.addEventListener('input', filterItems); clearButton.addEventListener('click', () => { checkboxes.forEach((checkbox) => { checkbox.checked = false; }); refreshSelectionState(); }); refreshSelectionState(); })(); </script> <?php include ROOT_PATH . '/includes/layout/footer.php'; ?>
Simpan