From 9d381f6f7630ed2e5c934c771866b186d5219611 Mon Sep 17 00:00:00 2001 From: fede Date: Sat, 9 Nov 2024 01:20:27 -0300 Subject: [PATCH 01/78] chore: regeneradas las entidades de propiedad contrato y estado propiedad Signed-off-by: fede --- Entidades/Alquilafacilcontext.cs | 29 +++++++++++++++++++++++++++++ Entidades/Contrato.cs | 2 ++ Entidades/Estadopropiedad.cs | 13 +++++++++++++ Entidades/Propiedade.cs | 4 ++++ 4 files changed, 48 insertions(+) create mode 100644 Entidades/Estadopropiedad.cs diff --git a/Entidades/Alquilafacilcontext.cs b/Entidades/Alquilafacilcontext.cs index a422bc2..74288dc 100644 --- a/Entidades/Alquilafacilcontext.cs +++ b/Entidades/Alquilafacilcontext.cs @@ -23,6 +23,8 @@ public partial class AlquilaFacilContext : DbContext public virtual DbSet Defectos { get; set; } + public virtual DbSet EstadoPropiedads { get; set; } + public virtual DbSet Estadodefectos { get; set; } public virtual DbSet Estadoventas { get; set; } @@ -158,6 +160,9 @@ public partial class AlquilaFacilContext : DbContext entity.Property(e => e.Fechainicio) .HasColumnType("date") .HasColumnName("fechainicio"); + entity.Property(e => e.Habilitado) + .HasColumnType("bit(1)") + .HasColumnName("habilitado"); entity.Property(e => e.Idpropiedad) .HasColumnType("int(11)") .HasColumnName("idpropiedad"); @@ -259,6 +264,20 @@ public partial class AlquilaFacilContext : DbContext .HasConstraintName("FK_DEF_EST"); }); + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PRIMARY"); + + entity.ToTable("EstadoPropiedad"); + + entity.Property(e => e.Id) + .HasColumnType("int(11)") + .HasColumnName("id"); + entity.Property(e => e.Descripcion) + .HasMaxLength(11) + .HasColumnName("descripcion"); + }); + modelBuilder.Entity(entity => { entity.HasKey(e => e.Id).HasName("PRIMARY"); @@ -385,6 +404,8 @@ public partial class AlquilaFacilContext : DbContext { entity.HasKey(e => e.Id).HasName("PRIMARY"); + entity.HasIndex(e => e.Idestado, "FK_PROP_EST"); + entity.HasIndex(e => e.Dnipropietario, "FK_PROP_PROPI"); entity.HasIndex(e => e.Idtipropiedad, "FK_PROP_TIPO"); @@ -398,6 +419,9 @@ public partial class AlquilaFacilContext : DbContext entity.Property(e => e.Dnipropietario) .HasColumnType("bigint(20)") .HasColumnName("dnipropietario"); + entity.Property(e => e.Idestado) + .HasColumnType("int(11)") + .HasColumnName("idestado"); entity.Property(e => e.Idtipropiedad) .HasColumnType("int(11)") .HasColumnName("idtipropiedad"); @@ -417,6 +441,11 @@ public partial class AlquilaFacilContext : DbContext .OnDelete(DeleteBehavior.Restrict) .HasConstraintName("FK_PROP_PROPI"); + entity.HasOne(d => d.IdestadoNavigation).WithMany(p => p.Propiedades) + .HasForeignKey(d => d.Idestado) + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("FK_PROP_EST"); + entity.HasOne(d => d.IdtipropiedadNavigation).WithMany(p => p.Propiedades) .HasForeignKey(d => d.Idtipropiedad) .OnDelete(DeleteBehavior.Restrict) diff --git a/Entidades/Contrato.cs b/Entidades/Contrato.cs index c7fd30f..2f0fdbc 100644 --- a/Entidades/Contrato.cs +++ b/Entidades/Contrato.cs @@ -25,6 +25,8 @@ public partial class Contrato public long? Idventa { get; set; } + public ulong Habilitado { get; set; } + public virtual ICollection Defectos { get; set; } = new List(); public virtual Cliente? DniinquilinoNavigation { get; set; } diff --git a/Entidades/Estadopropiedad.cs b/Entidades/Estadopropiedad.cs new file mode 100644 index 0000000..a220579 --- /dev/null +++ b/Entidades/Estadopropiedad.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; + +namespace Entidades; + +public partial class EstadoPropiedad +{ + public int Id { get; set; } + + public string Descripcion { get; set; } = null!; + + public virtual ICollection Propiedades { get; set; } = new List(); +} diff --git a/Entidades/Propiedade.cs b/Entidades/Propiedade.cs index d0abc4f..49bb2f8 100644 --- a/Entidades/Propiedade.cs +++ b/Entidades/Propiedade.cs @@ -19,10 +19,14 @@ public partial class Propiedade public int Idtipropiedad { get; set; } + public int? Idestado { get; set; } + public virtual ICollection Contratos { get; set; } = new List(); public virtual Cliente? DnipropietarioNavigation { get; set; } + public virtual EstadoPropiedad? IdestadoNavigation { get; set; } + public virtual TipoPropiedad IdtipropiedadNavigation { get; set; } = null!; public virtual ICollection Venta { get; set; } = new List(); -- 2.52.0 From 52e4afb7c45e221856d2f7434caf39b3ebb98735 Mon Sep 17 00:00:00 2001 From: fede Date: Mon, 11 Nov 2024 11:55:45 -0300 Subject: [PATCH 02/78] primeros cambios --- Aspnet/AlquilaFacil.csproj | 1 + Aspnet/Aspnet.sln | 25 ++++++++++++++++++ Aspnet/Controllers/PropiedadesController.cs | 8 ++++++ Aspnet/Controllers/PropietarioController.cs | 29 +++++++++++++++++---- Entidades/Dto/PropiedadDto.cs | 9 +++++++ Modelo/RepositorioBase.cs | 6 ++--- Modelo/RepositorioInquilinos.cs | 4 ++- Modelo/RepositorioPropiedades.cs | 17 ++++++++++-- Modelo/RepositorioPropietario.cs | 13 +++++++++ Modelo/RepositorioUsuarios.cs | 25 +++++++++++------- 10 files changed, 117 insertions(+), 20 deletions(-) create mode 100644 Aspnet/Aspnet.sln create mode 100644 Entidades/Dto/PropiedadDto.cs create mode 100644 Modelo/RepositorioPropietario.cs diff --git a/Aspnet/AlquilaFacil.csproj b/Aspnet/AlquilaFacil.csproj index 842f7c7..a513915 100644 --- a/Aspnet/AlquilaFacil.csproj +++ b/Aspnet/AlquilaFacil.csproj @@ -9,6 +9,7 @@ + diff --git a/Aspnet/Aspnet.sln b/Aspnet/Aspnet.sln new file mode 100644 index 0000000..baec485 --- /dev/null +++ b/Aspnet/Aspnet.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AlquilaFacil", "AlquilaFacil.csproj", "{76BA8B31-BAD3-49CD-B8B8-BE22D8AEA281}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {76BA8B31-BAD3-49CD-B8B8-BE22D8AEA281}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {76BA8B31-BAD3-49CD-B8B8-BE22D8AEA281}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76BA8B31-BAD3-49CD-B8B8-BE22D8AEA281}.Release|Any CPU.ActiveCfg = Release|Any CPU + {76BA8B31-BAD3-49CD-B8B8-BE22D8AEA281}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {CF93AFAC-32EF-4993-84A2-CA2EB32F58FF} + EndGlobalSection +EndGlobal diff --git a/Aspnet/Controllers/PropiedadesController.cs b/Aspnet/Controllers/PropiedadesController.cs index b81634e..2c35437 100644 --- a/Aspnet/Controllers/PropiedadesController.cs +++ b/Aspnet/Controllers/PropiedadesController.cs @@ -1,8 +1,16 @@ using Microsoft.AspNetCore.Mvc; +using Modelo; namespace AlquilaFacil.Controllers; [ApiController] public class PropiedadesController: ControllerBase { + [HttpGet("api/propiedades")] + public IActionResult ListarPropietarios([FromHeader(Name = "Auth")] string Auth) { + if (String.IsNullOrEmpty(Auth)) return Unauthorized(); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + if (validacion1 == false) return Unauthorized(); + var ret = RepositorioPropiedades.Singleton.ListarPropiedades(); + } } \ No newline at end of file diff --git a/Aspnet/Controllers/PropietarioController.cs b/Aspnet/Controllers/PropietarioController.cs index 6b0d395..84a54d4 100644 --- a/Aspnet/Controllers/PropietarioController.cs +++ b/Aspnet/Controllers/PropietarioController.cs @@ -10,11 +10,6 @@ namespace AlquilaFacil.Controllers; [ApiController] public class PropietarioController: ControllerBase { - [HttpGet("api/propietario")] - public IActionResult ListarPropietarios([FromHeader(Name = "Auth")] string Auth) { - return Ok(); - } - [HttpPost("api/propietarios")] public IActionResult AltaPropietario([FromBody]CrearClienteDto Propietario,[FromHeader(Name = "Auth")] string Auth) { if (String.IsNullOrEmpty(Auth)) return Unauthorized(); @@ -38,6 +33,30 @@ public class PropietarioController: ControllerBase { return ret ? Ok(new {message = "Se añadio el propietario exitosamente"}) : BadRequest(); } + + [HttpPatch("api/propietarios")] + public IActionResult PatchPropietario([FromBody]CrearClienteDto Propietario, [FromHeader(Name = "Auth")] string Auth){ + if (String.IsNullOrEmpty(Auth)) return Unauthorized(); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + if (validacion1 == false) return Unauthorized(); + + string validacion2 = verificarCrearUsuario(Propietario); + if (validacion2 != "") return BadRequest(validacion2); + + var cli = new Cliente { + Dni = Propietario.dni, + Nombre = Propietario.nombre, + Domicilio = Propietario.domicilio, + Apellido = Propietario.apellido, + Celular = Propietario.celular, + Email = Propietario.email, + Contraseña = Encoding.UTF8.GetBytes(HacerHash(Propietario.contraseña)) + }; + var ret = RepositorioUsuarios.Singleton.ActualizarPropietario(cli); + return ret ? + Ok(new {message = "Se Modifico el propietario exitosamente"}) : BadRequest(); + } + private string verificarCrearUsuario(CrearClienteDto cid) { string msg = ""; diff --git a/Entidades/Dto/PropiedadDto.cs b/Entidades/Dto/PropiedadDto.cs new file mode 100644 index 0000000..fef97c8 --- /dev/null +++ b/Entidades/Dto/PropiedadDto.cs @@ -0,0 +1,9 @@ +namespace Entidades.Dto; +public class PropiedadDto { + public string Ubicacion { get; set; } = null!; + public int Canthabitaciones { get; set; } + public int? Piso { get; set; } + public string? Letra { get; set; } + public string Email { get; set; } + public int Idtipropiedad { get; set; } +} \ No newline at end of file diff --git a/Modelo/RepositorioBase.cs b/Modelo/RepositorioBase.cs index 2315c13..6454fa3 100644 --- a/Modelo/RepositorioBase.cs +++ b/Modelo/RepositorioBase.cs @@ -7,11 +7,11 @@ namespace Modelo; public abstract class RepositorioBase where S : new() { - protected AlquilaFacilContext Context { get{ return new AlquilaFacilContext();}} + protected AlquilaFacilContext Context { get { return new AlquilaFacilContext(); }} private static readonly S instance = new(); - public static S Singleton { get{return instance;}} + public static S Singleton { get { return instance; }} - public bool Guardar(AlquilaFacilContext context){ + public bool Guardar(AlquilaFacilContext context) { bool ret = false; try { diff --git a/Modelo/RepositorioInquilinos.cs b/Modelo/RepositorioInquilinos.cs index 809b15e..48de959 100644 --- a/Modelo/RepositorioInquilinos.cs +++ b/Modelo/RepositorioInquilinos.cs @@ -8,7 +8,9 @@ public class RepositorioInquilinos: RepositorioBase { { FormattableString sqlq = $""" - SELECT I.Dni, I.Nombre, I.Apellido FROM Inquilinos + SELECT I.Dni, I.Nombre, I.Apellido FROM Clientes I + JOIN cliente_Grupos cg on cg.idcliente = I.Dni + WHERE cg.idgrupo = 2; """; return Context.Database.SqlQuery(sqlq); } diff --git a/Modelo/RepositorioPropiedades.cs b/Modelo/RepositorioPropiedades.cs index b911e46..3375117 100644 --- a/Modelo/RepositorioPropiedades.cs +++ b/Modelo/RepositorioPropiedades.cs @@ -1,9 +1,22 @@ +using Entidades; +using Microsoft.EntityFrameworkCore; using Modelo; public class RepositorioPropiedades: RepositorioBase { + public IQueryable ListarPropiedades(){ + var con = Context; + return con.Propiedades.AsQueryable(); + } - public bool AñadirPropiedad(){ - return false; + public bool AñadirPropiedad(Propiedade prop){ + var con = Context; + + int count = con.Propiedades.Count()+1; + + prop.Id = count; + prop.Idestado = 1; + con.Propiedades.Add(prop); + return Guardar(con); } } \ No newline at end of file diff --git a/Modelo/RepositorioPropietario.cs b/Modelo/RepositorioPropietario.cs new file mode 100644 index 0000000..56e8510 --- /dev/null +++ b/Modelo/RepositorioPropietario.cs @@ -0,0 +1,13 @@ +using Entidades; +using Microsoft.EntityFrameworkCore; +using Modelo; + +public class RepositorioPropietario: RepositorioBase { + public Cliente? ObtenerClientePorEmail(string email){ + var con = Context; + + Cliente? cli = con.Clientes.FirstOrDefault(c => c.Email == email); + return cli; + } + +} diff --git a/Modelo/RepositorioUsuarios.cs b/Modelo/RepositorioUsuarios.cs index b139d04..1a0a657 100644 --- a/Modelo/RepositorioUsuarios.cs +++ b/Modelo/RepositorioUsuarios.cs @@ -7,9 +7,8 @@ using System.Reflection.Metadata.Ecma335; namespace Modelo; -public class RepositorioUsuarios: RepositorioBase -{ - public bool AltaInquilino(Cliente cli){ +public class RepositorioUsuarios: RepositorioBase { + public bool AltaInquilino(Cliente cli) { var con = Context; //check por si la cuenta ya existe (puede ser propietario) @@ -34,8 +33,7 @@ public class RepositorioUsuarios: RepositorioBase } - public bool AltaPropietario(Cliente cli) - { + public bool AltaPropietario(Cliente cli) { var con = Context; //check por si la cuenta ya existe (puede ser propietario) @@ -58,6 +56,16 @@ public class RepositorioUsuarios: RepositorioBase return Guardar(con); } + public bool ActualizarPropietario(Cliente cli) { + var con = Context; + Cliente? cliOld = con.Clientes.Find(cli.Dni); + if (cliOld == null) return false; + + cliOld = cli; + + } + + public bool CheckUsuario(LoginDto logindto) { string Contraseña = HacerHash(logindto.Contraseña); @@ -71,12 +79,12 @@ public class RepositorioUsuarios: RepositorioBase return false; } - private string HacerHash(string pass){ + private string HacerHash(string pass) { var buf = SHA256.HashData(Encoding.UTF8.GetBytes(pass)); return BitConverter.ToString(buf).Replace("-",""); } - public bool CheckToken(string email, string token){ + public bool CheckToken(string email, string token) { var usu = Context.Clientes.FirstOrDefault(x => x.Email == email); if (usu == null) return false; @@ -87,8 +95,7 @@ public class RepositorioUsuarios: RepositorioBase return usu.Token == token; } - public void GuardarToken(LoginDto login, string tokenString) - { + public void GuardarToken(LoginDto login, string tokenString) { var con = Context; var usu = con.Clientes.FirstOrDefault(x => x.Email == login.Email); if (usu == null) return; -- 2.52.0 From d2adb7733e70dee7bec8e745ef37af484235be14 Mon Sep 17 00:00:00 2001 From: fede Date: Sun, 17 Nov 2024 02:04:26 -0300 Subject: [PATCH 03/78] =?UTF-8?q?nom=C3=A1s=20me=20faltan=20los=20put=20pa?= =?UTF-8?q?ra=20propiedades?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: fede --- Aspnet/Controllers/AccionesController.cs | 2 +- Aspnet/Controllers/PropiedadesController.cs | 89 +++++++++++++++++++++ Aspnet/Controllers/PropietarioController.cs | 14 +++- Aspnet/makefile | 2 +- Entidades/Alquilafacilcontext.cs | 4 + Entidades/Cliente.cs | 2 + Entidades/Dto/AltaPropiedadDto.cs | 9 +++ Entidades/Dto/PropiedadDto.cs | 9 --- Entidades/Dto/PropiedadesDto.cs | 9 +++ Modelo/RepositorioPropiedades.cs | 56 ++++++++++++- Modelo/RepositorioPropietario.cs | 31 ++++++- Modelo/RepositorioUsuarios.cs | 7 +- 12 files changed, 211 insertions(+), 23 deletions(-) create mode 100644 Entidades/Dto/AltaPropiedadDto.cs delete mode 100644 Entidades/Dto/PropiedadDto.cs create mode 100644 Entidades/Dto/PropiedadesDto.cs diff --git a/Aspnet/Controllers/AccionesController.cs b/Aspnet/Controllers/AccionesController.cs index 240f30c..4772e51 100644 --- a/Aspnet/Controllers/AccionesController.cs +++ b/Aspnet/Controllers/AccionesController.cs @@ -7,10 +7,10 @@ namespace AlquilaFacil.Controllers; [ApiController] public class AccionesController: ControllerBase { + //Reutilizo el loginDto pero no lleno el campo de contraseña [HttpPost("api/acciones")] public IActionResult ListarAccionesPorUsuario([FromBody] LoginDto email, [FromHeader(Name = "Auth")] string Auth) { if (email.Email == "" || email.Email == null) return BadRequest(); - if (Auth == "") return Unauthorized(new { esValido = false}); diff --git a/Aspnet/Controllers/PropiedadesController.cs b/Aspnet/Controllers/PropiedadesController.cs index 2c35437..77d05a0 100644 --- a/Aspnet/Controllers/PropiedadesController.cs +++ b/Aspnet/Controllers/PropiedadesController.cs @@ -1,3 +1,6 @@ +using Entidades; +using Entidades.Dto; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using Modelo; @@ -12,5 +15,91 @@ public class PropiedadesController: ControllerBase { if (validacion1 == false) return Unauthorized(); var ret = RepositorioPropiedades.Singleton.ListarPropiedades(); + return Ok(ret); + } + + [HttpGet("api/propiedad")] + public IActionResult ObtenerPropiedadPorId(int Id, [FromHeader(Name = "Auth")] string Auth) { + if (String.IsNullOrEmpty(Auth)) return Unauthorized(); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + if (validacion1 == false) return Unauthorized(); + + if (Id < 0) return BadRequest("la id de propiedad no puede ser negativa"); + + var ret = RepositorioPropiedades.Singleton.ObtenerPropiedadPorId(Id); + if (ret == null) return BadRequest("No existe la propiedad"); + return Ok(ret); + } + + [HttpGet("api/propiedades/Propietario")] + public IActionResult ObtenerPropiedadesPorPropietario( + [FromBody] string email, + [FromHeader(Name = "Auth")] string Auth) { + + if (String.IsNullOrEmpty(Auth)) return Unauthorized(); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + if (validacion1 == false) return Unauthorized(); + + email = email.Trim(); + if (String.IsNullOrEmpty(email)) return BadRequest("falta campo email"); + + var ret = RepositorioPropiedades.Singleton.ObtenerPropiedadesPorEmail(email); + + return Ok(ret); + } + + [HttpPost("api/propiedad")] + public IActionResult AltaPropiedad([FromBody] AltaPropiedadDto propiedad, [FromHeader(Name = "Auth")] string Auth) { + if (String.IsNullOrEmpty(Auth)) return Unauthorized(); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + if (validacion1 == false) return Unauthorized(); + + string validacion2 = ValidarPropiedad(propiedad); + if (validacion2 != "") return BadRequest(new { message = validacion2 }); + + Cliente? cli = RepositorioPropietario.Singleton.ObtenerPropietarioPorEmail(propiedad.Email); + if (cli == null) return BadRequest("El email no corresponde a un propietario"); + + Propiedade Prop = new Propiedade{ + Canthabitaciones = propiedad.Canthabitaciones, + Dnipropietario = cli.Dni, + Idtipropiedad = propiedad.Idtipropiedad, + Ubicacion = propiedad.Ubicacion, + Letra = propiedad.Letra != null ? propiedad.Letra : null, + Piso = propiedad.Piso != null ? propiedad.Piso : null, + }; + + var ret = RepositorioPropiedades.Singleton.AñadirPropiedad(Prop); + return (ret)? + Ok("Fue Cargado Correctamente") : + BadRequest("Fallo al momento de añadir la propiedad a la base de datos"); + } + + [HttpDelete("api/propiedad")] + public IActionResult BajaPropiedad(int id, [FromHeader(Name = "Auth")] string Auth){ + if (String.IsNullOrEmpty(Auth)) return Unauthorized(); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, "/accion/2"); + if (validacion1 == false) return Unauthorized(); + + if (id <= 0) return BadRequest("No es una id valida"); + + var ret = RepositorioPropiedades.Singleton.BajaPropiedad(id); + + return ret ? + Ok(new {message = $"la propiedad con id {id} fue dada de baja"}): + BadRequest("Fallo al dar de baja la propiedad"); + } + + private string ValidarPropiedad(AltaPropiedadDto prop) { + if (prop == null) return "Esta mal formado el body de la request"; + + string ret = ""; + if (String.IsNullOrEmpty(prop.Email)) ret += "Falta Definir un email de propietario\n"; + if (prop.Canthabitaciones < 0) ret += "No se puede tener una cantidad de habitaciones negativa\n"; + if (prop.Idtipropiedad <= 0) ret += "No tiene un tipo de propiedad asociada"; + if (String.IsNullOrEmpty(prop.Ubicacion)) ret += "Tiene que definir la ubicacion de la propiedad\n"; + + return ret; + } } \ No newline at end of file diff --git a/Aspnet/Controllers/PropietarioController.cs b/Aspnet/Controllers/PropietarioController.cs index 84a54d4..0023501 100644 --- a/Aspnet/Controllers/PropietarioController.cs +++ b/Aspnet/Controllers/PropietarioController.cs @@ -2,6 +2,7 @@ using System.Security.Cryptography; using System.Text; using Entidades; using Entidades.Dto; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using Modelo; @@ -10,8 +11,19 @@ namespace AlquilaFacil.Controllers; [ApiController] public class PropietarioController: ControllerBase { + [HttpGet("api/propietario")] + public IActionResult ObtenerPropietarioPorDni(long Dni, [FromHeader(Name ="Auth")] string Auth) { + if (String.IsNullOrEmpty(Auth)) return Unauthorized(); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + if (validacion1 == false) return Unauthorized(); + + var ret = RepositorioPropietario.Singleton.ObtenerPropietarioPorDni(Dni); + return Ok(ret); + } + [HttpPost("api/propietarios")] - public IActionResult AltaPropietario([FromBody]CrearClienteDto Propietario,[FromHeader(Name = "Auth")] string Auth) { + public IActionResult AltaPropietario([FromBody]CrearClienteDto Propietario, + [FromHeader(Name = "Auth")] string Auth) { if (String.IsNullOrEmpty(Auth)) return Unauthorized(); var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); if (validacion1 == false) return Unauthorized(); diff --git a/Aspnet/makefile b/Aspnet/makefile index 46aa736..e528264 100644 --- a/Aspnet/makefile +++ b/Aspnet/makefile @@ -1,2 +1,2 @@ run: - dotnet watch run + dotnet watch run --project AlquilaFacil.csproj diff --git a/Entidades/Alquilafacilcontext.cs b/Entidades/Alquilafacilcontext.cs index 74288dc..2966b4e 100644 --- a/Entidades/Alquilafacilcontext.cs +++ b/Entidades/Alquilafacilcontext.cs @@ -101,6 +101,10 @@ public partial class AlquilaFacilContext : DbContext entity.Property(e => e.Email) .HasMaxLength(50) .HasColumnName("email"); + entity.Property(e => e.Habilitado) + .HasDefaultValueSql("b'1'") + .HasColumnType("bit(1)") + .HasColumnName("habilitado"); entity.Property(e => e.Nombre) .HasMaxLength(20) .HasColumnName("nombre"); diff --git a/Entidades/Cliente.cs b/Entidades/Cliente.cs index e4a2b2d..ef52593 100644 --- a/Entidades/Cliente.cs +++ b/Entidades/Cliente.cs @@ -21,6 +21,8 @@ public partial class Cliente public string? Token { get; set; } + public ulong Habilitado { get; set; } + public virtual ICollection ContratoDniinquilinoNavigations { get; set; } = new List(); public virtual ICollection ContratoDnipropietarioNavigations { get; set; } = new List(); diff --git a/Entidades/Dto/AltaPropiedadDto.cs b/Entidades/Dto/AltaPropiedadDto.cs new file mode 100644 index 0000000..86865ce --- /dev/null +++ b/Entidades/Dto/AltaPropiedadDto.cs @@ -0,0 +1,9 @@ +namespace Entidades.Dto; +public class AltaPropiedadDto { + public string Ubicacion { get; set; } = null!; + public int Canthabitaciones { get; set; } + public int? Piso { get; set; } = null; + public string? Letra { get; set; } = null; + public string Email { get; set; } = string.Empty; + public int Idtipropiedad { get; set; } +} \ No newline at end of file diff --git a/Entidades/Dto/PropiedadDto.cs b/Entidades/Dto/PropiedadDto.cs deleted file mode 100644 index fef97c8..0000000 --- a/Entidades/Dto/PropiedadDto.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Entidades.Dto; -public class PropiedadDto { - public string Ubicacion { get; set; } = null!; - public int Canthabitaciones { get; set; } - public int? Piso { get; set; } - public string? Letra { get; set; } - public string Email { get; set; } - public int Idtipropiedad { get; set; } -} \ No newline at end of file diff --git a/Entidades/Dto/PropiedadesDto.cs b/Entidades/Dto/PropiedadesDto.cs new file mode 100644 index 0000000..d63e3a9 --- /dev/null +++ b/Entidades/Dto/PropiedadesDto.cs @@ -0,0 +1,9 @@ +namespace Entidades.Dto; +public class PropiedadesDto { + public int id { get; set; } + public string Ubicacion { get; set; } = ""; + public int canthabitaciones { get; set; } + public string piso { get; set; } = ""; + public string letra { get; set; } = ""; + public string TipoPropiedad { get; set; } = ""; +} \ No newline at end of file diff --git a/Modelo/RepositorioPropiedades.cs b/Modelo/RepositorioPropiedades.cs index 3375117..ab6b5d1 100644 --- a/Modelo/RepositorioPropiedades.cs +++ b/Modelo/RepositorioPropiedades.cs @@ -1,15 +1,43 @@ +using System; +using System.Reflection.Metadata.Ecma335; using Entidades; +using Entidades.Dto; using Microsoft.EntityFrameworkCore; using Modelo; public class RepositorioPropiedades: RepositorioBase { - public IQueryable ListarPropiedades(){ - var con = Context; - return con.Propiedades.AsQueryable(); + + public IQueryable ListarPropiedades(){ + FormattableString sqlq = $""" + SELECT p.id, p.ubicacion, p.canthabitaciones, p.piso, p.letra, tp.descripcion AS TipoPropiedad FROM Propiedades p + JOIN EstadoPropiedad ep ON p.idestado = 1 + JOIN TipoPropiedad tp ON p.idtipropiedad = tp.id + """; + + var ret = Context.Database.SqlQuery(sqlq); + return ret; } - public bool AñadirPropiedad(Propiedade prop){ + public Propiedade? ObtenerPropiedadPorId(int Id) { + + FormattableString sqlq = $""" + SELECT * FROM Propiedades p + WHERE p.id = {Id} + LIMIT 1 + """; + + Propiedade? prop = Context.Database.SqlQuery(sqlq).First(); + if (prop == null || prop.Id == 0) { + return null; + } + + return prop; + } + + public bool AñadirPropiedad(Propiedade? prop){ + if (prop == null) return false; + var con = Context; int count = con.Propiedades.Count()+1; @@ -19,4 +47,24 @@ public class RepositorioPropiedades: RepositorioBase con.Propiedades.Add(prop); return Guardar(con); } + + public IQueryable ObtenerPropiedadesPorEmail(string email) { + FormattableString sqlq = $""" + SELECT p.id, p.ubicacion, p.canthabitaciones, p.piso, p.letra, tp.descripcion as TipoPropiedad From Propiedades p + JOIN Clientes c ON c.dni = p.dnipropietario + JOIN TipoPropiedad tp ON tp.id = p.idtipropiedad + WHERE c.email = {email} + """; + var ret = Context.Database.SqlQuery(sqlq); + return ret; + } + + public bool BajaPropiedad(int id) { + var con = Context; + Propiedade prop = con.Propiedades.Find(id); + prop.Idestado = 3; + + return Guardar(con); + + } } \ No newline at end of file diff --git a/Modelo/RepositorioPropietario.cs b/Modelo/RepositorioPropietario.cs index 56e8510..5b02fd5 100644 --- a/Modelo/RepositorioPropietario.cs +++ b/Modelo/RepositorioPropietario.cs @@ -1,13 +1,38 @@ +using System; using Entidades; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; using Modelo; public class RepositorioPropietario: RepositorioBase { - public Cliente? ObtenerClientePorEmail(string email){ - var con = Context; + public Cliente? ObtenerPropietarioPorDni(long Dni){ + + if (Dni < 1) return null; - Cliente? cli = con.Clientes.FirstOrDefault(c => c.Email == email); + FormattableString sqlq = $""" + SELECT * FROM Clientes c + JOIN cliente_Grupos cg ON cg.idgrupo = 1 + WHERE c.dni = {Dni} + LIMIT 1 + """; + + Cliente? cli = Context.Database.SqlQuery(sqlq).First(); + if (cli.Dni == 0 || cli == null) return null; return cli; + } + public Cliente? ObtenerPropietarioPorEmail(string email){ + + FormattableString sqlq = $""" + SELECT * FROM Clientes c + JOIN cliente_Grupos cg ON cg.idgrupo = 1 + WHERE c.email = {email} + LIMIT 1 + """; + + Cliente? cli = Context.Database.SqlQuery(sqlq).First(); + if (cli.Dni == 0 || cli == null) return null; + return cli; + } } diff --git a/Modelo/RepositorioUsuarios.cs b/Modelo/RepositorioUsuarios.cs index 1a0a657..9ec7cd3 100644 --- a/Modelo/RepositorioUsuarios.cs +++ b/Modelo/RepositorioUsuarios.cs @@ -1,10 +1,8 @@ -using System.Collections.ObjectModel; using System.Security.Cryptography; using System.Text; using Entidades.Dto; using Entidades; -using System.Reflection.Metadata.Ecma335; - + namespace Modelo; public class RepositorioUsuarios: RepositorioBase { @@ -60,9 +58,10 @@ public class RepositorioUsuarios: RepositorioBase { var con = Context; Cliente? cliOld = con.Clientes.Find(cli.Dni); if (cliOld == null) return false; + if (cli.Dni != cliOld.Dni) return false; cliOld = cli; - + return Guardar(con); } -- 2.52.0 From 32c13d91b8d1e2c42d89beae886f9f1c20368ea2 Mon Sep 17 00:00:00 2001 From: fede Date: Sun, 17 Nov 2024 12:25:47 -0300 Subject: [PATCH 04/78] =?UTF-8?q?chore:=20A=C3=B1adida=20entidad=20de=20se?= =?UTF-8?q?rvicios=20que=20me=20olvid=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Entidades/Alquilafacilcontext.cs | 38 ++++++++++++++++++++++++++++++++ Entidades/Propiedade.cs | 2 ++ Entidades/Servicio.cs | 13 +++++++++++ 3 files changed, 53 insertions(+) create mode 100644 Entidades/Servicio.cs diff --git a/Entidades/Alquilafacilcontext.cs b/Entidades/Alquilafacilcontext.cs index 2966b4e..9aa070d 100644 --- a/Entidades/Alquilafacilcontext.cs +++ b/Entidades/Alquilafacilcontext.cs @@ -39,6 +39,8 @@ public partial class AlquilaFacilContext : DbContext public virtual DbSet Recibos { get; set; } + public virtual DbSet Servicios { get; set; } + public virtual DbSet TipoPropiedads { get; set; } public virtual DbSet Ventas { get; set; } @@ -473,6 +475,42 @@ public partial class AlquilaFacilContext : DbContext .HasColumnName("monto"); }); + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PRIMARY"); + + entity.Property(e => e.Id) + .HasColumnType("int(11)") + .HasColumnName("id"); + entity.Property(e => e.Descripcion) + .HasMaxLength(20) + .HasColumnName("descripcion"); + + entity.HasMany(d => d.IdPropiedads).WithMany(p => p.IdServicios) + .UsingEntity>( + "ServicioPropiedad", + r => r.HasOne().WithMany() + .HasForeignKey("IdPropiedad") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("Servicio_Propiedad_ibfk_2"), + l => l.HasOne().WithMany() + .HasForeignKey("IdServicio") + .OnDelete(DeleteBehavior.Restrict) + .HasConstraintName("Servicio_Propiedad_ibfk_1"), + j => + { + j.HasKey("IdServicio", "IdPropiedad").HasName("PRIMARY"); + j.ToTable("Servicio_Propiedad"); + j.HasIndex(new[] { "IdPropiedad" }, "idPropiedad"); + j.IndexerProperty("IdServicio") + .HasColumnType("int(11)") + .HasColumnName("idServicio"); + j.IndexerProperty("IdPropiedad") + .HasColumnType("int(11)") + .HasColumnName("idPropiedad"); + }); + }); + modelBuilder.Entity(entity => { entity.HasKey(e => e.Id).HasName("PRIMARY"); diff --git a/Entidades/Propiedade.cs b/Entidades/Propiedade.cs index 49bb2f8..8d47663 100644 --- a/Entidades/Propiedade.cs +++ b/Entidades/Propiedade.cs @@ -30,4 +30,6 @@ public partial class Propiedade public virtual TipoPropiedad IdtipropiedadNavigation { get; set; } = null!; public virtual ICollection Venta { get; set; } = new List(); + + public virtual ICollection IdServicios { get; set; } = new List(); } diff --git a/Entidades/Servicio.cs b/Entidades/Servicio.cs new file mode 100644 index 0000000..80bfdf1 --- /dev/null +++ b/Entidades/Servicio.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; + +namespace Entidades; + +public partial class Servicio +{ + public int Id { get; set; } + + public string Descripcion { get; set; } = null!; + + public virtual ICollection IdPropiedads { get; set; } = new List(); +} -- 2.52.0 From ef93c52a5a27c16669c77f92047c8caad7e8b7d4 Mon Sep 17 00:00:00 2001 From: fede Date: Mon, 18 Nov 2024 16:08:36 -0300 Subject: [PATCH 05/78] feat: primera implementacion propiedades ABM Signed-off-by: fede --- Aspnet/Controllers/PropiedadesController.cs | 47 ++++++++++++++++++++- Aspnet/Controllers/ServiciosController.cs | 12 ++++++ Entidades/Dto/ServicioAPropiedadDto.cs | 2 + Entidades/Servicio.cs | 2 + Modelo/RepositorioPropiedades.cs | 34 +++++++++++++++ Modelo/RepositorioPropietario.cs | 2 + Modelo/RepositorioServicios.cs | 11 +++++ 7 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 Aspnet/Controllers/ServiciosController.cs create mode 100644 Entidades/Dto/ServicioAPropiedadDto.cs create mode 100644 Modelo/RepositorioServicios.cs diff --git a/Aspnet/Controllers/PropiedadesController.cs b/Aspnet/Controllers/PropiedadesController.cs index 77d05a0..40d872e 100644 --- a/Aspnet/Controllers/PropiedadesController.cs +++ b/Aspnet/Controllers/PropiedadesController.cs @@ -78,7 +78,7 @@ public class PropiedadesController: ControllerBase { [HttpDelete("api/propiedad")] public IActionResult BajaPropiedad(int id, [FromHeader(Name = "Auth")] string Auth){ if (String.IsNullOrEmpty(Auth)) return Unauthorized(); - var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, "/accion/2"); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); if (validacion1 == false) return Unauthorized(); if (id <= 0) return BadRequest("No es una id valida"); @@ -90,6 +90,51 @@ public class PropiedadesController: ControllerBase { BadRequest("Fallo al dar de baja la propiedad"); } + [HttpPut("api/propiedades/addServicio")] + public IActionResult AñadirServicio([FromBody] ServicioAPropiedadDto Servicios, [FromHeader(Name = "Auth")] string Auth) { + if (String.IsNullOrEmpty(Auth)) return Unauthorized(); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + if (validacion1 == false) return Unauthorized(); + + if (Servicios.propiedadid <= 0) return BadRequest("No puede tener una id negativa o cero"); + if (Servicios.idServicios.Count() < 1) return BadRequest("Falta añadir servicios"); + if (Servicios.idServicios.Any(x => x<= 0)) return BadRequest("No tienen haber ids negativas o cero de servicio"); + + var serv = RepositorioServicios.Singleton.ObtenerServiciosPorPropiedad(Servicios.propiedadid); + + bool validacion2 = Servicios.idServicios.Any(x => serv.Contains(x)); + + if (validacion2 == true) return BadRequest("Hay elementos repetidos"); + + bool ret = RepositorioPropiedades. + Singleton.AñadirServicioAPropiedad(Servicios.propiedadid, Servicios.idServicios); + + return ret ? + Ok("Los Servicios Se Cargaron correctamente a la propiedad") : BadRequest("No se pudo Cargar los Servicios a la propiedad"); + + } + + [HttpPut("api/propiedades/RmServicio")] + public IActionResult EliminarServicio([FromBody] ServicioAPropiedadDto servicio, [FromHeader(Name = "Auth")] string Auth) { + if (String.IsNullOrEmpty(Auth)) return Unauthorized(); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + if (validacion1 == false) return Unauthorized(); + + if (servicio.propiedadid <= 0) return BadRequest("No puede tener una id negativa o cero"); + if (servicio.idServicios.Count() < 1) return BadRequest("Falta añadir servicios"); + if (servicio.idServicios.Any(x => x<= 0)) return BadRequest("No tienen haber ids negativas o cero de servicio"); + + var serv = RepositorioServicios.Singleton.ObtenerServiciosPorPropiedad(servicio.propiedadid); + + var repetidos = serv.Intersect(servicio.idServicios); + + bool ret = RepositorioPropiedades.Singleton.BajaServiciosAPropiedad(servicio.propiedadid, servicio.idServicios); + + return ret ? + Ok("Se Eliminaron los servicios seleccionados de la propiedad") : BadRequest("Fallo al eliminarse los servicios de la propiedad"); + + } + private string ValidarPropiedad(AltaPropiedadDto prop) { if (prop == null) return "Esta mal formado el body de la request"; diff --git a/Aspnet/Controllers/ServiciosController.cs b/Aspnet/Controllers/ServiciosController.cs new file mode 100644 index 0000000..fea5d8b --- /dev/null +++ b/Aspnet/Controllers/ServiciosController.cs @@ -0,0 +1,12 @@ +using Entidades; +using Entidades.Dto; +using Microsoft.AspNetCore.Http.HttpResults; +using Microsoft.AspNetCore.Mvc; +using Modelo; + +namespace AlquilaFacil.Controllers; + +[ApiController] +public class ServiciosController: ControllerBase { + +} \ No newline at end of file diff --git a/Entidades/Dto/ServicioAPropiedadDto.cs b/Entidades/Dto/ServicioAPropiedadDto.cs new file mode 100644 index 0000000..79cd5ab --- /dev/null +++ b/Entidades/Dto/ServicioAPropiedadDto.cs @@ -0,0 +1,2 @@ +namespace Entidades.Dto; +public record ServicioAPropiedadDto(int propiedadid, List idServicios); \ No newline at end of file diff --git a/Entidades/Servicio.cs b/Entidades/Servicio.cs index 80bfdf1..b0246e0 100644 --- a/Entidades/Servicio.cs +++ b/Entidades/Servicio.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Text.Json.Serialization; namespace Entidades; @@ -9,5 +10,6 @@ public partial class Servicio public string Descripcion { get; set; } = null!; + [JsonIgnore] public virtual ICollection IdPropiedads { get; set; } = new List(); } diff --git a/Modelo/RepositorioPropiedades.cs b/Modelo/RepositorioPropiedades.cs index ab6b5d1..ea3f720 100644 --- a/Modelo/RepositorioPropiedades.cs +++ b/Modelo/RepositorioPropiedades.cs @@ -59,6 +59,21 @@ public class RepositorioPropiedades: RepositorioBase return ret; } + public bool AñadirServicioAPropiedad(int idprop, List idserv){ + var con = Context; + Propiedade? prop = con.Propiedades.Find(idprop); + if (prop == null) return false; + + foreach (int id in idserv) { + Servicio? servicio = con.Servicios.Find(id); + if (servicio == null) return false; + + prop.IdServicios.Add(servicio); + } + + return Guardar(con); + } + public bool BajaPropiedad(int id) { var con = Context; Propiedade prop = con.Propiedades.Find(id); @@ -67,4 +82,23 @@ public class RepositorioPropiedades: RepositorioBase return Guardar(con); } + + public bool BajaServiciosAPropiedad(int idprop, List idserv) + { + var con = Context; + Propiedade? prop = con.Propiedades.Include(x=>x.IdServicios).FirstOrDefault(x => x.Id == idprop); + if (prop == null) return false; + + + foreach (int id in idserv) { + Servicio? servicio = con.Servicios.Find(id); + if (servicio == null) return false; + + if (prop.IdServicios.Contains(servicio)){ + prop.IdServicios.Remove(servicio); + } + } + + return Guardar(con); + } } \ No newline at end of file diff --git a/Modelo/RepositorioPropietario.cs b/Modelo/RepositorioPropietario.cs index 5b02fd5..e50df24 100644 --- a/Modelo/RepositorioPropietario.cs +++ b/Modelo/RepositorioPropietario.cs @@ -17,6 +17,7 @@ public class RepositorioPropietario: RepositorioBase { """; Cliente? cli = Context.Database.SqlQuery(sqlq).First(); + if (cli == null|| cli.Dni == 0) return null; if (cli.Dni == 0 || cli == null) return null; return cli; @@ -32,6 +33,7 @@ public class RepositorioPropietario: RepositorioBase { """; Cliente? cli = Context.Database.SqlQuery(sqlq).First(); + if (cli == null|| cli.Dni == 0) return null; if (cli.Dni == 0 || cli == null) return null; return cli; } diff --git a/Modelo/RepositorioServicios.cs b/Modelo/RepositorioServicios.cs new file mode 100644 index 0000000..8f1c799 --- /dev/null +++ b/Modelo/RepositorioServicios.cs @@ -0,0 +1,11 @@ +using Modelo; +using Entidades; +public class RepositorioServicios: RepositorioBase { + public IQueryable ObtenerServiciosPorPropiedad(int idpropiedad){ + var con = Context; + return con.Propiedades + .Where(x => x.Id == idpropiedad) + .SelectMany(x => x.IdServicios) + .Select(x=>x.Id); + } +} -- 2.52.0 From e56d8f70585fe84fa7eeb6e3689186f3b449eed8 Mon Sep 17 00:00:00 2001 From: fede Date: Sat, 23 Nov 2024 20:37:58 -0300 Subject: [PATCH 06/78] Fix: Bug VerduleriaVendeFacturas --- Aspnet/Controllers/GruposController.cs | 6 +++++- Aspnet/Controllers/InquilinoController.cs | 9 ++++++--- Aspnet/Controllers/LoginController.cs | 1 - Aspnet/Controllers/PermisosController.cs | 6 +++++- Aspnet/Controllers/PropiedadesController.cs | 18 +++++++++--------- Aspnet/Controllers/PropietarioController.cs | 6 +++--- Modelo/RepositorioPermisos.cs | 13 +++++++++---- 7 files changed, 37 insertions(+), 22 deletions(-) diff --git a/Aspnet/Controllers/GruposController.cs b/Aspnet/Controllers/GruposController.cs index dd8f26b..31f8ffa 100644 --- a/Aspnet/Controllers/GruposController.cs +++ b/Aspnet/Controllers/GruposController.cs @@ -6,7 +6,11 @@ namespace AlquilaFacil.Controllers; [ApiController] public class GruposController: ControllerBase { [HttpPost("api/admin/grupos")] - public IActionResult CrearGrupo([FromBody] AdminGrupo grupo) { + public IActionResult CrearGrupo([FromBody] AdminGrupo grupo, [FromHeader(Name = "Auth")] string Auth) { + if (!string.IsNullOrEmpty(Auth)) return BadRequest(); + var ret2 = RepositorioPermisos.Singleton.CheckPermisos(Auth, 10); + if (ret2 == false) return BadRequest(ret2); + if (String.IsNullOrEmpty(grupo.descripcion)) return BadRequest(); bool ret = RepositorioGrupos.Singleton.CrearGrupo(grupo.descripcion); diff --git a/Aspnet/Controllers/InquilinoController.cs b/Aspnet/Controllers/InquilinoController.cs index 255ae7e..ef55739 100644 --- a/Aspnet/Controllers/InquilinoController.cs +++ b/Aspnet/Controllers/InquilinoController.cs @@ -15,9 +15,8 @@ public class InquilinoController: ControllerBase [HttpGet("api/inquilino")] public IActionResult Get([FromHeader(Name = "Auth")] string Auth) { if (!string.IsNullOrEmpty(Auth)) return BadRequest(); - string path = Request.Path; + var ret = RepositorioPermisos.Singleton.CheckPermisos(Auth, 9); - var ret = RepositorioPermisos.Singleton.CheckPermisos(Auth, path); if (ret == false) return BadRequest(ret); var list = RepositorioInquilinos.Singleton.GetInquilinos(); @@ -26,7 +25,11 @@ public class InquilinoController: ControllerBase } [HttpPost("api/inquilino")] - public IActionResult Post([FromBody] CrearClienteDto cid) { + public IActionResult Post([FromBody] CrearClienteDto cid, [FromHeader(Name = "Auth")] string Auth) { + if (!string.IsNullOrEmpty(Auth)) return BadRequest(); + var ret3 = RepositorioPermisos.Singleton.CheckPermisos(Auth, 4); + if (ret3 == false) return BadRequest(ret3); + var ret = verificarCrearUsuario(cid); if (ret != "") return BadRequest(ret); diff --git a/Aspnet/Controllers/LoginController.cs b/Aspnet/Controllers/LoginController.cs index 2667370..b4efd38 100644 --- a/Aspnet/Controllers/LoginController.cs +++ b/Aspnet/Controllers/LoginController.cs @@ -53,7 +53,6 @@ public class LoginController: ControllerBase } - private string GenerarToken(LoginDto loginDto){ var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.ASCII.GetBytes("ffb2cdc15d472e41a5b626e294c45020"); diff --git a/Aspnet/Controllers/PermisosController.cs b/Aspnet/Controllers/PermisosController.cs index 3ec45df..a63f9a1 100644 --- a/Aspnet/Controllers/PermisosController.cs +++ b/Aspnet/Controllers/PermisosController.cs @@ -7,7 +7,11 @@ namespace AlquilaFacil.Controllers; [ApiController] public class PermisosController: ControllerBase { [HttpPost("api/admin/permisos")] - public IActionResult CrearPermisos([FromBody] AdminPermiso permiso) { + public IActionResult CrearPermisos([FromBody] AdminPermiso permiso, [FromHeader(Name = "Auth")] string Auth) { + if (!string.IsNullOrEmpty(Auth)) return BadRequest(); + var ret2 = RepositorioPermisos.Singleton.CheckPermisos(Auth, 11); + if (ret2 == false) return BadRequest(ret2); + if (String.IsNullOrEmpty(permiso.descripcion)) return BadRequest(); bool ret = RepositorioPermisos.Singleton.CrearPermiso(permiso.descripcion); diff --git a/Aspnet/Controllers/PropiedadesController.cs b/Aspnet/Controllers/PropiedadesController.cs index 40d872e..8f62418 100644 --- a/Aspnet/Controllers/PropiedadesController.cs +++ b/Aspnet/Controllers/PropiedadesController.cs @@ -9,9 +9,9 @@ namespace AlquilaFacil.Controllers; [ApiController] public class PropiedadesController: ControllerBase { [HttpGet("api/propiedades")] - public IActionResult ListarPropietarios([FromHeader(Name = "Auth")] string Auth) { + public IActionResult ListarPropiedades([FromHeader(Name = "Auth")] string Auth) { if (String.IsNullOrEmpty(Auth)) return Unauthorized(); - var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, 12); if (validacion1 == false) return Unauthorized(); var ret = RepositorioPropiedades.Singleton.ListarPropiedades(); @@ -21,7 +21,7 @@ public class PropiedadesController: ControllerBase { [HttpGet("api/propiedad")] public IActionResult ObtenerPropiedadPorId(int Id, [FromHeader(Name = "Auth")] string Auth) { if (String.IsNullOrEmpty(Auth)) return Unauthorized(); - var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, 12); if (validacion1 == false) return Unauthorized(); if (Id < 0) return BadRequest("la id de propiedad no puede ser negativa"); @@ -32,12 +32,12 @@ public class PropiedadesController: ControllerBase { } [HttpGet("api/propiedades/Propietario")] - public IActionResult ObtenerPropiedadesPorPropietario( + public IActionResult ObtenerPropiedadesPorPropietario ( [FromBody] string email, [FromHeader(Name = "Auth")] string Auth) { if (String.IsNullOrEmpty(Auth)) return Unauthorized(); - var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, 12); if (validacion1 == false) return Unauthorized(); email = email.Trim(); @@ -51,7 +51,7 @@ public class PropiedadesController: ControllerBase { [HttpPost("api/propiedad")] public IActionResult AltaPropiedad([FromBody] AltaPropiedadDto propiedad, [FromHeader(Name = "Auth")] string Auth) { if (String.IsNullOrEmpty(Auth)) return Unauthorized(); - var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, 1); if (validacion1 == false) return Unauthorized(); string validacion2 = ValidarPropiedad(propiedad); @@ -78,7 +78,7 @@ public class PropiedadesController: ControllerBase { [HttpDelete("api/propiedad")] public IActionResult BajaPropiedad(int id, [FromHeader(Name = "Auth")] string Auth){ if (String.IsNullOrEmpty(Auth)) return Unauthorized(); - var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, 2); if (validacion1 == false) return Unauthorized(); if (id <= 0) return BadRequest("No es una id valida"); @@ -93,7 +93,7 @@ public class PropiedadesController: ControllerBase { [HttpPut("api/propiedades/addServicio")] public IActionResult AñadirServicio([FromBody] ServicioAPropiedadDto Servicios, [FromHeader(Name = "Auth")] string Auth) { if (String.IsNullOrEmpty(Auth)) return Unauthorized(); - var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, 8); if (validacion1 == false) return Unauthorized(); if (Servicios.propiedadid <= 0) return BadRequest("No puede tener una id negativa o cero"); @@ -117,7 +117,7 @@ public class PropiedadesController: ControllerBase { [HttpPut("api/propiedades/RmServicio")] public IActionResult EliminarServicio([FromBody] ServicioAPropiedadDto servicio, [FromHeader(Name = "Auth")] string Auth) { if (String.IsNullOrEmpty(Auth)) return Unauthorized(); - var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, 13); if (validacion1 == false) return Unauthorized(); if (servicio.propiedadid <= 0) return BadRequest("No puede tener una id negativa o cero"); diff --git a/Aspnet/Controllers/PropietarioController.cs b/Aspnet/Controllers/PropietarioController.cs index 0023501..9291d41 100644 --- a/Aspnet/Controllers/PropietarioController.cs +++ b/Aspnet/Controllers/PropietarioController.cs @@ -14,7 +14,7 @@ public class PropietarioController: ControllerBase { [HttpGet("api/propietario")] public IActionResult ObtenerPropietarioPorDni(long Dni, [FromHeader(Name ="Auth")] string Auth) { if (String.IsNullOrEmpty(Auth)) return Unauthorized(); - var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, 14); if (validacion1 == false) return Unauthorized(); var ret = RepositorioPropietario.Singleton.ObtenerPropietarioPorDni(Dni); @@ -25,7 +25,7 @@ public class PropietarioController: ControllerBase { public IActionResult AltaPropietario([FromBody]CrearClienteDto Propietario, [FromHeader(Name = "Auth")] string Auth) { if (String.IsNullOrEmpty(Auth)) return Unauthorized(); - var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, 5); if (validacion1 == false) return Unauthorized(); string validacion2 = verificarCrearUsuario(Propietario); @@ -49,7 +49,7 @@ public class PropietarioController: ControllerBase { [HttpPatch("api/propietarios")] public IActionResult PatchPropietario([FromBody]CrearClienteDto Propietario, [FromHeader(Name = "Auth")] string Auth){ if (String.IsNullOrEmpty(Auth)) return Unauthorized(); - var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, Request.Path); + var validacion1 = RepositorioPermisos.Singleton.CheckPermisos(Auth, 5); if (validacion1 == false) return Unauthorized(); string validacion2 = verificarCrearUsuario(Propietario); diff --git a/Modelo/RepositorioPermisos.cs b/Modelo/RepositorioPermisos.cs index d07b1ec..06b51a1 100644 --- a/Modelo/RepositorioPermisos.cs +++ b/Modelo/RepositorioPermisos.cs @@ -17,8 +17,10 @@ public class RepositorioPermisos: RepositorioBase { return list; } - public bool CheckPermisos(string token, string path){ + public bool CheckPermisos(string token, int idpermiso){ var con = Context; + bool tienePermiso = false; + //checkeo que el token corresponda a un usuario Cliente? cli = con.Clientes.FirstOrDefault(x => x.Token == token); if (cli == null || cli.Dni == 0) return false; @@ -30,10 +32,13 @@ public class RepositorioPermisos: RepositorioBase { .SelectMany(x => x.Idpermisos) .Distinct(); + ///////////////////////////////////////////////////////////////// + //Esto esta comentado porque antes pasaba el string del path de la url, es una mala idea a muchos niveles + ///////////////////////////////////////////////////////////////// //me inspiré y hice un regex pero si eliminaba los primeros 8(?) caracteres del string era lo mismo - Match match = Regex.Match(path, @"^/accion/(\d+)$"); - int.TryParse(match.Groups[1].Value, out int idpermiso); - bool tienePermiso = false; + //Match match = Regex.Match(path, @"^/accion/(\d+)$"); + //int.TryParse(match.Groups[1].Value, out int idpermiso); + ///////////////////////////////////////////////////////////////// Parallel.ForEach(permisos, (x, i) =>{ if (x.Id == idpermiso) { -- 2.52.0 From feb4db86c062e336457d34dabca18cabfad650c8 Mon Sep 17 00:00:00 2001 From: fede Date: Sun, 24 Nov 2024 12:59:39 -0300 Subject: [PATCH 07/78] Fix: botones se salen de la pantalla modified: Entidades/Grupo.cs modified: Front/src/lib/NavBarAutocompletable.svelte new file: Front/src/lib/css/popup.css modified: Modelo/RepositorioPermisos.cs --- Aspnet/Aspnet.sln | 25 -------------- Entidades/Grupo.cs | 2 ++ Front/src/lib/NavBarAutocompletable.svelte | 37 +++++++++++++++------ Front/src/lib/css/popup.css | 38 ++++++++++++++++++++++ Modelo/RepositorioPermisos.cs | 6 ++-- 5 files changed, 70 insertions(+), 38 deletions(-) delete mode 100644 Aspnet/Aspnet.sln create mode 100644 Front/src/lib/css/popup.css diff --git a/Aspnet/Aspnet.sln b/Aspnet/Aspnet.sln deleted file mode 100644 index baec485..0000000 --- a/Aspnet/Aspnet.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.002.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AlquilaFacil", "AlquilaFacil.csproj", "{76BA8B31-BAD3-49CD-B8B8-BE22D8AEA281}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {76BA8B31-BAD3-49CD-B8B8-BE22D8AEA281}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {76BA8B31-BAD3-49CD-B8B8-BE22D8AEA281}.Debug|Any CPU.Build.0 = Debug|Any CPU - {76BA8B31-BAD3-49CD-B8B8-BE22D8AEA281}.Release|Any CPU.ActiveCfg = Release|Any CPU - {76BA8B31-BAD3-49CD-B8B8-BE22D8AEA281}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {CF93AFAC-32EF-4993-84A2-CA2EB32F58FF} - EndGlobalSection -EndGlobal diff --git a/Entidades/Grupo.cs b/Entidades/Grupo.cs index 8a1a624..420bc05 100644 --- a/Entidades/Grupo.cs +++ b/Entidades/Grupo.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Text.Json.Serialization; namespace Entidades; @@ -9,6 +10,7 @@ public partial class Grupo public string Nombre { get; set; } = null!; + [JsonIgnore] public virtual ICollection Idclientes { get; set; } = new List(); public virtual ICollection Idpermisos { get; set; } = new List(); diff --git a/Front/src/lib/NavBarAutocompletable.svelte b/Front/src/lib/NavBarAutocompletable.svelte index f5b0cb8..7ba3452 100644 --- a/Front/src/lib/NavBarAutocompletable.svelte +++ b/Front/src/lib/NavBarAutocompletable.svelte @@ -2,19 +2,24 @@ import { Navbar, NavbarBrand, NavbarToggler, NavItem, Nav, NavLink, Collapse } from "@sveltestrap/sveltestrap"; import { onMount } from "svelte"; import { writable } from 'svelte/store'; - + import './css/popup.css'; let isOpen: boolean = $state(false); - interface Permiso { + type Permiso = { id: number; descripcion: string; - } + }; - const permisos = writable([]); + type Grupo = { + id: number; + nombre: string; + idpermisos: Permiso[]; + }; + + const permisos = writable([]); const email = localStorage.getItem('email'); const token = sessionStorage.getItem('token'); - async function obtenerPermisos(){ try { const response = await fetch("http://localhost:5007/api/acciones",{ @@ -36,17 +41,18 @@ } } - $inspect(permisos); onMount( () => { obtenerPermisos(); }) + + AlquilaFacil -
+
Volver al Menú @@ -55,9 +61,20 @@ diff --git a/Front/src/lib/css/popup.css b/Front/src/lib/css/popup.css new file mode 100644 index 0000000..0fcdcf2 --- /dev/null +++ b/Front/src/lib/css/popup.css @@ -0,0 +1,38 @@ +@keyframes rollDown { + from { + opacity: 0; + transform: rotateX(-90deg); + transform-origin: top; + } + to { + opacity: 1; + transform: rotateX(0deg); + } +} + +@keyframes rollUp { + from { + opacity: 1; + transform: rotateX(0deg); + } + to { + opacity: 0; + transform: rotateX(-90deg); + } +} + +.dropdown-menu { + display: none; + animation: none; + transform-origin: top; +} + +.dropdown-menu.show { + animation: rollDown 0.5s ease forwards; + display: block; +} + +.dropdown-menu.hide { + animation: rollUp 0.5s ease forwards; + display: block; +} \ No newline at end of file diff --git a/Modelo/RepositorioPermisos.cs b/Modelo/RepositorioPermisos.cs index 06b51a1..e53fb40 100644 --- a/Modelo/RepositorioPermisos.cs +++ b/Modelo/RepositorioPermisos.cs @@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore; namespace Modelo; public class RepositorioPermisos: RepositorioBase { - public IQueryable? ListarPermisos(string email) { + public object? ListarPermisos(string email) { var con = Context; Cliente? cli = con.Clientes.Include(x => x.Idgrupos).FirstOrDefault(c => c.Email == email); if (cli == null) return null; @@ -12,8 +12,8 @@ public class RepositorioPermisos: RepositorioBase { var list = con.Clientes .Where(c => c.Dni == cli.Dni) .SelectMany(c => c.Idgrupos) - .SelectMany(g => g.Idpermisos) - .Distinct(); + .Include(x=> x.Idpermisos); + return list; } -- 2.52.0 From d2a7368deeca045aadcddbbd12cd7e19cae59e98 Mon Sep 17 00:00:00 2001 From: fede Date: Sun, 24 Nov 2024 14:05:22 -0300 Subject: [PATCH 08/78] update: cambiado favicon --- Front/index.html | 2 +- Front/public/favicon.png | Bin 41894 -> 0 bytes Front/public/favicon.svg | 4 ++++ Front/src/App.svelte | 2 -- 4 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 Front/public/favicon.png create mode 100644 Front/public/favicon.svg diff --git a/Front/index.html b/Front/index.html index 9df6701..135b7b8 100644 --- a/Front/index.html +++ b/Front/index.html @@ -2,7 +2,7 @@ - + diff --git a/Front/public/favicon.png b/Front/public/favicon.png deleted file mode 100644 index bc2d027c0720b47f197fe7896eb6a8d9c1fb025c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41894 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 diff --git a/Front/public/favicon.svg b/Front/public/favicon.svg new file mode 100644 index 0000000..fbd876d --- /dev/null +++ b/Front/public/favicon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Front/src/App.svelte b/Front/src/App.svelte index 2f9e3d0..5e2a448 100644 --- a/Front/src/App.svelte +++ b/Front/src/App.svelte @@ -13,8 +13,6 @@ - - -- 2.52.0 From 50bb59e15f20f88e1a39f66cc362439c8a00a8be Mon Sep 17 00:00:00 2001 From: fede Date: Sun, 24 Nov 2024 22:47:38 -0300 Subject: [PATCH 09/78] =?UTF-8?q?FEAT:=20hecho=20el=20arreglo=20de=20los?= =?UTF-8?q?=20botones=20y=20empec=C3=A9=20con=20el=20registro=20de=20propi?= =?UTF-8?q?edades?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Aspnet/Controllers/AccionesController.cs | 28 ++++-- Aspnet/Controllers/GruposController.cs | 5 - Entidades/Dto/AccionesDeGrupo.cs | 2 + Front/src/App.svelte | 43 +++++++-- .../BarraHorizontalConTexto.svelte | 0 .../{lib => Componentes}/FormPostCli.svelte | 0 Front/src/Componentes/ListaAcciones.svelte | 48 ++++++++++ .../NavBarAutocompletable.svelte | 11 ++- .../{lib => Componentes}/NavBarLogin.svelte | 0 .../{lib => Componentes}/RutaProtegida.svelte | 0 Front/src/{lib => Componentes}/css/popup.css | 0 Front/src/{lib => Componentes}/login.svelte | 0 Front/src/Menu/page.svelte | 6 -- Front/src/login/loginPage.svelte | 10 -- Front/src/paginas/PublicarPropiedad.svelte | 91 +++++++++++++++++++ Front/src/paginas/grupos/AdminG.svelte | 14 +++ Front/src/paginas/grupos/EstadisticaG.svelte | 5 + Front/src/paginas/grupos/InquilinoG.svelte | 14 +++ Front/src/paginas/grupos/PropietarioG.svelte | 13 +++ .../{Info/page.svelte => paginas/info.svelte} | 4 +- .../page.svelte => paginas/inquilino.svelte} | 6 +- Front/src/paginas/login.svelte | 10 ++ Front/src/paginas/menu.svelte | 6 ++ .../propiedades.svelte} | 0 .../propietario.svelte} | 6 +- Modelo/RepositorioGrupos.cs | 6 +- Modelo/RepositorioPropiedades.cs | 4 +- Modelo/RepositorioUsuarios.cs | 18 +++- 28 files changed, 299 insertions(+), 51 deletions(-) create mode 100644 Entidades/Dto/AccionesDeGrupo.cs rename Front/src/{lib => Componentes}/BarraHorizontalConTexto.svelte (100%) rename Front/src/{lib => Componentes}/FormPostCli.svelte (100%) create mode 100644 Front/src/Componentes/ListaAcciones.svelte rename Front/src/{lib => Componentes}/NavBarAutocompletable.svelte (89%) rename Front/src/{lib => Componentes}/NavBarLogin.svelte (100%) rename Front/src/{lib => Componentes}/RutaProtegida.svelte (100%) rename Front/src/{lib => Componentes}/css/popup.css (100%) rename Front/src/{lib => Componentes}/login.svelte (100%) delete mode 100644 Front/src/Menu/page.svelte delete mode 100644 Front/src/login/loginPage.svelte create mode 100644 Front/src/paginas/PublicarPropiedad.svelte create mode 100644 Front/src/paginas/grupos/AdminG.svelte create mode 100644 Front/src/paginas/grupos/EstadisticaG.svelte create mode 100644 Front/src/paginas/grupos/InquilinoG.svelte create mode 100644 Front/src/paginas/grupos/PropietarioG.svelte rename Front/src/{Info/page.svelte => paginas/info.svelte} (83%) rename Front/src/{Inquilino/page.svelte => paginas/inquilino.svelte} (84%) create mode 100644 Front/src/paginas/login.svelte create mode 100644 Front/src/paginas/menu.svelte rename Front/src/{Propiedades/page.svelte => paginas/propiedades.svelte} (100%) rename Front/src/{Propietario/page.svelte => paginas/propietario.svelte} (84%) diff --git a/Aspnet/Controllers/AccionesController.cs b/Aspnet/Controllers/AccionesController.cs index 4772e51..9be95b8 100644 --- a/Aspnet/Controllers/AccionesController.cs +++ b/Aspnet/Controllers/AccionesController.cs @@ -1,24 +1,40 @@ +using System.ComponentModel.DataAnnotations; using Entidades.Dto; using Microsoft.AspNetCore.Mvc; using Modelo; +using System.Text.Json; + namespace AlquilaFacil.Controllers; [ApiController] public class AccionesController: ControllerBase { - //Reutilizo el loginDto pero no lleno el campo de contraseña - [HttpPost("api/acciones")] - public IActionResult ListarAccionesPorUsuario([FromBody] LoginDto email, [FromHeader(Name = "Auth")] string Auth) { - if (email.Email == "" || email.Email == null) return BadRequest(); + [HttpGet("api/acciones")] + public IActionResult ListarAccionesPorUsuario([FromHeader(Name ="Email")] string Email, [FromHeader(Name = "Auth")] string Auth) { + if (Email == "" || Email == null) return BadRequest(); if (Auth == "") return Unauthorized(new { esValido = false}); - bool esValido = RepositorioUsuarios.Singleton.CheckToken(email.Email, Auth); + bool esValido = RepositorioUsuarios.Singleton.CheckToken(Email, Auth); if (!esValido) return Unauthorized(); - var Permisos = RepositorioPermisos.Singleton.ListarPermisos(email.Email); + var Permisos = RepositorioPermisos.Singleton.ListarPermisos(Email); Response.Headers["Content-Type"] = "application/json"; return Ok(Permisos); } + + [HttpPost("api/acciones/grupo")] + public IActionResult ListarAccionesPorGrupo([FromHeader(Name = "Auth")] string Auth, + [FromBody] AccionesPorGrupoDto req) { + if (string.IsNullOrEmpty(Auth)) return BadRequest(); + bool esValido = RepositorioUsuarios.Singleton.CheckToken(req.Email, Auth); + if (esValido == false) return BadRequest(esValido); + + bool tieneGrupo = RepositorioUsuarios.Singleton.CheckGrupo(req.Email, req.Grupo); + if (tieneGrupo == false) return Unauthorized(); + + var permisos = RepositorioGrupos.Singleton.ListarPermisosDeGrupo(req.Grupo); + return Ok(permisos); + } } \ No newline at end of file diff --git a/Aspnet/Controllers/GruposController.cs b/Aspnet/Controllers/GruposController.cs index 31f8ffa..be028f2 100644 --- a/Aspnet/Controllers/GruposController.cs +++ b/Aspnet/Controllers/GruposController.cs @@ -16,11 +16,6 @@ public class GruposController: ControllerBase { bool ret = RepositorioGrupos.Singleton.CrearGrupo(grupo.descripcion); return (ret) ? Ok(ret) : BadRequest(); } - - [HttpGet("api/admin/grupos")] - public IActionResult ListarGrupo(){ - return Ok(RepositorioGrupos.Singleton.Listar()); - } } public record AdminGrupo(string descripcion); diff --git a/Entidades/Dto/AccionesDeGrupo.cs b/Entidades/Dto/AccionesDeGrupo.cs new file mode 100644 index 0000000..a3e4b29 --- /dev/null +++ b/Entidades/Dto/AccionesDeGrupo.cs @@ -0,0 +1,2 @@ +namespace Entidades.Dto; +public record AccionesPorGrupoDto(string Email, string Grupo); \ No newline at end of file diff --git a/Front/src/App.svelte b/Front/src/App.svelte index 5e2a448..95c0f96 100644 --- a/Front/src/App.svelte +++ b/Front/src/App.svelte @@ -1,15 +1,25 @@ - + + @@ -18,6 +28,11 @@ + + + + + @@ -27,5 +42,19 @@ + + + + + + + + + + + + + + diff --git a/Front/src/lib/BarraHorizontalConTexto.svelte b/Front/src/Componentes/BarraHorizontalConTexto.svelte similarity index 100% rename from Front/src/lib/BarraHorizontalConTexto.svelte rename to Front/src/Componentes/BarraHorizontalConTexto.svelte diff --git a/Front/src/lib/FormPostCli.svelte b/Front/src/Componentes/FormPostCli.svelte similarity index 100% rename from Front/src/lib/FormPostCli.svelte rename to Front/src/Componentes/FormPostCli.svelte diff --git a/Front/src/Componentes/ListaAcciones.svelte b/Front/src/Componentes/ListaAcciones.svelte new file mode 100644 index 0000000..d93319e --- /dev/null +++ b/Front/src/Componentes/ListaAcciones.svelte @@ -0,0 +1,48 @@ + + +
+ {#each $permisos as item} + + {item.descripcion} + + {/each} +
\ No newline at end of file diff --git a/Front/src/lib/NavBarAutocompletable.svelte b/Front/src/Componentes/NavBarAutocompletable.svelte similarity index 89% rename from Front/src/lib/NavBarAutocompletable.svelte rename to Front/src/Componentes/NavBarAutocompletable.svelte index 7ba3452..ad8a925 100644 --- a/Front/src/lib/NavBarAutocompletable.svelte +++ b/Front/src/Componentes/NavBarAutocompletable.svelte @@ -23,12 +23,12 @@ async function obtenerPermisos(){ try { const response = await fetch("http://localhost:5007/api/acciones",{ - method: 'POST', + method: 'GET', headers: { 'Auth' : String(token), + 'Email' : String(email), 'Content-Type' : "application/json" }, - body: JSON.stringify({email}) }); if (response.ok){ const json = await response.json(); @@ -45,7 +45,10 @@ obtenerPermisos(); }) - + function redirijir(path: string){ + location.replace(path); + } + @@ -63,7 +66,7 @@ {#each $permisos as item }