diff --git a/WebVentaCoche/Controllers/AccountController.cs b/WebVentaCoche/Controllers/AccountController.cs index da16343..f40d542 100644 --- a/WebVentaCoche/Controllers/AccountController.cs +++ b/WebVentaCoche/Controllers/AccountController.cs @@ -21,6 +21,41 @@ namespace WebVentaCoche.Controllers _context = context; } + //GET:/Account/Details/{id} + [HttpGet] + public async Task Details(string id) + { + if (string.IsNullOrEmpty(id)) + return BadRequest(); + + var user = await _userHelper.GetUserByIdAsync(id); + if (user == null) + return NotFound(); + + var addresses = await _context.Addresses.Where(a => a.UserId == id).ToListAsync(); + + var vm = new UserDetailsViewModel + { + Id = user.Id, + Name = user.Name, + Surname = user.Surname, + Email = user.Email, + PhoneNumber = user.PhoneNumber, + UserType = user.UserType, + Addresses = addresses.Select(a => new AddressViewModel + { + Id = a.Id, + Street = a.Street, + City = a.City, + State = a.State, + ZipCode = a.ZipCode, + Country = a.Country + }).ToList() + }; + + return View(vm); + } + //GET:/Account/Settings [HttpGet] public async Task Settings() @@ -29,9 +64,7 @@ namespace WebVentaCoche.Controllers var user = await _userHelper.GetUserByIdAsync(userId); if (user == null) return NotFound(); - var addresses = await _context.Addresses - .Where(a => a.UserId == userId) - .ToListAsync(); + var addresses = await _context.Addresses.Where(a => a.UserId == userId).ToListAsync(); var vm = new UserDetailsViewModel { diff --git a/WebVentaCoche/Controllers/CartController.cs b/WebVentaCoche/Controllers/CartController.cs index 38b98b2..af70804 100644 --- a/WebVentaCoche/Controllers/CartController.cs +++ b/WebVentaCoche/Controllers/CartController.cs @@ -4,6 +4,8 @@ using WebVentaCoche.DataBase; using WebVentaCoche.Helpers; using WebVentaCoche.Models; using System.Linq; +using System.Security.Claims; +using WebVentaCoche.Enums; namespace WebVentaCoche.Controllers { @@ -16,8 +18,7 @@ namespace WebVentaCoche.Controllers _context = context; } - // GET: /Cart/Index - // Muestra la lista de productos del carrito y el total. + //GET:/Cart/Index public IActionResult Index() { // Diccionario de IDs -> Cantidades desde sesión (o cualquier fuente) @@ -45,8 +46,63 @@ namespace WebVentaCoche.Controllers return View(viewModel); } - // POST: /Cart/AddProductToCart - // Añade el producto al carrito y devuelve un JSON con el nuevo contador + // POST: /Cart/Purchase + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Purchase() + { + // 1) Obtén el carrito de la sesión + var items = CartSessionHelper.GetCartItems(HttpContext.Session); + if (!items.Any()) + { + TempData["Error"] = "El carrito está vacío."; + return RedirectToAction("Index"); + } + + var userId = User.FindFirstValue(ClaimTypes.NameIdentifier)!; + + var addr = await _context.Addresses.Where(a => a.UserId == userId).FirstOrDefaultAsync(); + if (addr == null) + { + TempData["Error"] = "No tienes ninguna dirección guardada. Primero añade una dirección en tu perfil."; + return RedirectToAction("Addresses", "Account"); + } + var shippingAddress = $"{addr.Street}, {addr.City}, {addr.State}, {addr.ZipCode}, {addr.Country}"; + + var order = new Order + { + UserId = userId, + OrderDate = DateTime.UtcNow, + Status = OrderStatus.Pending, + ShippingAddress = shippingAddress + }; + + decimal total = 0m; + foreach (var (productId, quantity) in items) + { + var product = await _context.Products.FindAsync(productId); + if (product == null) continue; + + var detail = new OrderDetail + { + ProductId = product.Id, + Quantity = quantity, + UnitPrice = product.Price + }; + total += detail.Subtotal; + order.OrderDetails.Add(detail); + } + + order.TotalAmount = total; + _context.Orders.Add(order); + await _context.SaveChangesAsync(); + + CartSessionHelper.ClearCart(HttpContext.Session); + + return RedirectToAction("Details", "Order", new { id = order.Id }); + } + + //POST:/Cart/AddProductToCart [HttpPost] public IActionResult AddProductToCart(int id) { @@ -66,8 +122,7 @@ namespace WebVentaCoche.Controllers return Json(new { success = true, cartCount = count }); } - // POST: /Cart/Remove - // Elimina un producto según su ID + //POST:/Cart/Remove [HttpPost] public IActionResult Remove(int id) { @@ -75,8 +130,7 @@ namespace WebVentaCoche.Controllers return RedirectToAction("Index"); } - // POST: /Cart/Clear - // Vacía todo el carrito (la sesión) + //POST:/Cart/Clear [HttpPost] public IActionResult Clear() { @@ -84,8 +138,7 @@ namespace WebVentaCoche.Controllers return RedirectToAction("Index"); } - // GET: /Cart/Checkout - // (Opcional) Muestra un formulario o datos para la compra/pago. + //GET:/Cart/Checkout public IActionResult Checkout() { return View(); diff --git a/WebVentaCoche/Controllers/OrderController.cs b/WebVentaCoche/Controllers/OrderController.cs index 881bf3b..4c1c7a0 100644 --- a/WebVentaCoche/Controllers/OrderController.cs +++ b/WebVentaCoche/Controllers/OrderController.cs @@ -1,12 +1,13 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; +using System.Security.Claims; using WebVentaCoche.DataBase; using WebVentaCoche.Models; namespace WebVentaCoche.Controllers { - [Authorize] // Requiere autenticación para acceder al controlador + [Authorize] public class OrderController : Controller { private readonly ApplicationDbContext _context; @@ -19,28 +20,31 @@ namespace WebVentaCoche.Controllers //GET:Order/Index public async Task Index() { - var orders = await _context.Orders - .Include(o => o.User) // Cargar la relación con el usuario - .ToListAsync(); + var orders = await _context.Orders.Include(o => o.User).ToListAsync(); return View(orders); } + //GET:/Order/MyOrders + [Authorize] + public async Task UserOrders() + { + var userId = User.FindFirstValue(ClaimTypes.NameIdentifier)!; + var myOrders = await _context.Orders.Where(o => o.UserId == userId).Include(o => o.OrderDetails).ThenInclude(d => d.Product).OrderByDescending(o => o.OrderDate).ToListAsync(); - //GET:Order/Details/5 + return View(myOrders); + } + //GET:Order/Details/{id} + [HttpGet] public async Task Details(int? id) { if (id == null) - { return NotFound(); - } - var order = await _context.Orders - .Include(o => o.User) // Cargar la relación con el usuario - .FirstOrDefaultAsync(o => o.Id == id); + var userId = User.FindFirstValue(ClaimTypes.NameIdentifier)!; + + var order = await _context.Orders.Include(o => o.OrderDetails).ThenInclude(d => d.Product).FirstOrDefaultAsync(o => o.Id == id && o.UserId == userId); if (order == null) - { return NotFound(); - } return View(order); } diff --git a/WebVentaCoche/Enums/OrderStatus.cs b/WebVentaCoche/Enums/OrderStatus.cs index 43a219f..18a5e22 100644 --- a/WebVentaCoche/Enums/OrderStatus.cs +++ b/WebVentaCoche/Enums/OrderStatus.cs @@ -2,10 +2,10 @@ { public enum OrderStatus { - Pending, // Orden pendiente - Processing, // Orden en proceso - Shipped, // Orden enviada - Delivered, // Orden entregada - Cancelled // Orden cancelada + Pending, + Processing, + Shipped, + Delivered, + Cancelled } } diff --git a/WebVentaCoche/Migrations/20250430123732_AddOrderAndDetails.Designer.cs b/WebVentaCoche/Migrations/20250430123732_AddOrderAndDetails.Designer.cs new file mode 100644 index 0000000..bc799ea --- /dev/null +++ b/WebVentaCoche/Migrations/20250430123732_AddOrderAndDetails.Designer.cs @@ -0,0 +1,478 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using WebVentaCoche.DataBase; + +#nullable disable + +namespace WebVentaCoche.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250430123732_AddOrderAndDetails")] + partial class AddOrderAndDetails + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("WebVentaCoche.Models.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("City") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Country") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Street") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("ZipCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Addresses"); + }); + + modelBuilder.Entity("WebVentaCoche.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("OrderDate") + .HasColumnType("datetime2"); + + b.Property("ShippedDate") + .HasColumnType("datetime2"); + + b.Property("ShippingAddress") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TotalAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Orders"); + }); + + modelBuilder.Entity("WebVentaCoche.Models.OrderDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("OrderId") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.HasIndex("ProductId"); + + b.ToTable("OrderDetails"); + }); + + modelBuilder.Entity("WebVentaCoche.Models.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ImagePath") + .HasColumnType("nvarchar(max)"); + + b.Property("LongDescription") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ShortDescription") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Products"); + }); + + modelBuilder.Entity("WebVentaCoche.Models.User", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("Surname") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("UserType") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("WebVentaCoche.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("WebVentaCoche.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WebVentaCoche.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("WebVentaCoche.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("WebVentaCoche.Models.Address", b => + { + b.HasOne("WebVentaCoche.Models.User", "User") + .WithMany("Addresses") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("WebVentaCoche.Models.Order", b => + { + b.HasOne("WebVentaCoche.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("WebVentaCoche.Models.OrderDetail", b => + { + b.HasOne("WebVentaCoche.Models.Order", "Order") + .WithMany("OrderDetails") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("WebVentaCoche.Models.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Order"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("WebVentaCoche.Models.Order", b => + { + b.Navigation("OrderDetails"); + }); + + modelBuilder.Entity("WebVentaCoche.Models.User", b => + { + b.Navigation("Addresses"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/WebVentaCoche/Migrations/20250430123732_AddOrderAndDetails.cs b/WebVentaCoche/Migrations/20250430123732_AddOrderAndDetails.cs new file mode 100644 index 0000000..1bb1f71 --- /dev/null +++ b/WebVentaCoche/Migrations/20250430123732_AddOrderAndDetails.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace WebVentaCoche.Migrations +{ + /// + public partial class AddOrderAndDetails : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/WebVentaCoche/Models/Order.cs b/WebVentaCoche/Models/Order.cs index 542c06a..cd7c0d7 100644 --- a/WebVentaCoche/Models/Order.cs +++ b/WebVentaCoche/Models/Order.cs @@ -9,27 +9,19 @@ namespace WebVentaCoche.Models { [Key] public int Id { get; set; } - [Required] public string UserId { get; set; } - [ForeignKey("UserId")] public virtual User User { get; set; } - [Required] - public DateTime OrderDate { get; set; } // Fecha y hora en que se realizó la orden - - public DateTime? ShippedDate { get; set; } // Fecha de envío (opcional) - + public DateTime OrderDate { get; set; } + public DateTime? ShippedDate { get; set; } [Required] - public decimal TotalAmount { get; set; } // Monto total de la orden - + public decimal TotalAmount { get; set; } [Required] - public OrderStatus Status { get; set; } // Estado de la orden (Pendiente, Enviado, Completado, Cancelado) - + public OrderStatus Status { get; set; } [Required] - public string ShippingAddress { get; set; } // Dirección de envío del pedido - - public virtual List OrderDetails { get; set; } // Relación con los detalles de la orden + public string ShippingAddress { get; set; } + public virtual List OrderDetails { get; set; } = new List(); } } diff --git a/WebVentaCoche/Models/OrderDetail.cs b/WebVentaCoche/Models/OrderDetail.cs index c83deb6..ec2c76f 100644 --- a/WebVentaCoche/Models/OrderDetail.cs +++ b/WebVentaCoche/Models/OrderDetail.cs @@ -7,23 +7,15 @@ namespace WebVentaCoche.Models { [Key] public int Id { get; set; } - - [Required] public int OrderId { get; set; } [ForeignKey("OrderId")] - public virtual Order Order { get; set; } // Relación con la orden - - [Required] + public virtual Order Order { get; set; } public int ProductId { get; set; } - [ForeignKey("ProductId")] - public virtual Product Product { get; set; } // Relación con el producto - - [Required] - public int Quantity { get; set; } // Cantidad del producto en la orden - - [Required] - public decimal UnitPrice { get; set; } // Precio unitario del producto en el momento de la compra + public virtual Product Product { get; set; } + public int Quantity { get; set; } + public decimal UnitPrice { get; set; } + public decimal Subtotal => Quantity * UnitPrice; } } diff --git a/WebVentaCoche/Services/EmailService.cs b/WebVentaCoche/Services/EmailService.cs index bf44f44..b826e78 100644 --- a/WebVentaCoche/Services/EmailService.cs +++ b/WebVentaCoche/Services/EmailService.cs @@ -12,7 +12,6 @@ namespace WebVentaCoche.Services public EmailService(IConfiguration configuration) { - // Leer configuraciones desde appsettings.json _apiKey = configuration["SendGrid:ApiKey"]; _fromEmail = configuration["SendGrid:FromEmail"]; _fromName = configuration["SendGrid:FromName"]; @@ -20,23 +19,17 @@ namespace WebVentaCoche.Services public async Task SendEmailAsync(string to, string subject, string htmlContent) { - // Crear cliente SendGrid var client = new SendGridClient(_apiKey); - // Configurar remitente y destinatario var from = new EmailAddress(_fromEmail, _fromName); var toEmail = new EmailAddress(to); - // Convertir el contenido HTML a texto plano eliminando etiquetas var plainTextContent = System.Text.RegularExpressions.Regex.Replace(htmlContent, "<[^>]+>", string.Empty); - // Crear el mensaje var message = MailHelper.CreateSingleEmail(from, toEmail, subject, plainTextContent, htmlContent); - // Enviar el mensaje var response = await client.SendEmailAsync(message); - // Manejar errores en caso de que el envío falle if (response.StatusCode == System.Net.HttpStatusCode.BadRequest) { throw new Exception("Error: Solicitud inválida al enviar el correo."); diff --git a/WebVentaCoche/ViewModels/OrderSummaryViewModel.cs b/WebVentaCoche/ViewModels/OrderSummaryViewModel.cs new file mode 100644 index 0000000..021a1de --- /dev/null +++ b/WebVentaCoche/ViewModels/OrderSummaryViewModel.cs @@ -0,0 +1,10 @@ +namespace WebVentaCoche.ViewModels +{ + public class OrderSummaryViewModel + { + public int Id { get; set; } + public DateTime OrderDate { get; set; } + public decimal TotalAmount { get; set; } + public string Status { get; set; } = null!; + } +} diff --git a/WebVentaCoche/Views/Cart/Index.cshtml b/WebVentaCoche/Views/Cart/Index.cshtml index ea882dd..62b0ae0 100644 --- a/WebVentaCoche/Views/Cart/Index.cshtml +++ b/WebVentaCoche/Views/Cart/Index.cshtml @@ -88,9 +88,14 @@ - - Proceder al Pago - +
+ @Html.AntiForgeryToken() + +
+ + } diff --git a/WebVentaCoche/Views/Order/Details.cshtml b/WebVentaCoche/Views/Order/Details.cshtml index 6463a29..15dca00 100644 --- a/WebVentaCoche/Views/Order/Details.cshtml +++ b/WebVentaCoche/Views/Order/Details.cshtml @@ -1,20 +1,60 @@ @model WebVentaCoche.Models.Order @{ - ViewData["Title"] = "Detalles de la Orden"; + ViewData["Title"] = "Detalle de Pedido"; } -
-

