modificaciones en globalizar config

This commit is contained in:
2026-01-01 17:16:47 -06:00
parent 2c0c3e7148
commit 20d550f12c
16 changed files with 390 additions and 139 deletions

View File

@@ -19,36 +19,96 @@ public class MenuViewComponent : ViewComponent
var userIdClaim = HttpContext.User.FindFirst(ClaimTypes.NameIdentifier);
if (userIdClaim == null || !long.TryParse(userIdClaim.Value, out var userId))
{
return View(new List<foundation_system.Models.Permiso>());
return View(new foundation_system.Models.ViewModels.MenuViewModel());
}
var isRoot = HttpContext.User.IsInRole("ROOT");
List<foundation_system.Models.Permiso> menuItems;
List<int> userPermisoIds;
if (isRoot)
{
menuItems = await _context.Permisos
.Include(p => p.Modulo)
.Where(p => p.EsMenu)
.OrderBy(p => p.Modulo!.Orden)
.ThenBy(p => p.Orden)
.ToListAsync();
userPermisoIds = await _context.Permisos.Select(p => p.Id).ToListAsync();
}
else
{
menuItems = await _context.RolesUsuario
userPermisoIds = await _context.RolesUsuario
.Where(ru => ru.UsuarioId == userId)
.Join(_context.RolesPermisos, ru => ru.RolId, rp => rp.RolId, (ru, rp) => rp)
.Join(_context.Permisos, rp => rp.PermisoId, p => p.Id, (rp, p) => p)
.Include(p => p.Modulo)
.Where(p => p.EsMenu)
.OrderBy(p => p.Modulo!.Orden)
.ThenBy(p => p.Orden)
.Join(_context.RolesPermisos, ru => ru.RolId, rp => rp.RolId, (ru, rp) => rp.PermisoId)
.Distinct()
.ToListAsync();
}
return View(menuItems);
// Fetch all active modules and permissions
var allModules = await _context.Modulos
.Include(m => m.Permisos)
.Where(m => m.Activo)
.OrderBy(m => m.Orden)
.ToListAsync();
var menuViewModel = new foundation_system.Models.ViewModels.MenuViewModel();
// Build the tree starting from root modules (ParentId == null)
var rootModules = allModules.Where(m => m.ParentId == null).OrderBy(m => m.Orden);
foreach (var module in rootModules)
{
var menuItem = BuildModuleMenuItem(module, allModules, userPermisoIds);
if (menuItem != null)
{
menuViewModel.Items.Add(menuItem);
}
}
return View(menuViewModel);
}
private foundation_system.Models.ViewModels.MenuItem? BuildModuleMenuItem(
foundation_system.Models.Modulo module,
List<foundation_system.Models.Modulo> allModules,
List<int> userPermisoIds)
{
var item = new foundation_system.Models.ViewModels.MenuItem
{
Title = module.Nombre,
Icon = module.Icono,
IsGroup = true,
Order = module.Orden
};
// 1. Add Submodules
var subModules = allModules.Where(m => m.ParentId == module.Id).OrderBy(m => m.Orden);
foreach (var sub in subModules)
{
var subItem = BuildModuleMenuItem(sub, allModules, userPermisoIds);
if (subItem != null)
{
item.Children.Add(subItem);
}
}
// 2. Add Direct Permissions (Menu Items)
var permissions = module.Permisos
.Where(p => p.EsMenu && userPermisoIds.Contains(p.Id))
.OrderBy(p => p.Orden);
foreach (var p in permissions)
{
item.Children.Add(new foundation_system.Models.ViewModels.MenuItem
{
Title = p.Nombre,
Icon = p.Icono,
Url = p.Url,
IsGroup = false,
Order = p.Orden
});
}
// Only return the item if it has children (permissions or submodules with permissions)
if (item.Children.Any())
{
return item;
}
return null;
}
}

View File

