juridico
This commit is contained in:
commit
06a5f64fb2
50
juridico/actions/document_download.php
Normal file
50
juridico/actions/document_download.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../config/database.php';
|
||||
require_once '../includes/auth.php';
|
||||
|
||||
checkLogin();
|
||||
|
||||
if (isset($_GET['id'])) {
|
||||
$id = $_GET['id'];
|
||||
|
||||
try {
|
||||
$stmt = $db->prepare("SELECT file_path, title FROM documents WHERE id = :id");
|
||||
$stmt->execute(['id' => $id]);
|
||||
$doc = $stmt->fetch();
|
||||
|
||||
if ($doc) {
|
||||
$file_path = '../uploads/' . $doc['file_path'];
|
||||
|
||||
if (file_exists($file_path)) {
|
||||
$ext = pathinfo($doc['file_path'], PATHINFO_EXTENSION);
|
||||
$download_name = preg_replace('/[^a-zA-Z0-9_-]/', '_', $doc['title']) . '.' . $ext;
|
||||
|
||||
header('Content-Description: File Transfer');
|
||||
header('Content-Type: application/octet-stream');
|
||||
header('Content-Disposition: attachment; filename="' . $download_name . '"');
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate');
|
||||
header('Pragma: public');
|
||||
header('Content-Length: ' . filesize($file_path));
|
||||
|
||||
ob_clean();
|
||||
flush();
|
||||
readfile($file_path);
|
||||
exit;
|
||||
} else {
|
||||
$_SESSION['error'] = 'El archivo físico no se encuentra en el servidor.';
|
||||
}
|
||||
} else {
|
||||
$_SESSION['error'] = 'Documento no encontrado.';
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$_SESSION['error'] = 'Error al consultar la base de datos.';
|
||||
}
|
||||
} else {
|
||||
$_SESSION['error'] = 'ID de documento no proporcionado.';
|
||||
}
|
||||
|
||||
header('Location: ../documents.php');
|
||||
exit;
|
||||
?>
|
||||
56
juridico/actions/document_upload.php
Normal file
56
juridico/actions/document_upload.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../config/database.php';
|
||||
require_once '../includes/auth.php';
|
||||
|
||||
requireRole(['superadmin', 'admin', 'supervisor']);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$type = $_POST['type'];
|
||||
$reference_number = trim($_POST['reference_number']);
|
||||
$title = trim($_POST['title']);
|
||||
$description = trim($_POST['description']);
|
||||
$uploaded_by = $_SESSION['user_id'];
|
||||
|
||||
if (isset($_FILES['document_file']) && $_FILES['document_file']['error'] === UPLOAD_ERR_OK) {
|
||||
$file = $_FILES['document_file'];
|
||||
|
||||
// Crear nombre único para el archivo
|
||||
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
|
||||
$filename = uniqid('doc_') . '_' . time() . '.' . $ext;
|
||||
$upload_dir = '../uploads/';
|
||||
|
||||
// Asegurarse de que el directorio exista
|
||||
if (!is_dir($upload_dir)) {
|
||||
mkdir($upload_dir, 0777, true);
|
||||
}
|
||||
|
||||
$dest_path = $upload_dir . $filename;
|
||||
|
||||
if (move_uploaded_file($file['tmp_name'], $dest_path)) {
|
||||
try {
|
||||
$stmt = $db->prepare("INSERT INTO documents (reference_number, title, description, type, file_path, uploaded_by) VALUES (:ref, :title, :desc, :type, :path, :user_id)");
|
||||
$stmt->execute([
|
||||
'ref' => $reference_number,
|
||||
'title' => $title,
|
||||
'desc' => $description,
|
||||
'type' => $type,
|
||||
'path' => $filename,
|
||||
'user_id' => $uploaded_by
|
||||
]);
|
||||
$_SESSION['success'] = 'Documento registrado y subido correctamente.';
|
||||
} catch (PDOException $e) {
|
||||
$_SESSION['error'] = 'Error al registrar en la base de datos.';
|
||||
if (file_exists($dest_path)) unlink($dest_path);
|
||||
}
|
||||
} else {
|
||||
$_SESSION['error'] = 'Error al mover el archivo subido al servidor.';
|
||||
}
|
||||
} else {
|
||||
$_SESSION['error'] = 'Debe seleccionar un archivo válido.';
|
||||
}
|
||||
|
||||
header('Location: ../documents.php');
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
35
juridico/actions/login_action.php
Normal file
35
juridico/actions/login_action.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../config/database.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = trim($_POST['username']);
|
||||
$password = $_POST['password'];
|
||||
|
||||
try {
|
||||
$stmt = $db->prepare("SELECT * FROM users WHERE username = :username");
|
||||
$stmt->execute(['username' => $username]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['user_name'] = $user['name'];
|
||||
$_SESSION['user_role'] = $user['role'];
|
||||
|
||||
header('Location: ../dashboard.php');
|
||||
exit;
|
||||
} else {
|
||||
$_SESSION['error'] = 'Usuario o contraseña incorrectos.';
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$_SESSION['error'] = 'Error del sistema. Contacte al administrador.';
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
header('Location: ../index.php');
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
40
juridico/actions/user_create.php
Normal file
40
juridico/actions/user_create.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../config/database.php';
|
||||
require_once '../includes/auth.php';
|
||||
|
||||
requireRole(['superadmin', 'admin']);
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$name = trim($_POST['name']);
|
||||
$username = trim($_POST['username']);
|
||||
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);
|
||||
$role = $_POST['role'];
|
||||
|
||||
// Validar rol según permisos
|
||||
if (!hasRole('superadmin') && in_array($role, ['admin', 'superadmin'])) {
|
||||
$_SESSION['error'] = 'No tienes permisos para crear este tipo de usuario.';
|
||||
header('Location: ../users.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = $db->prepare("INSERT INTO users (username, password, name, role) VALUES (:username, :password, :name, :role)");
|
||||
$stmt->execute([
|
||||
'username' => $username,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
'role' => $role
|
||||
]);
|
||||
$_SESSION['success'] = 'Usuario creado correctamente.';
|
||||
} catch (PDOException $e) {
|
||||
if ($e->getCode() == 23000) { // UNIQUE constraint failed
|
||||
$_SESSION['error'] = 'El nombre de usuario ya existe.';
|
||||
} else {
|
||||
$_SESSION['error'] = 'Error al crear el usuario. Código: ' . $e->getCode();
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: ../users.php');
|
||||
exit;
|
||||
}
|
||||
11
juridico/config/database.php
Normal file
11
juridico/config/database.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
$dbFile = __DIR__ . '/../database/juridico.sqlite';
|
||||
|
||||
try {
|
||||
$db = new PDO('sqlite:' . $dbFile);
|
||||
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
die("Error de conexión a la base de datos: " . $e->getMessage());
|
||||
}
|
||||
?>
|
||||
156
juridico/dashboard.php
Normal file
156
juridico/dashboard.php
Normal file
@ -0,0 +1,156 @@
|
||||
<?php
|
||||
require_once 'config/database.php';
|
||||
require_once 'includes/header.php';
|
||||
|
||||
// Obtener estadísticas
|
||||
$stats = [
|
||||
'total' => 0,
|
||||
'entradas' => 0,
|
||||
'salidas' => 0,
|
||||
'archivos' => 0,
|
||||
'usuarios' => 0
|
||||
];
|
||||
|
||||
try {
|
||||
$stmt = $db->query("SELECT type, COUNT(*) as count FROM documents GROUP BY type");
|
||||
while ($row = $stmt->fetch()) {
|
||||
if ($row['type'] == 'entrada') $stats['entradas'] = $row['count'];
|
||||
if ($row['type'] == 'salida') $stats['salidas'] = $row['count'];
|
||||
if ($row['type'] == 'archivo_interno') $stats['archivos'] = $row['count'];
|
||||
$stats['total'] += $row['count'];
|
||||
}
|
||||
|
||||
$stmt = $db->query("SELECT COUNT(*) FROM users");
|
||||
$stats['usuarios'] = $stmt->fetchColumn();
|
||||
|
||||
// Documentos recientes
|
||||
$stmt = $db->query("SELECT d.*, u.name as user_name FROM documents d LEFT JOIN users u ON d.uploaded_by = u.id ORDER BY d.created_at DESC LIMIT 5");
|
||||
$recent_docs = $stmt->fetchAll();
|
||||
} catch (PDOException $e) {
|
||||
$error = "Error al cargar las estadísticas.";
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="mb-8 animate-fade-in">
|
||||
<h1 class="text-3xl font-bold text-slate-800 tracking-tight">Panel de Control</h1>
|
||||
<p class="text-slate-500 mt-2">Resumen de la actividad del departamento de Consultoría Jurídica.</p>
|
||||
</div>
|
||||
|
||||
<?php if(isset($error)): ?>
|
||||
<div class="bg-red-50 border-l-4 border-red-500 p-4 mb-8 rounded-r-lg shadow-sm">
|
||||
<p class="text-sm text-red-700 font-medium"><?= $error ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Stats Grid -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8 animate-fade-in-delayed">
|
||||
<!-- Total Documentos -->
|
||||
<div class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-sm font-bold text-slate-500 uppercase tracking-wider">Total Registros</h3>
|
||||
<div class="w-10 h-10 rounded-full bg-blue-50 flex items-center justify-center">
|
||||
<i class="fas fa-folder-open text-blue-600 text-lg"></i>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-3xl font-bold text-slate-800"><?= $stats['total'] ?></p>
|
||||
</div>
|
||||
|
||||
<!-- Entradas -->
|
||||
<div class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-sm font-bold text-slate-500 uppercase tracking-wider">Oficios Entrada</h3>
|
||||
<div class="w-10 h-10 rounded-full bg-emerald-50 flex items-center justify-center">
|
||||
<i class="fas fa-inbox text-emerald-600 text-lg"></i>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-3xl font-bold text-slate-800"><?= $stats['entradas'] ?></p>
|
||||
</div>
|
||||
|
||||
<!-- Salidas -->
|
||||
<div class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-sm font-bold text-slate-500 uppercase tracking-wider">Oficios Salida</h3>
|
||||
<div class="w-10 h-10 rounded-full bg-amber-50 flex items-center justify-center">
|
||||
<i class="fas fa-paper-plane text-amber-600 text-lg"></i>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-3xl font-bold text-slate-800"><?= $stats['salidas'] ?></p>
|
||||
</div>
|
||||
|
||||
<!-- Usuarios -->
|
||||
<div class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100 hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-sm font-bold text-slate-500 uppercase tracking-wider">Usuarios Activos</h3>
|
||||
<div class="w-10 h-10 rounded-full bg-purple-50 flex items-center justify-center">
|
||||
<i class="fas fa-users text-purple-600 text-lg"></i>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-3xl font-bold text-slate-800"><?= $stats['usuarios'] ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actividad Reciente -->
|
||||
<div class="bg-white rounded-2xl shadow-sm border border-slate-100 overflow-hidden animate-fade-in-delayed">
|
||||
<div class="px-6 py-5 border-b border-slate-100 bg-slate-50/50">
|
||||
<h3 class="text-lg font-bold text-slate-800">Documentos Recientes</h3>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider bg-slate-50">Referencia / Expediente</th>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider bg-slate-50">Asunto</th>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider bg-slate-50">Tipo</th>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider bg-slate-50">Fecha Registro</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
<?php if(empty($recent_docs)): ?>
|
||||
<tr>
|
||||
<td colspan="4" class="px-6 py-8 text-center text-slate-500 font-medium">No hay documentos registrados aún.</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach($recent_docs as $doc): ?>
|
||||
<tr class="hover:bg-slate-50 transition-colors">
|
||||
<td class="px-6 py-4">
|
||||
<span class="inline-flex items-center gap-2 font-semibold text-slate-800">
|
||||
<i class="fas fa-file-alt text-accent"></i>
|
||||
<?= htmlspecialchars($doc['reference_number']) ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<p class="text-sm text-slate-800 font-medium line-clamp-1"><?= htmlspecialchars($doc['title']) ?></p>
|
||||
<p class="text-xs text-slate-500 mt-1">Por: <?= htmlspecialchars($doc['user_name'] ?? 'Sistema') ?></p>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<?php
|
||||
$typeClasses = [
|
||||
'entrada' => 'bg-emerald-100 text-emerald-800 border-emerald-200',
|
||||
'salida' => 'bg-amber-100 text-amber-800 border-amber-200',
|
||||
'archivo_interno' => 'bg-slate-100 text-slate-800 border-slate-200'
|
||||
];
|
||||
$typeLabels = [
|
||||
'entrada' => 'Entrada',
|
||||
'salida' => 'Salida',
|
||||
'archivo_interno' => 'Archivo Interno'
|
||||
];
|
||||
$class = $typeClasses[$doc['type']] ?? 'bg-gray-100 text-gray-800 border-gray-200';
|
||||
$label = $typeLabels[$doc['type']] ?? ucfirst($doc['type']);
|
||||
?>
|
||||
<span class="px-3 py-1 rounded-full text-[10px] font-bold border <?= $class ?> uppercase tracking-wider">
|
||||
<?= $label ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-600 font-medium">
|
||||
<?= date('d/m/Y H:i', strtotime($doc['created_at'])) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once 'includes/footer.php'; ?>
|
||||
BIN
juridico/database/juridico.sqlite
Normal file
BIN
juridico/database/juridico.sqlite
Normal file
Binary file not shown.
104
juridico/document_add.php
Normal file
104
juridico/document_add.php
Normal file
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
require_once 'includes/header.php';
|
||||
requireRole(['superadmin', 'admin', 'supervisor']);
|
||||
?>
|
||||
|
||||
<div class="mb-8 animate-fade-in">
|
||||
<div class="flex items-center gap-3">
|
||||
<a href="documents.php" class="w-10 h-10 rounded-full bg-slate-200 hover:bg-slate-300 text-slate-600 flex items-center justify-center transition-colors">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
</a>
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-slate-800 tracking-tight">Registrar Documento</h1>
|
||||
<p class="text-slate-500 mt-1">Ingreso de nuevos oficios o expedientes al archivo digital.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-3xl shadow-lg border border-slate-100 max-w-4xl animate-fade-in-delayed overflow-hidden">
|
||||
<div class="h-2 bg-gradient-to-r from-primary via-blue-800 to-accent"></div>
|
||||
<form action="actions/document_upload.php" method="POST" enctype="multipart/form-data" class="p-8">
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<!-- Columna Izquierda -->
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-slate-600 uppercase tracking-wider mb-2">
|
||||
Clasificación del Documento <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<select name="type" required class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:ring-accent focus:border-accent bg-slate-50 text-sm font-medium shadow-inner transition-colors">
|
||||
<option value="" disabled selected>Seleccione el tipo...</option>
|
||||
<option value="entrada">Oficio de Entrada (Recibido)</option>
|
||||
<option value="salida">Oficio de Salida (Emitido)</option>
|
||||
<option value="archivo_interno">Archivo Interno / Expediente</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-slate-600 uppercase tracking-wider mb-2">
|
||||
N° de Referencia o Expediente <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<input type="text" name="reference_number" required placeholder="Ej: CJ-2026-001"
|
||||
class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:ring-accent focus:border-accent bg-slate-50 text-sm font-medium shadow-inner transition-colors">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-slate-600 uppercase tracking-wider mb-2">
|
||||
Asunto / Título <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<input type="text" name="title" required placeholder="Breve descripción del asunto..."
|
||||
class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:ring-accent focus:border-accent bg-slate-50 text-sm font-medium shadow-inner transition-colors">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Columna Derecha -->
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-slate-600 uppercase tracking-wider mb-2">
|
||||
Descripción Detallada
|
||||
</label>
|
||||
<textarea name="description" rows="4" placeholder="Observaciones adicionales, partes involucradas, etc..."
|
||||
class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:ring-accent focus:border-accent bg-slate-50 text-sm font-medium shadow-inner transition-colors resize-none"></textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-slate-600 uppercase tracking-wider mb-2">
|
||||
Archivo Digital (PDF, DOCX) <span class="text-red-500">*</span>
|
||||
</label>
|
||||
<div class="mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-slate-300 border-dashed rounded-xl bg-slate-50 hover:bg-slate-100 transition-colors cursor-pointer" onclick="document.getElementById('file-upload').click()">
|
||||
<div class="space-y-2 text-center">
|
||||
<i class="fas fa-cloud-upload-alt text-4xl text-slate-400"></i>
|
||||
<div class="flex text-sm text-slate-600 justify-center">
|
||||
<label for="file-upload" class="relative cursor-pointer bg-white rounded-md font-bold text-accent hover:text-orange-800 focus-within:outline-none px-2 py-1 shadow-sm">
|
||||
<span>Seleccionar archivo</span>
|
||||
<input id="file-upload" name="document_file" type="file" class="sr-only" required accept=".pdf,.doc,.docx,.jpg,.png">
|
||||
</label>
|
||||
</div>
|
||||
<p class="text-xs text-slate-500 font-medium" id="file-name">PDF, Word o Imágenes hasta 10MB</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 pt-6 border-t border-slate-100 flex justify-end gap-4">
|
||||
<a href="documents.php" class="px-6 py-3 border border-slate-300 text-slate-700 font-bold text-sm uppercase tracking-wider rounded-xl hover:bg-slate-50 transition-colors">
|
||||
Cancelar
|
||||
</a>
|
||||
<button type="submit" class="px-8 py-3 bg-primary hover:bg-slate-800 text-white font-bold text-sm uppercase tracking-wider rounded-xl transition-all shadow-lg hover:shadow-xl hover:-translate-y-0.5 flex items-center gap-2">
|
||||
<i class="fas fa-save"></i> Guardar Registro
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('file-upload').addEventListener('change', function(e) {
|
||||
if(e.target.files.length > 0) {
|
||||
document.getElementById('file-name').textContent = e.target.files[0].name;
|
||||
document.getElementById('file-name').classList.add('text-primary', 'font-bold');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php require_once 'includes/footer.php'; ?>
|
||||
171
juridico/documents.php
Normal file
171
juridico/documents.php
Normal file
@ -0,0 +1,171 @@
|
||||
<?php
|
||||
require_once 'config/database.php';
|
||||
require_once 'includes/header.php';
|
||||
|
||||
// Filtros básicos
|
||||
$type_filter = $_GET['type'] ?? '';
|
||||
$search = $_GET['search'] ?? '';
|
||||
|
||||
try {
|
||||
$query = "SELECT d.*, u.name as user_name FROM documents d LEFT JOIN users u ON d.uploaded_by = u.id WHERE 1=1";
|
||||
$params = [];
|
||||
|
||||
if ($type_filter) {
|
||||
$query .= " AND d.type = :type";
|
||||
$params['type'] = $type_filter;
|
||||
}
|
||||
|
||||
if ($search) {
|
||||
$query .= " AND (d.reference_number LIKE :search OR d.title LIKE :search OR d.description LIKE :search)";
|
||||
$params['search'] = "%$search%";
|
||||
}
|
||||
|
||||
$query .= " ORDER BY d.created_at DESC";
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute($params);
|
||||
$documents = $stmt->fetchAll();
|
||||
} catch (PDOException $e) {
|
||||
$error = "Error al cargar los documentos.";
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="mb-8 flex flex-col md:flex-row md:justify-between md:items-end gap-4 animate-fade-in">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-slate-800 tracking-tight">Archivos y Expedientes</h1>
|
||||
<p class="text-slate-500 mt-2">Consulta y gestiona el archivo digital de la dirección.</p>
|
||||
</div>
|
||||
|
||||
<?php if(hasRole(['superadmin', 'admin', 'supervisor'])): ?>
|
||||
<a href="document_add.php" class="bg-primary hover:bg-slate-800 text-white px-6 py-2.5 rounded-xl font-bold text-sm tracking-wider uppercase transition-all shadow-lg hover:shadow-xl hover:-translate-y-0.5 flex items-center justify-center gap-2">
|
||||
<i class="fas fa-file-upload"></i> Registrar Oficio
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Filtros -->
|
||||
<div class="bg-white p-4 rounded-2xl shadow-sm border border-slate-100 mb-6 animate-fade-in-delayed">
|
||||
<form method="GET" class="flex flex-col md:flex-row gap-4">
|
||||
<div class="flex-1">
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
|
||||
<i class="fas fa-search text-slate-400"></i>
|
||||
</div>
|
||||
<input type="text" name="search" value="<?= htmlspecialchars($search) ?>" placeholder="Buscar por referencia, asunto o descripción..."
|
||||
class="block w-full pl-11 pr-3 py-2.5 border border-slate-300 rounded-xl focus:ring-accent focus:border-accent text-sm bg-slate-50 text-slate-800 transition-colors shadow-inner font-medium">
|
||||
</div>
|
||||
</div>
|
||||
<div class="md:w-56">
|
||||
<select name="type" class="block w-full px-4 py-2.5 border border-slate-300 rounded-xl focus:ring-accent focus:border-accent text-sm bg-slate-50 text-slate-800 transition-colors shadow-inner font-medium">
|
||||
<option value="">Todos los tipos</option>
|
||||
<option value="entrada" <?= $type_filter == 'entrada' ? 'selected' : '' ?>>Entrada (Recibidos)</option>
|
||||
<option value="salida" <?= $type_filter == 'salida' ? 'selected' : '' ?>>Salida (Emitidos)</option>
|
||||
<option value="archivo_interno" <?= $type_filter == 'archivo_interno' ? 'selected' : '' ?>>Archivo Interno</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="px-6 py-2.5 bg-slate-800 hover:bg-slate-900 text-white font-bold text-sm uppercase tracking-wider rounded-xl transition-colors shadow-md">
|
||||
Filtrar
|
||||
</button>
|
||||
<?php if($search || $type_filter): ?>
|
||||
<a href="documents.php" class="px-6 py-2.5 bg-red-50 hover:bg-red-100 text-red-600 font-bold text-sm uppercase tracking-wider rounded-xl transition-colors border border-red-200 text-center">
|
||||
Limpiar
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php if (isset($_SESSION['success'])): ?>
|
||||
<div class="bg-emerald-50 border-l-4 border-emerald-500 p-4 mb-6 rounded-r-lg shadow-sm">
|
||||
<p class="text-sm text-emerald-700 font-medium"><?= htmlspecialchars($_SESSION['success']); unset($_SESSION['success']); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($_SESSION['error'])): ?>
|
||||
<div class="bg-red-50 border-l-4 border-red-500 p-4 mb-6 rounded-r-lg shadow-sm">
|
||||
<p class="text-sm text-red-700 font-medium"><?= htmlspecialchars($_SESSION['error']); unset($_SESSION['error']); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-sm border border-slate-100 overflow-hidden animate-fade-in-delayed">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider bg-slate-50 border-b border-slate-100">Referencia / Expediente</th>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider bg-slate-50 border-b border-slate-100">Asunto</th>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider bg-slate-50 border-b border-slate-100">Clasificación</th>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider bg-slate-50 border-b border-slate-100">Registrado por</th>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider bg-slate-50 border-b border-slate-100 text-right">Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
<?php if(empty($documents)): ?>
|
||||
<tr>
|
||||
<td colspan="5" class="px-6 py-16 text-center">
|
||||
<div class="flex flex-col items-center justify-center text-slate-300">
|
||||
<i class="fas fa-folder-open text-6xl mb-4 text-slate-200"></i>
|
||||
<p class="text-lg font-bold text-slate-400">No se encontraron documentos.</p>
|
||||
<p class="text-sm text-slate-400 mt-1">Ajusta los filtros o registra un nuevo oficio.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach($documents as $doc): ?>
|
||||
<tr class="hover:bg-slate-50 transition-colors group">
|
||||
<td class="px-6 py-4">
|
||||
<span class="inline-flex items-center gap-2 font-bold text-slate-800">
|
||||
<i class="fas fa-file-pdf text-red-500 text-lg group-hover:scale-110 transition-transform"></i>
|
||||
<?= htmlspecialchars($doc['reference_number']) ?>
|
||||
</span>
|
||||
<div class="text-[10px] text-slate-400 mt-1 uppercase font-bold tracking-widest flex items-center gap-1">
|
||||
<i class="far fa-calendar-alt"></i> <?= date('d/m/Y', strtotime($doc['created_at'])) ?>
|
||||
<i class="far fa-clock ml-2"></i> <?= date('H:i', strtotime($doc['created_at'])) ?>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 max-w-[200px] md:max-w-xs">
|
||||
<p class="text-sm text-slate-800 font-bold truncate" title="<?= htmlspecialchars($doc['title']) ?>"><?= htmlspecialchars($doc['title']) ?></p>
|
||||
<p class="text-xs text-slate-500 mt-1 line-clamp-2 leading-relaxed" title="<?= htmlspecialchars($doc['description']) ?>"><?= htmlspecialchars($doc['description']) ?></p>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<?php
|
||||
$typeClasses = [
|
||||
'entrada' => 'bg-emerald-100 text-emerald-800 border-emerald-200',
|
||||
'salida' => 'bg-amber-100 text-amber-800 border-amber-200',
|
||||
'archivo_interno' => 'bg-slate-100 text-slate-800 border-slate-200'
|
||||
];
|
||||
$typeLabels = [
|
||||
'entrada' => 'Entrada',
|
||||
'salida' => 'Salida',
|
||||
'archivo_interno' => 'Archivo Interno'
|
||||
];
|
||||
$class = $typeClasses[$doc['type']] ?? 'bg-gray-100';
|
||||
$label = $typeLabels[$doc['type']] ?? ucfirst($doc['type']);
|
||||
?>
|
||||
<span class="px-3 py-1.5 rounded-full text-[10px] font-bold border <?= $class ?> uppercase tracking-wider shadow-sm flex items-center gap-1.5 w-fit">
|
||||
<?php if($doc['type'] == 'entrada') echo '<i class="fas fa-arrow-down"></i>'; ?>
|
||||
<?php if($doc['type'] == 'salida') echo '<i class="fas fa-arrow-up"></i>'; ?>
|
||||
<?php if($doc['type'] == 'archivo_interno') echo '<i class="fas fa-archive"></i>'; ?>
|
||||
<?= $label ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<div class="flex items-center gap-2.5">
|
||||
<div class="w-7 h-7 rounded-full bg-slate-200 flex items-center justify-center text-xs font-bold text-slate-600 shadow-inner border border-slate-300">
|
||||
<?= strtoupper(substr($doc['user_name'] ?? 'S', 0, 1)) ?>
|
||||
</div>
|
||||
<span class="text-xs font-semibold text-slate-700"><?= htmlspecialchars($doc['user_name'] ?? 'Sistema') ?></span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-right">
|
||||
<a href="actions/document_download.php?id=<?= $doc['id'] ?>" class="inline-flex items-center gap-2 px-4 py-2 bg-slate-800 text-white hover:bg-accent hover:text-white rounded-xl transition-all text-[11px] font-bold uppercase tracking-wider shadow-md hover:shadow-lg hover:-translate-y-0.5">
|
||||
<i class="fas fa-download"></i> Descargar
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once 'includes/footer.php'; ?>
|
||||
39
juridico/includes/auth.php
Normal file
39
juridico/includes/auth.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
function checkLogin() {
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
function hasRole($roles) {
|
||||
if (!isset($_SESSION['user_role'])) return false;
|
||||
if (is_array($roles)) {
|
||||
return in_array($_SESSION['user_role'], $roles);
|
||||
}
|
||||
return $_SESSION['user_role'] === $roles;
|
||||
}
|
||||
|
||||
function requireRole($roles) {
|
||||
checkLogin();
|
||||
if (!hasRole($roles)) {
|
||||
die("Acceso denegado. No tienes el rol necesario para ver esta página.");
|
||||
}
|
||||
}
|
||||
|
||||
function getUserName() {
|
||||
return $_SESSION['user_name'] ?? 'Usuario';
|
||||
}
|
||||
|
||||
function getRoleName() {
|
||||
$roles = [
|
||||
'superadmin' => 'Super Administrador',
|
||||
'admin' => 'Administrador',
|
||||
'supervisor' => 'Supervisor',
|
||||
'user' => 'Usuario Normal'
|
||||
];
|
||||
return $roles[$_SESSION['user_role']] ?? 'Desconocido';
|
||||
}
|
||||
?>
|
||||
15
juridico/includes/footer.php
Normal file
15
juridico/includes/footer.php
Normal file
@ -0,0 +1,15 @@
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
// Simple mobile menu toggle
|
||||
document.getElementById('mobile-menu-btn')?.addEventListener('click', () => {
|
||||
const sidebar = document.querySelector('aside');
|
||||
sidebar.classList.toggle('hidden');
|
||||
sidebar.classList.toggle('absolute');
|
||||
sidebar.classList.toggle('h-full');
|
||||
sidebar.classList.toggle('w-72');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
103
juridico/includes/header.php
Normal file
103
juridico/includes/header.php
Normal file
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
$currentPage = basename($_SERVER['PHP_SELF']);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Consultoría Jurídica - Gestión de Documentos</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="public/css/style.css">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'sans-serif'],
|
||||
},
|
||||
colors: {
|
||||
'primary': '#0f172a', // Slate 900 (más sobrio, elegante)
|
||||
'secondary': '#f1f5f9',
|
||||
'accent': '#c2410c', // Naranja oscuro/óxido
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="bg-slate-50 text-slate-800 font-sans antialiased flex h-screen overflow-hidden">
|
||||
|
||||
<!-- Sidebar -->
|
||||
<aside class="w-72 bg-primary text-white flex flex-col hidden md:flex shadow-2xl z-20 transition-all duration-300">
|
||||
<div class="p-8 flex flex-col items-center justify-center border-b border-slate-800 relative overflow-hidden">
|
||||
<div class="absolute inset-0 opacity-10 bg-[url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4IiBoZWlnaHQ9IjgiPgo8cmVjdCB3aWR0aD0iOCIgaGVpZ2h0PSI4IiBmaWxsPSIjZmZmIiBmaWxsLW9wYWNpdHk9IjAuMSIvPgo8cGF0aCBkPSJNMCAwTDggOFpNOCAwTDAgOFoiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9IjAuMSIvPjwvc3ZnPg==')]"></div>
|
||||
<div class="text-center relative z-10">
|
||||
<i class="fas fa-balance-scale text-4xl text-accent mb-3 drop-shadow-md"></i>
|
||||
<h1 class="text-xl font-bold tracking-widest text-slate-100">DIRECCIÓN DE</h1>
|
||||
<p class="text-sm text-slate-400 font-medium uppercase tracking-[0.2em] mt-1">Consultoría Jurídica</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="flex-1 px-4 py-6 space-y-2 overflow-y-auto custom-scrollbar">
|
||||
<a href="dashboard.php" class="flex items-center gap-4 px-4 py-3.5 rounded-xl transition-all duration-200 <?= $currentPage === 'dashboard.php' ? 'bg-slate-800 text-white shadow-md' : 'text-slate-300 hover:bg-slate-800/50 hover:text-white' ?>">
|
||||
<i class="fas fa-chart-pie w-5 text-center <?= $currentPage === 'dashboard.php' ? 'text-accent' : '' ?>"></i>
|
||||
<span class="font-medium text-sm tracking-wide">Panel de Control</span>
|
||||
</a>
|
||||
<a href="documents.php" class="flex items-center gap-4 px-4 py-3.5 rounded-xl transition-all duration-200 <?= $currentPage === 'documents.php' ? 'bg-slate-800 text-white shadow-md' : 'text-slate-300 hover:bg-slate-800/50 hover:text-white' ?>">
|
||||
<i class="fas fa-folder-open w-5 text-center <?= $currentPage === 'documents.php' ? 'text-accent' : '' ?>"></i>
|
||||
<span class="font-medium text-sm tracking-wide">Archivos y Expedientes</span>
|
||||
</a>
|
||||
|
||||
<?php if(hasRole(['superadmin', 'admin', 'supervisor'])): ?>
|
||||
<a href="document_add.php" class="flex items-center gap-4 px-4 py-3.5 rounded-xl transition-all duration-200 <?= $currentPage === 'document_add.php' ? 'bg-slate-800 text-white shadow-md' : 'text-slate-300 hover:bg-slate-800/50 hover:text-white' ?>">
|
||||
<i class="fas fa-file-signature w-5 text-center <?= $currentPage === 'document_add.php' ? 'text-accent' : '' ?>"></i>
|
||||
<span class="font-medium text-sm tracking-wide">Registrar Documento</span>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if(hasRole(['superadmin', 'admin'])): ?>
|
||||
<div class="pt-6 mt-6 border-t border-slate-800">
|
||||
<p class="px-4 text-xs font-bold text-slate-500 uppercase tracking-widest mb-3">Administración</p>
|
||||
<a href="users.php" class="flex items-center gap-4 px-4 py-3.5 rounded-xl transition-all duration-200 <?= $currentPage === 'users.php' ? 'bg-slate-800 text-white shadow-md' : 'text-slate-300 hover:bg-slate-800/50 hover:text-white' ?>">
|
||||
<i class="fas fa-users-cog w-5 text-center <?= $currentPage === 'users.php' ? 'text-accent' : '' ?>"></i>
|
||||
<span class="font-medium text-sm tracking-wide">Gestión de Usuarios</span>
|
||||
</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</nav>
|
||||
|
||||
<div class="p-5 border-t border-slate-800 bg-slate-900/50">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<div class="w-10 h-10 rounded-full bg-slate-800 border border-slate-700 flex items-center justify-center text-accent font-bold shadow-inner">
|
||||
<?= strtoupper(substr(getUserName(), 0, 1)) ?>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-sm font-bold text-slate-200 truncate"><?= htmlspecialchars(getUserName()) ?></p>
|
||||
<p class="text-xs text-accent font-medium truncate"><?= getRoleName() ?></p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="logout.php" class="flex items-center justify-center gap-2 w-full py-2.5 bg-slate-800 hover:bg-red-900/80 hover:text-red-100 text-slate-300 border border-slate-700 hover:border-red-800 rounded-lg transition-all duration-300 text-xs font-semibold tracking-wider uppercase">
|
||||
<i class="fas fa-sign-out-alt"></i> Cerrar Sesión
|
||||
</a>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="flex-1 flex flex-col h-screen overflow-hidden bg-slate-50 relative">
|
||||
<!-- Mobile Header -->
|
||||
<header class="md:hidden bg-primary text-white p-4 flex justify-between items-center shadow-md z-10">
|
||||
<div class="flex items-center gap-3">
|
||||
<i class="fas fa-balance-scale text-accent"></i>
|
||||
<span class="font-bold tracking-wider text-sm">DIR. JURÍDICA</span>
|
||||
</div>
|
||||
<button id="mobile-menu-btn" class="text-slate-300 focus:outline-none">
|
||||
<i class="fas fa-bars text-xl"></i>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<!-- Content Area -->
|
||||
<div class="flex-1 overflow-y-auto p-4 md:p-8 custom-scrollbar">
|
||||
95
juridico/index.php
Normal file
95
juridico/index.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (isset($_SESSION['user_id'])) {
|
||||
header('Location: dashboard.php');
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Acceso - Consultoría Jurídica</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'sans-serif'],
|
||||
},
|
||||
colors: {
|
||||
'primary': '#0f172a',
|
||||
'accent': '#c2410c',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="bg-slate-50 flex items-center justify-center h-screen bg-[url('https://images.unsplash.com/photo-1589829085413-56de8ae18c73?q=80&w=2000&auto=format&fit=crop')] bg-cover bg-center relative">
|
||||
|
||||
<!-- Overlay oscuro para dar contraste -->
|
||||
<div class="absolute inset-0 bg-slate-900/80 backdrop-blur-sm z-0"></div>
|
||||
|
||||
<div class="relative z-10 w-full max-w-md p-8 bg-white/95 backdrop-blur-md rounded-2xl shadow-[0_20px_50px_rgba(0,0,0,0.5)] border border-white/20 transform transition-all hover:scale-[1.01] duration-300">
|
||||
<div class="text-center mb-8">
|
||||
<i class="fas fa-balance-scale text-5xl text-accent mb-4 drop-shadow-md"></i>
|
||||
<h1 class="text-2xl font-bold text-slate-800 tracking-wider">SISTEMA JURÍDICO</h1>
|
||||
<p class="text-xs text-slate-500 mt-2 uppercase tracking-[0.3em] font-bold">Gestión de Documentos</p>
|
||||
</div>
|
||||
|
||||
<?php if (isset($_SESSION['error'])): ?>
|
||||
<div class="bg-red-50 border-l-4 border-red-500 p-4 mb-6 rounded-r-lg shadow-sm">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<i class="fas fa-exclamation-circle text-red-500"></i>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<p class="text-sm text-red-700 font-medium">
|
||||
<?= htmlspecialchars($_SESSION['error']); unset($_SESSION['error']); ?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="actions/login_action.php" method="POST" class="space-y-6">
|
||||
<div>
|
||||
<label for="username" class="block text-xs font-bold text-slate-600 uppercase tracking-wider mb-2">Usuario</label>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
|
||||
<i class="fas fa-user text-slate-400"></i>
|
||||
</div>
|
||||
<input type="text" name="username" id="username" required
|
||||
class="block w-full pl-11 pr-3 py-3 border border-slate-300 rounded-xl focus:ring-accent focus:border-accent sm:text-sm bg-slate-50 text-slate-800 transition-colors shadow-inner font-medium placeholder-slate-400"
|
||||
placeholder="Ingresa tu usuario">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="password" class="block text-xs font-bold text-slate-600 uppercase tracking-wider mb-2">Contraseña</label>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
|
||||
<i class="fas fa-lock text-slate-400"></i>
|
||||
</div>
|
||||
<input type="password" name="password" id="password" required
|
||||
class="block w-full pl-11 pr-3 py-3 border border-slate-300 rounded-xl focus:ring-accent focus:border-accent sm:text-sm bg-slate-50 text-slate-800 transition-colors shadow-inner font-medium placeholder-slate-400"
|
||||
placeholder="••••••••">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-2">
|
||||
<button type="submit"
|
||||
class="w-full flex justify-center items-center gap-2 py-3.5 px-4 border border-transparent rounded-xl shadow-lg text-sm font-bold text-white bg-primary hover:bg-slate-800 hover:shadow-xl hover:-translate-y-0.5 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary transition-all duration-300 uppercase tracking-wider">
|
||||
<span>Ingresar al Sistema</span>
|
||||
<i class="fas fa-arrow-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
6
juridico/logout.php
Normal file
6
juridico/logout.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
session_start();
|
||||
session_destroy();
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
?>
|
||||
57
juridico/public/css/style.css
Normal file
57
juridico/public/css/style.css
Normal file
@ -0,0 +1,57 @@
|
||||
/* Custom Scrollbar */
|
||||
.custom-scrollbar::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||
background-color: #cbd5e1;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #94a3b8;
|
||||
}
|
||||
|
||||
aside .custom-scrollbar::-webkit-scrollbar-thumb {
|
||||
background-color: #334155;
|
||||
}
|
||||
|
||||
aside .custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #475569;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 0.4s ease-out forwards;
|
||||
}
|
||||
|
||||
.animate-fade-in-delayed {
|
||||
animation: fadeIn 0.4s ease-out 0.15s forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Form inputs focus ring override to match accent color */
|
||||
input:focus, textarea:focus, select:focus {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 2px rgba(194, 65, 12, 0.2);
|
||||
border-color: #c2410c;
|
||||
}
|
||||
|
||||
/* Glass effect for modal/cards if needed */
|
||||
.glass-card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.05);
|
||||
}
|
||||
56
juridico/setup.php
Normal file
56
juridico/setup.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
// Script para inicializar la base de datos y crear el Super Administrador
|
||||
$dbDir = __DIR__ . '/database';
|
||||
if (!is_dir($dbDir)) {
|
||||
mkdir($dbDir, 0777, true);
|
||||
}
|
||||
$uploadsDir = __DIR__ . '/uploads';
|
||||
if (!is_dir($uploadsDir)) {
|
||||
mkdir($uploadsDir, 0777, true);
|
||||
}
|
||||
|
||||
require_once 'config/database.php';
|
||||
|
||||
try {
|
||||
$queries = [
|
||||
"CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
password TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
role TEXT NOT NULL CHECK(role IN ('superadmin', 'admin', 'supervisor', 'user')),
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)",
|
||||
"CREATE TABLE IF NOT EXISTS documents (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
reference_number TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
type TEXT NOT NULL CHECK(type IN ('entrada', 'salida', 'archivo_interno')),
|
||||
category TEXT,
|
||||
file_path TEXT NOT NULL,
|
||||
uploaded_by INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY(uploaded_by) REFERENCES users(id)
|
||||
)"
|
||||
];
|
||||
|
||||
foreach ($queries as $query) {
|
||||
$db->exec($query);
|
||||
}
|
||||
|
||||
// Crear Super Administrador por defecto si no existe
|
||||
$stmt = $db->prepare("SELECT COUNT(*) FROM users WHERE username = 'superadmin'");
|
||||
$stmt->execute();
|
||||
if ($stmt->fetchColumn() == 0) {
|
||||
$password = password_hash('superadmin123', PASSWORD_DEFAULT);
|
||||
$stmt = $db->prepare("INSERT INTO users (username, password, name, role) VALUES ('superadmin', :password, 'Super Administrador Principal', 'superadmin')");
|
||||
$stmt->execute(['password' => $password]);
|
||||
echo "Usuario superadmin creado con éxito. Contraseña: superadmin123\n";
|
||||
}
|
||||
|
||||
echo "Base de datos inicializada correctamente.\n";
|
||||
} catch (PDOException $e) {
|
||||
die("Error inicializando la base de datos: " . $e->getMessage());
|
||||
}
|
||||
?>
|
||||
BIN
juridico/uploads/doc_6a1dea56a6af8_1780345430.docx
Normal file
BIN
juridico/uploads/doc_6a1dea56a6af8_1780345430.docx
Normal file
Binary file not shown.
143
juridico/users.php
Normal file
143
juridico/users.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
require_once 'config/database.php';
|
||||
require_once 'includes/header.php';
|
||||
|
||||
requireRole(['superadmin', 'admin']);
|
||||
|
||||
try {
|
||||
$stmt = $db->query("SELECT * FROM users ORDER BY created_at DESC");
|
||||
$users = $stmt->fetchAll();
|
||||
} catch (PDOException $e) {
|
||||
$error = "Error al cargar los usuarios.";
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="mb-8 flex justify-between items-end animate-fade-in">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-slate-800 tracking-tight">Gestión de Usuarios</h1>
|
||||
<p class="text-slate-500 mt-2">Administra el acceso al sistema de Consultoría Jurídica.</p>
|
||||
</div>
|
||||
<button onclick="document.getElementById('modal-add-user').classList.remove('hidden')" class="bg-primary hover:bg-slate-800 text-white px-6 py-2.5 rounded-xl font-bold text-sm tracking-wider uppercase transition-all shadow-lg hover:shadow-xl hover:-translate-y-0.5 flex items-center gap-2">
|
||||
<i class="fas fa-plus"></i> Nuevo Usuario
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<?php if (isset($_SESSION['success'])): ?>
|
||||
<div class="bg-emerald-50 border-l-4 border-emerald-500 p-4 mb-6 rounded-r-lg shadow-sm">
|
||||
<p class="text-sm text-emerald-700 font-medium"><?= htmlspecialchars($_SESSION['success']); unset($_SESSION['success']); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($_SESSION['error'])): ?>
|
||||
<div class="bg-red-50 border-l-4 border-red-500 p-4 mb-6 rounded-r-lg shadow-sm">
|
||||
<p class="text-sm text-red-700 font-medium"><?= htmlspecialchars($_SESSION['error']); unset($_SESSION['error']); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-sm border border-slate-100 overflow-hidden animate-fade-in-delayed">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider bg-slate-50">Nombre / Usuario</th>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider bg-slate-50">Rol de Acceso</th>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider bg-slate-50">Fecha Creación</th>
|
||||
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider bg-slate-50 text-right">Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
<?php foreach($users as $u): ?>
|
||||
<tr class="hover:bg-slate-50 transition-colors">
|
||||
<td class="px-6 py-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-10 h-10 rounded-full bg-slate-100 border border-slate-200 flex items-center justify-center text-slate-500 font-bold shadow-inner">
|
||||
<?= strtoupper(substr($u['name'], 0, 1)) ?>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-bold text-slate-800"><?= htmlspecialchars($u['name']) ?></p>
|
||||
<p class="text-xs text-slate-500 mt-0.5 font-medium">@<?= htmlspecialchars($u['username']) ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<?php
|
||||
$roleClasses = [
|
||||
'superadmin' => 'bg-purple-100 text-purple-800 border-purple-200',
|
||||
'admin' => 'bg-blue-100 text-blue-800 border-blue-200',
|
||||
'supervisor' => 'bg-amber-100 text-amber-800 border-amber-200',
|
||||
'user' => 'bg-slate-100 text-slate-800 border-slate-200'
|
||||
];
|
||||
$roleLabels = [
|
||||
'superadmin' => 'Super Admin',
|
||||
'admin' => 'Administrador',
|
||||
'supervisor' => 'Supervisor',
|
||||
'user' => 'Usuario Normal'
|
||||
];
|
||||
$class = $roleClasses[$u['role']] ?? 'bg-gray-100';
|
||||
$label = $roleLabels[$u['role']] ?? $u['role'];
|
||||
?>
|
||||
<span class="px-3 py-1 rounded-full text-[10px] font-bold border <?= $class ?> uppercase tracking-wider shadow-sm">
|
||||
<?= $label ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-600 font-medium">
|
||||
<?= date('d/m/Y', strtotime($u['created_at'])) ?>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-right">
|
||||
<?php if($_SESSION['user_id'] != $u['id'] && (hasRole('superadmin') || (hasRole('admin') && $u['role'] != 'superadmin'))): ?>
|
||||
<button class="text-slate-400 hover:text-red-600 transition-colors" title="Eliminar (No implementado en esta demo)">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Nuevo Usuario -->
|
||||
<div id="modal-add-user" class="fixed inset-0 z-50 hidden">
|
||||
<div class="absolute inset-0 bg-slate-900/60 backdrop-blur-sm transition-opacity" onclick="document.getElementById('modal-add-user').classList.add('hidden')"></div>
|
||||
<div class="flex items-center justify-center min-h-screen px-4">
|
||||
<div class="bg-white rounded-2xl shadow-2xl border border-slate-100 w-full max-w-md relative z-10 animate-fade-in">
|
||||
<div class="px-6 py-4 border-b border-slate-100 flex justify-between items-center bg-slate-50 rounded-t-2xl">
|
||||
<h3 class="text-lg font-bold text-slate-800">Registrar Nuevo Usuario</h3>
|
||||
<button onclick="document.getElementById('modal-add-user').classList.add('hidden')" class="text-slate-400 hover:text-slate-600 transition-colors">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<form action="actions/user_create.php" method="POST" class="p-6 space-y-4">
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-slate-600 uppercase tracking-wider mb-2">Nombre Completo</label>
|
||||
<input type="text" name="name" required class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:ring-accent focus:border-accent bg-slate-50 text-sm font-medium shadow-inner transition-colors">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-slate-600 uppercase tracking-wider mb-2">Nombre de Usuario</label>
|
||||
<input type="text" name="username" required class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:ring-accent focus:border-accent bg-slate-50 text-sm font-medium shadow-inner transition-colors">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-slate-600 uppercase tracking-wider mb-2">Contraseña</label>
|
||||
<input type="password" name="password" required class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:ring-accent focus:border-accent bg-slate-50 text-sm font-medium shadow-inner transition-colors">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-slate-600 uppercase tracking-wider mb-2">Rol del Sistema</label>
|
||||
<select name="role" required class="w-full px-4 py-3 border border-slate-300 rounded-xl focus:ring-accent focus:border-accent bg-slate-50 text-sm font-medium shadow-inner transition-colors">
|
||||
<option value="user">Usuario Normal</option>
|
||||
<option value="supervisor">Supervisor</option>
|
||||
<?php if(hasRole('superadmin')): ?>
|
||||
<option value="admin">Administrador</option>
|
||||
<option value="superadmin">Super Administrador</option>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="pt-4 flex gap-3">
|
||||
<button type="button" onclick="document.getElementById('modal-add-user').classList.add('hidden')" class="flex-1 px-4 py-3 border border-slate-300 text-slate-700 font-bold text-sm uppercase tracking-wider rounded-xl hover:bg-slate-50 transition-colors">Cancelar</button>
|
||||
<button type="submit" class="flex-1 px-4 py-3 bg-primary hover:bg-slate-800 text-white font-bold text-sm uppercase tracking-wider rounded-xl transition-all shadow-md">Guardar</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once 'includes/footer.php'; ?>
|
||||
Loading…
x
Reference in New Issue
Block a user