Detalles de la Orden

-
-
-
Orden #@Model.Id
-

Fecha del Pedido: @Model.OrderDate.ToString("dd/MM/yyyy")

-

Cliente: @Model.User.Name @Model.User.Surname

-

Estado: @Model.Status

-

Total: @Model.TotalAmount.ToString("C")

-

Dirección de Envío: @Model.ShippingAddress

- Volver a la Lista +
+

Pedido #@Model.Id

+ +
+
+

Fecha del pedido: @Model.OrderDate.ToString("g")

+

Estado: @Model.Status

+
+
+

Dirección de envío:
@Model.ShippingAddress

+

+ Fecha de envío: @(Model.ShippedDate.HasValue ? Model.ShippedDate.Value.ToString("g") : "Pendiente") +

+ +

Productos del Pedido

+ + + + + + + + + + + + @foreach (var detalle in Model.OrderDetails) + { + + + + + + + + } + +
ImagenNombreCantidadPrecio unidadSubtotal
+ @if (!string.IsNullOrEmpty(detalle.Product.ImagePath)) + { + @detalle.Product.Name + } + else + { + Sin imagen + } + @detalle.Product.Name@detalle.Quantity@detalle.UnitPrice.ToString("0.00") €@detalle.Subtotal.ToString("0.00") €
+ +
+