@@ -1,8 +1,9 @@
using System.Diagnostics;
using System.Security.Claims;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using foundation_system.Data;
using foundation_system.Models;
using foundation_system.Models.ViewModels;
using foundation_system.Services;
using Microsoft.AspNetCore.Authorization;
namespace foundation_system.Controllers;
@@ -11,24 +12,24 @@ namespace foundation_system.Controllers;
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly ApplicationDbContext _context;
private readonly IDashboardService _dashboardService;
public HomeController(ILogger<HomeController> logger, ApplicationDbContext context)
public HomeController(ILogger<HomeController> logger, IDashboardService dashboardService)
{
_logger = logger;
_context = context;
_dashboardService = dashboardService;
}
public async Task<IActionResult> Index()
{
var totalNinos = await _context.Ninos.CountAsync(n => n.Activo);
var asistenciasHoy = await _context.Asistencias
.CountAsync(a => a.Fecha == DateOnly.FromDateTime(DateTime.Now) && a.Estado == "PRESENTE");
var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier);
if (userIdClaim == null || !long.TryParse(userIdClaim.Value, out var userId))
{
return View(new DashboardViewModel());
}
ViewBag.TotalNinos = totalNinos;
ViewBag.AsistenciasHoy = asistenciasHoy;
return View();
var vm = await _dashboardService.GetDashboardDataAsync(userId);
return View(vm);
}
public IActionResult Privacy()

View File

@@ -19,19 +19,27 @@ public class ModuloController : Controller
// GET: Modulo
public async Task<IActionResult> Index()
{
return View(await _context.Modulos.OrderBy(m => m.Orden).ToListAsync());
var modulos = await _context.Modulos
.Include(m => m.Parent)
.OrderBy(m => m.Orden)
.ToListAsync();
return View(modulos);
}
// GET: Modulo/Create
public IActionResult Create()
public async Task<IActionResult> Create()
{
ViewBag.ModulosPadre = await _context.Modulos
.Where(m => m.Activo)
.OrderBy(m => m.Orden)
.ToListAsync();
return View();
}
// POST: Modulo/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Nombre,Icono,Orden,Activo")] Modulo modulo)
public async Task<IActionResult> Create([Bind("Nombre,Icono,Orden,Activo,ParentId")] Modulo modulo)
{
if (ModelState.IsValid)
{
@@ -40,6 +48,10 @@ public class ModuloController : Controller
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewBag.ModulosPadre = await _context.Modulos
.Where(m => m.Activo)
.OrderBy(m => m.Orden)
.ToListAsync();
return View(modulo);
}
@@ -50,13 +62,19 @@ public class ModuloController : Controller
var modulo = await _context.Modulos.FindAsync(id);
if (modulo == null) return NotFound();
// Exclude current module and its children from parent options
ViewBag.ModulosPadre = await _context.Modulos
.Where(m => m.Activo && m.Id != id)
.OrderBy(m => m.Orden)
.ToListAsync();
return View(modulo);
}
// POST: Modulo/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Nombre,Icono,Orden,Activo")] Modulo modulo)
public async Task<IActionResult> Edit(int id, [Bind("Id,Nombre,Icono,Orden,Activo,ParentId")] Modulo modulo)
{
if (id != modulo.Id) return NotFound();
@@ -74,6 +92,10 @@ public class ModuloController : Controller
}
return RedirectToAction(nameof(Index));
}
ViewBag.ModulosPadre = await _context.Modulos
.Where(m => m.Activo && m.Id != id)
.OrderBy(m => m.Orden)
.ToListAsync();
return View(modulo);
}

View File

@@ -1,5 +1,3 @@
using System;
using System.Threading.Tasks;
using foundation_system.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

View File

