This commit is contained in:
unknown 2026-06-02 17:07:50 -04:00
commit 06a5f64fb2
18 changed files with 1137 additions and 0 deletions

View 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;
?>

View 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;
}
?>

View 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;
}
?>

View 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;
}

View 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
View 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'; ?>

Binary file not shown.

104
juridico/document_add.php Normal file
View 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">
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
View 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'; ?>

View 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';
}
?>

View 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>

View 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
View 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
View File

@ -0,0 +1,6 @@
<?php
session_start();
session_destroy();
header('Location: index.php');
exit;
?>

View 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
View 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());
}
?>

Binary file not shown.

143
juridico/users.php Normal file
View 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'; ?>