Total del pedido: @Model.TotalAmount.ToString("0.00") €

+
diff --git a/WebVentaCoche/Views/Order/UserOrders.cshtml b/WebVentaCoche/Views/Order/UserOrders.cshtml new file mode 100644 index 0000000..8e2eff4 --- /dev/null +++ b/WebVentaCoche/Views/Order/UserOrders.cshtml @@ -0,0 +1,40 @@ +@model IEnumerable + +@{ + ViewData["Title"] = "Mis Pedidos"; +} + +
+

Mis Pedidos

+ + @if (!Model.Any()) + { +

Aún no has realizado ningún pedido.

+ } + else + { +
+ @foreach (var order in Model) + { +
+
+
+
Pedido #@order.Id
+

Fecha: @order.OrderDate.ToString("g")

+

Total: @order.TotalAmount.ToString("0.00") €

+

Estado: @order.Status

+

Envío:
@order.ShippingAddress

+
+ +
+
+ } +
+ } +
diff --git a/WebVentaCoche/Views/Shared/_Layout.cshtml b/WebVentaCoche/Views/Shared/_Layout.cshtml index dae5510..b254b2b 100644 --- a/WebVentaCoche/Views/Shared/_Layout.cshtml +++ b/WebVentaCoche/Views/Shared/_Layout.cshtml @@ -76,7 +76,7 @@