@@ -1,33 +1,72 @@
-- =============================================
-- Author: System
-- Create date: 2025-12-31
-- Description: Adds permissions for new financial reports.
-- Description: Adds permissions for new financial reports with hierarchy.
-- =============================================
DO $$
DECLARE
v_modulo_id INT;
v_reportes_id INT;
v_financieros_id INT;
BEGIN
SELECT id INTO v_modulo_id FROM modulos WHERE nombre = 'Reportes';
-- 1. Ensure Root Module 'Reportes' exists
SELECT id INTO v_reportes_id FROM modulos WHERE nombre = 'Reportes';
-- 1. Movimientos
INSERT INTO permisos (modulo_id, codigo, nombre, descripcion, url, icono, orden, es_menu)
SELECT v_modulo_id, 'reportes.movimientos', 'Movimientos de Caja', 'Detalle de ingresos y egresos', '/Reportes/Movimientos', 'bi bi-list-check', 2, true
WHERE NOT EXISTS (SELECT 1 FROM permisos WHERE codigo = 'reportes.movimientos');
IF v_reportes_id IS NULL THEN
INSERT INTO modulos (nombre, icono, orden, activo)
VALUES ('Reportes', 'bi bi-file-earmark-text', 90, true)
RETURNING id INTO v_reportes_id;
END IF;
-- 2. Histórico de Saldos
INSERT INTO permisos (modulo_id, codigo, nombre, descripcion, url, icono, orden, es_menu)
SELECT v_modulo_id, 'reportes.saldos', 'Histórico de Saldos', 'Evolución diaria del saldo', '/Reportes/HistoricoSaldos', 'bi bi-graph-up', 3, true
WHERE NOT EXISTS (SELECT 1 FROM permisos WHERE codigo = 'reportes.saldos');
-- 2. Create Sub-Module 'Reportes Financieros'
SELECT id INTO v_financieros_id FROM modulos WHERE nombre = 'Reportes Financieros' AND parent_id = v_reportes_id;
-- 3. Gastos por Categoría
INSERT INTO permisos (modulo_id, codigo, nombre, descripcion, url, icono, orden, es_menu)
SELECT v_modulo_id, 'reportes.gastos', 'Gastos por Categoría', 'Análisis de gastos', '/Reportes/GastosCategoria', 'bi bi-pie-chart', 4, true
WHERE NOT EXISTS (SELECT 1 FROM permisos WHERE codigo = 'reportes.gastos');
IF v_financieros_id IS NULL THEN
INSERT INTO modulos (nombre, icono, orden, activo, parent_id)
VALUES ('Reportes Financieros', 'bi bi-cash-coin', 1, true, v_reportes_id)
RETURNING id INTO v_financieros_id;
END IF;
-- 4. Reposiciones
INSERT INTO permisos (modulo_id, codigo, nombre, descripcion, url, icono, orden, es_menu)
SELECT v_modulo_id, 'reportes.reposiciones', 'Reposiciones', 'Reporte de reintegros', '/Reportes/Reposiciones', 'bi bi-cash-stack', 5, true
WHERE NOT EXISTS (SELECT 1 FROM permisos WHERE codigo = 'reportes.reposiciones');
-- 3. Add Permissions (Menu Items) linked to 'Reportes Financieros'
-- Arqueo de Caja (Move if exists or insert)
IF EXISTS (SELECT 1 FROM permisos WHERE codigo = 'reportes.arqueo') THEN
UPDATE permisos SET modulo_id = v_financieros_id WHERE codigo = 'reportes.arqueo';
ELSE
INSERT INTO permisos (modulo_id, codigo, nombre, descripcion, url, icono, orden, es_menu)
VALUES (v_financieros_id, 'reportes.arqueo', 'Arqueo de Caja', 'Cierre diario de caja', '/Reportes/ArqueoCaja', 'bi bi-calculator', 1, true);
END IF;
-- Movimientos
IF EXISTS (SELECT 1 FROM permisos WHERE codigo = 'reportes.movimientos') THEN
UPDATE permisos SET modulo_id = v_financieros_id WHERE codigo = 'reportes.movimientos';
ELSE
INSERT INTO permisos (modulo_id, codigo, nombre, descripcion, url, icono, orden, es_menu)
VALUES (v_financieros_id, 'reportes.movimientos', 'Movimientos de Caja', 'Detalle de ingresos y egresos', '/Reportes/Movimientos', 'bi bi-list-check', 2, true);
END IF;
-- Histórico de Saldos
IF EXISTS (SELECT 1 FROM permisos WHERE codigo = 'reportes.saldos') THEN
UPDATE permisos SET modulo_id = v_financieros_id WHERE codigo = 'reportes.saldos';
ELSE
INSERT INTO permisos (modulo_id, codigo, nombre, descripcion, url, icono, orden, es_menu)
VALUES (v_financieros_id, 'reportes.saldos', 'Histórico de Saldos', 'Evolución diaria del saldo', '/Reportes/HistoricoSaldos', 'bi bi-graph-up', 3, true);
END IF;
-- Gastos por Categoría
IF EXISTS (SELECT 1 FROM permisos WHERE codigo = 'reportes.gastos') THEN
UPDATE permisos SET modulo_id = v_financieros_id WHERE codigo = 'reportes.gastos';
ELSE
INSERT INTO permisos (modulo_id, codigo, nombre, descripcion, url, icono, orden, es_menu)
VALUES (v_financieros_id, 'reportes.gastos', 'Gastos por Categoría', 'Análisis de gastos', '/Reportes/GastosCategoria', 'bi bi-pie-chart', 4, true);
END IF;
-- Reposiciones
IF EXISTS (SELECT 1 FROM permisos WHERE codigo = 'reportes.reposiciones') THEN
UPDATE permisos SET modulo_id = v_financieros_id WHERE codigo = 'reportes.reposiciones';
ELSE
INSERT INTO permisos (modulo_id, codigo, nombre, descripcion, url, icono, orden, es_menu)
VALUES (v_financieros_id, 'reportes.reposiciones', 'Reposiciones', 'Reporte de reintegros', '/Reportes/Reposiciones', 'bi bi-cash-stack', 5, true);
END IF;
END $$;

View File

@@ -28,6 +28,14 @@ public class Modulo
[Column("creado_en")]
public DateTime CreadoEn { get; set; } = DateTime.UtcNow;
// Navigation property
[Column("parent_id")]
public int? ParentId { get; set; }
// Navigation properties
[ForeignKey("ParentId")]
public virtual Modulo? Parent { get; set; }
public virtual ICollection<Modulo> SubModulos { get; set; } = new List<Modulo>();
public virtual ICollection<Permiso> Permisos { get; set; } = new List<Permiso>();
}

View File

@@ -18,6 +18,8 @@ builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddScoped<IAuthService, AuthService>();
builder.Services.AddScoped<IAntecedentesService, AntecedentesService>();
builder.Services.AddScoped<IReporteService, ReporteService>();
builder.Services.AddScoped<IDashboardService, DashboardService>();
builder.Services.AddScoped<IConfiguracionService, ConfiguracionService>();
// Configure cookie authentication
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)

View File

@@ -1,7 +1,9 @@
@model foundation_system.Models.ViewModels.LoginViewModel
@inject foundation_system.Services.IConfiguracionService ConfigService
@{
ViewData["Title"] = "Iniciar Sesión";
Layout = null;
var logoUrl = await ConfigService.GetValorOrDefaultAsync("LOGO_FUNDATION", "/img/logo-placeholder.png");
}
<!DOCTYPE html>
@@ -24,13 +26,7 @@
<div class="auth-card animate-fade-in">
<div class="auth-header">
<div class="auth-logo">
<div class="logo-icon">
<svg viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="45" stroke="currentColor" stroke-width="4"/>
<path d="M30 55 L50 35 L70 55" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M40 65 L50 55 L60 65" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<img src="@logoUrl" alt="Logo" class="img-fluid mb-3" style="max-height: 80px; width: auto; object-fit: contain;" />
</div>
<h1 class="auth-title">MIES</h1>
<p class="auth-subtitle">Misión Esperanza</p>

View File

@@ -1,50 +1,154 @@
@{
@model foundation_system.Models.ViewModels.DashboardViewModel
@inject foundation_system.Services.IConfiguracionService ConfigService
@{
ViewData["Title"] = "Dashboard";
var nameFoundation = await ConfigService.GetValorOrDefaultAsync("NAME_FOUNDATION", "foundation_system");
var description = await ConfigService.GetValorOrDefaultAsync("DESCRIPTION_FOUNDATION", "");
}
<div class="row g-4">
<div class="col-md-4">
<div class="card-custom border-start border-4 border-primary">
<div class="d-flex align-items-center">
<div class="flex-shrink-0 bg-primary bg-opacity-10 p-3 rounded">
<i class="bi bi-people-fill text-primary fs-3"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="text-muted small mb-1">Total Niños</h6>
<h3 class="mb-0">@ViewBag.TotalNinos</h3>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card-custom border-start border-4 border-success">
<div class="d-flex align-items-center">
<div class="flex-shrink-0 bg-success bg-opacity-10 p-3 rounded">
<i class="bi bi-calendar-check-fill text-success fs-3"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="text-muted small mb-1">Asistencias Hoy</h6>
<h3 class="mb-0">@ViewBag.AsistenciasHoy</h3>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card-custom border-start border-4 border-info">
<div class="d-flex align-items-center">
<div class="flex-shrink-0 bg-info bg-opacity-10 p-3 rounded">
<i class="bi bi-folder-fill text-info fs-3"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="text-muted small mb-1">Expedientes Activos</h6>
<h3 class="mb-0">@ViewBag.TotalNinos</h3>
</div>
</div>
</div>
</div>
</div>
<div class="card-custom mt-4">
<h4>Bienvenido al Sistema MIES</h4>
<p class="text-muted">Utilice el menú lateral para gestionar los expedientes de los niños y el control de asistencia diaria.</p>
<h4>Bienvenido al Sistema @nameFoundation</h4>
<p class="mb-2">
Este sistema ha sido diseñado para apoyar la gestión de la fundación,
permitiendo administrar de forma segura los expedientes de los niños,
el control de asistencia diaria, entre otros.
</p>
<hr />
<p class="text-muted mb-0">
@description;
</p>
</div>
<div class="row g-4">
@* Expediente Stats - Only visible if user has permission *@
@if (Model.TienePermisoExpediente)
{
<div class="col-md-4">
<div class="card-custom border-start border-4 border-primary">
<div class="d-flex align-items-center">
<div class="flex-shrink-0 bg-primary bg-opacity-10 p-3 rounded">
<i class="bi bi-people-fill text-primary fs-3"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="text-muted small mb-1">Niños Activos</h6>
<h3 class="mb-0">@Model.TotalNinosActivos</h3>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card-custom border-start border-4 border-warning">
<div class="d-flex align-items-center">
<div class="flex-shrink-0 bg-warning bg-opacity-10 p-3 rounded">
<i class="bi bi-cake-fill text-warning fs-3"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="text-muted small mb-1">Cumpleañeros del Mes</h6>
<h3 class="mb-0">@Model.CumpleanerosMes</h3>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card-custom border-start border-4 border-danger">
<div class="d-flex align-items-center">
<div class="flex-shrink-0 bg-danger bg-opacity-10 p-3 rounded">
<i class="bi bi-exclamation-triangle-fill text-danger fs-3"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="text-muted small mb-1">Mayores de 14 años</h6>
<h3 class="mb-0">@Model.NinosMayores14</h3>
</div>
</div>
</div>
</div>
}
@* Attendance Stats - Only visible if user has permission *@
@if (Model.TienePermisoAsistencia)
{
<div class="col-md-4">
<div class="card-custom border-start border-4 border-success">
<div class="d-flex align-items-center">
<div class="flex-shrink-0 bg-success bg-opacity-10 p-3 rounded">
<i class="bi bi-calendar-check-fill text-success fs-3"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="text-muted small mb-1">Asistencia Hoy</h6>
<h3 class="mb-0">@Model.AsistenciasHoy <small class="text-muted fs-6">/ @Model.TotalNinosParaAsistencia</small></h3>
<small class="text-success">@Model.PorcentajeAsistenciaHoy%</small>
</div>
</div>
</div>
</div>
}
@* Petty Cash Stats - Only visible if user has permission *@
@if (Model.TienePermisoCajaChica)
{
<div class="col-md-4">
<div class="card-custom border-start border-4 border-info">
<div class="d-flex align-items-center">
<div class="flex-shrink-0 bg-info bg-opacity-10 p-3 rounded">
<i class="bi bi-wallet2 text-info fs-3"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="text-muted small mb-1">Saldo Caja Chica</h6>
<h3 class="mb-0">C$ @Model.SaldoCajaChica.ToString("N2")</h3>
@if (!string.IsNullOrEmpty(Model.NombreCajaActiva))
{
<small class="text-muted">@Model.NombreCajaActiva</small>
}
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card-custom border-start border-4 border-secondary">
<div class="d-flex align-items-center">
<div class="flex-shrink-0 bg-secondary bg-opacity-10 p-3 rounded">
<i class="bi bi-arrow-down-circle-fill text-secondary fs-3"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="text-muted small mb-1">Gastos del Mes</h6>
<h3 class="mb-0">C$ @Model.GastosMes.ToString("N2")</h3>
<small class="text-muted">@Model.MovimientosMes movimientos</small>
</div>
</div>
</div>
</div>
}
</div>
@* Birthday List - Only visible if user has permission and there are birthdays *@
@if (Model.TienePermisoExpediente && Model.ListaCumpleaneros.Any())
{
<div class="card-custom mt-4">
<h5 class="mb-3"><i class="bi bi-cake2 me-2"></i>Cumpleañeros de @DateTime.Now.ToString("MMMM")</h5>
<div class="table-responsive">
<table class="table table-sm table-hover mb-0">
<thead>
<tr>
<th>Día</th>
<th>Nombre</th>
<th>Cumple</th>
</tr>
</thead>
<tbody>
@foreach (var cumple in Model.ListaCumpleaneros)
{
<tr>
<td><span class="badge bg-warning text-dark">@cumple.DiaCumple</span></td>
<td>@cumple.NombreCompleto</td>
<td>@cumple.Edad años</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}

View File

@@ -19,13 +19,24 @@
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="row">
<div class="col-md-8 mb-3">
<div class="col-md-6 mb-3">
<label asp-for="Nombre" class="form-label">Nombre del Módulo</label>
<input asp-for="Nombre" class="form-control" placeholder="Ej. Administración, Reportes..." />
<span asp-validation-for="Nombre" class="text-danger"></span>
</div>
<div class="col-md-4 mb-3">
<label asp-for="Orden" class="form-label">Orden de Visualización</label>
<label asp-for="ParentId" class="form-label">Módulo Padre (Opcional)</label>
<select asp-for="ParentId" class="form-select">
<option value="">-- Ninguno (Módulo Raíz) --</option>
@foreach (var modulo in (IEnumerable<foundation_system.Models.Modulo>)ViewBag.ModulosPadre)
{
<option value="@modulo.Id">@modulo.Nombre</option>
}
</select>
<small class="text-muted">Si se selecciona, este será un sub-módulo</small>
</div>
<div class="col-md-2 mb-3">
<label asp-for="Orden" class="form-label">Orden</label>
<input asp-for="Orden" class="form-control" type="number" />
<span asp-validation-for="Orden" class="text-danger"></span>
</div>

View File

@@ -21,13 +21,24 @@
<input type="hidden" asp-for="CreadoEn" />
<div class="row">
<div class="col-md-8 mb-3">
<div class="col-md-6 mb-3">
<label asp-for="Nombre" class="form-label">Nombre del Módulo</label>
<input asp-for="Nombre" class="form-control" />
<span asp-validation-for="Nombre" class="text-danger"></span>
</div>
<div class="col-md-4 mb-3">
<label asp-for="Orden" class="form-label">Orden de Visualización</label>
<label asp-for="ParentId" class="form-label">Módulo Padre (Opcional)</label>
<select asp-for="ParentId" class="form-select">
<option value="">-- Ninguno (Módulo Raíz) --</option>
@foreach (var modulo in (IEnumerable<foundation_system.Models.Modulo>)ViewBag.ModulosPadre)
{
<option value="@modulo.Id">@modulo.Nombre</option>
}
</select>
<small class="text-muted">Si se selecciona, este será un sub-módulo</small>
</div>
<div class="col-md-2 mb-3">
<label asp-for="Orden" class="form-label">Orden</label>
<input asp-for="Orden" class="form-control" type="number" />
<span asp-validation-for="Orden" class="text-danger"></span>
</div>

View File

@@ -1,29 +1,19 @@
@model IEnumerable<foundation_system.Models.Permiso>
@model foundation_system.Models.ViewModels.MenuViewModel
@{
var currentController = ViewContext.RouteData.Values["controller"]?.ToString();
var currentAction = ViewContext.RouteData.Values["action"]?.ToString();
var currentUrl = $"/{currentController}/{currentAction}";
ViewData["CurrentUrl"] = currentUrl;
}
<nav class="nav flex-column">
<a class="nav-link-custom @(ViewContext.RouteData.Values["controller"]?.ToString() == "Home" ? "active" : "")" asp-controller="Home" asp-action="Index">
<a class="nav-link-custom @(currentController == "Home" ? "active" : "")" asp-controller="Home" asp-action="Index">
<i class="bi bi-house-door"></i> Inicio
</a>
@{
var groupedMenu = Model.GroupBy(m => m.Modulo);
var currentController = ViewContext.RouteData.Values["controller"]?.ToString();
}
@foreach (var group in groupedMenu)
@foreach (var item in Model.Items)
{
<div class="nav-section-title mt-3">
@if (!string.IsNullOrEmpty(group.Key?.Icono))
{
<i class="bi @group.Key.Icono me-1"></i>
}
@(group.Key?.Nombre ?? "Sin Módulo")
</div>
@foreach (var item in group)
{
<a class="nav-link-custom @(currentController == item.Codigo ? "active" : "")" href="@item.Url">
<i class="bi @item.Icono"></i> @item.Nombre
</a>
}
<partial name="Components/Menu/_MenuItem" model="item" view-data="ViewData" />
}
</nav>

View File

@@ -1,9 +1,17 @@
<!DOCTYPE html>
@inject foundation_system.Services.IConfiguracionService ConfigService
@{
var logoUrl = await ConfigService.GetValorOrDefaultAsync("LOGO_FOUNDATION", "/assets/home.png");
var nameShort = await ConfigService.GetValorOrDefaultAsync("NAME_FOUNDATION_SHORT", "foundation_system");
var nameFoundation = await ConfigService.GetValorOrDefaultAsync("NAME_FOUNDATION", "foundation_system");
var descriptionShort = await ConfigService.GetValorOrDefaultAsync("DESCRIPTION_SHORT", "Fundacion sin fines de lucro");
var version = await ConfigService.GetValorOrDefaultAsync("VERSION_SYSTEM", "1.0.0");
}
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>@ViewData["Title"] - MIES</title>
<title>@ViewData["Title"] - @nameShort</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css"/>
<link rel="stylesheet" href="~/css/bootstrap-icons.min.css">
<link rel="stylesheet" href="~/css/all.min.css">
@@ -20,15 +28,16 @@
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-header">
<a class="sidebar-brand" asp-controller="Home" asp-action="Index">
<i class="bi bi-house-heart-fill me-2"></i>MIES
<a class="sidebar-brand d-flex align-items-center" asp-controller="Home" asp-action="Index">
<img src="@logoUrl" alt="Logo" class="me-2" style="height: 32px; width: auto; object-fit: contain;" />
<span>@nameShort</span>
</a>
</div>
<nav class="sidebar-nav p-3">
@await Component.InvokeAsync("Menu")
</nav>
<div class="sidebar-footer p-3 border-top border-secondary">
<small class="text-muted">v1.0.0 &copy; 2025</small>
<small class="text-muted">v @version &copy; 2026</small>
</div>
</aside>
@@ -53,7 +62,7 @@
<footer class="footer">
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center">
<span class="text-muted small">Misión Esperanza &copy; 2025 - Fundación para el desarrollo infantil.</span>
<span class="text-muted small">@nameFoundation &copy; 2026 - @descriptionShort.</span>
<div class="small text-muted">
<i class="bi bi-shield-check me-1"></i> Sistema Seguro
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 160 KiB