Edicion y agregacion de direcciones
This commit is contained in:
parent
49df540747
commit
065effae3d
@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||||||
# Visual Studio Version 17
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 17.11.35327.3
|
VisualStudioVersion = 17.11.35327.3
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebVentaCoche", "WebVentaCoche\WebVentaCoche.csproj", "{95AAD8ED-D4F9-4972-81EB-36FCD0FA2C7D}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebVentaCoche", "WebVentaCoche\WebVentaCoche.csproj", "{95AAD8ED-D4F9-4972-81EB-36FCD0FA2C7D}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
|||||||
86
WebVentaCoche/Controllers/AccountController.cs
Normal file
86
WebVentaCoche/Controllers/AccountController.cs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using WebVentaCoche.DataBase;
|
||||||
|
using WebVentaCoche.Helpers;
|
||||||
|
using WebVentaCoche.ViewModels;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace WebVentaCoche.Controllers
|
||||||
|
{
|
||||||
|
[Authorize]
|
||||||
|
public class AccountController : Controller
|
||||||
|
{
|
||||||
|
private readonly IUserHelper _userHelper;
|
||||||
|
private readonly ApplicationDbContext _context;
|
||||||
|
|
||||||
|
public AccountController(IUserHelper userHelper, ApplicationDbContext context)
|
||||||
|
{
|
||||||
|
_userHelper = userHelper;
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
//GET:/Account/Settings
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> Settings()
|
||||||
|
{
|
||||||
|
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
var user = await _userHelper.GetUserByIdAsync(userId);
|
||||||
|
if (user == null) return NotFound();
|
||||||
|
|
||||||
|
var addresses = await _context.Addresses
|
||||||
|
.Where(a => a.UserId == userId)
|
||||||
|
.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/Addresses
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> Addresses()
|
||||||
|
{
|
||||||
|
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
var addresses = await _context.Addresses.Where(a => a.UserId == userId).ToListAsync();
|
||||||
|
|
||||||
|
var vm = 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/Security
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult Security()
|
||||||
|
{
|
||||||
|
//TODO:VM con políticas de contraseña, etc.
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
83
WebVentaCoche/Controllers/AddressController .cs
Normal file
83
WebVentaCoche/Controllers/AddressController .cs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using WebVentaCoche.DataBase;
|
||||||
|
using WebVentaCoche.Models;
|
||||||
|
using WebVentaCoche.ViewModels;
|
||||||
|
|
||||||
|
namespace WebVentaCoche.Controllers
|
||||||
|
{
|
||||||
|
[Authorize]
|
||||||
|
public class AddressController : Controller
|
||||||
|
{
|
||||||
|
private readonly ApplicationDbContext _context;
|
||||||
|
|
||||||
|
public AddressController(ApplicationDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
//POST:/Address/Create
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> Create(AddressViewModel input)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
return RedirectToAction("Addresses", "Account");
|
||||||
|
|
||||||
|
var entity = new Address
|
||||||
|
{
|
||||||
|
Street = input.Street,
|
||||||
|
City = input.City,
|
||||||
|
State = input.State,
|
||||||
|
ZipCode = input.ZipCode,
|
||||||
|
Country = input.Country,
|
||||||
|
UserId = User.FindFirstValue(ClaimTypes.NameIdentifier)!
|
||||||
|
};
|
||||||
|
|
||||||
|
_context.Addresses.Add(entity);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
return RedirectToAction("Addresses", "Account");
|
||||||
|
}
|
||||||
|
|
||||||
|
//POST:/Address/Edit/{id}
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> Edit(int id, Address model)
|
||||||
|
{
|
||||||
|
if (id != model.Id)
|
||||||
|
return BadRequest();
|
||||||
|
|
||||||
|
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
var address = await _context.Addresses.FindAsync(id);
|
||||||
|
|
||||||
|
if (address == null || address.UserId != userId)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
address.Street = model.Street;
|
||||||
|
address.City = model.City;
|
||||||
|
address.State = model.State;
|
||||||
|
address.ZipCode = model.ZipCode;
|
||||||
|
address.Country = model.Country;
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
return RedirectToAction("Addresses", "Account");
|
||||||
|
}
|
||||||
|
|
||||||
|
//POST: /Address/Delete/{id}
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> Delete(int id)
|
||||||
|
{
|
||||||
|
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
var address = await _context.Addresses.FindAsync(id);
|
||||||
|
if (address == null || address.UserId != userId)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
_context.Addresses.Remove(address);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
return RedirectToAction("Addresses", "Account");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
94
WebVentaCoche/Controllers/CartController.cs
Normal file
94
WebVentaCoche/Controllers/CartController.cs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using WebVentaCoche.DataBase;
|
||||||
|
using WebVentaCoche.Helpers;
|
||||||
|
using WebVentaCoche.Models;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace WebVentaCoche.Controllers
|
||||||
|
{
|
||||||
|
public class CartController : Controller
|
||||||
|
{
|
||||||
|
private readonly ApplicationDbContext _context;
|
||||||
|
|
||||||
|
public CartController(ApplicationDbContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET: /Cart/Index
|
||||||
|
// Muestra la lista de productos del carrito y el total.
|
||||||
|
public IActionResult Index()
|
||||||
|
{
|
||||||
|
// Diccionario de IDs -> Cantidades desde sesión (o cualquier fuente)
|
||||||
|
var itemsDict = CartSessionHelper.GetCartItems(HttpContext.Session);
|
||||||
|
|
||||||
|
// Crear tu ViewModel
|
||||||
|
var viewModel = new CartViewModel();
|
||||||
|
|
||||||
|
foreach (var kvp in itemsDict)
|
||||||
|
{
|
||||||
|
var productId = kvp.Key;
|
||||||
|
var amount = kvp.Value;
|
||||||
|
var product = _context.Products.Find(productId);
|
||||||
|
|
||||||
|
if (product != null)
|
||||||
|
{
|
||||||
|
viewModel.Products.Add(new CartProduct
|
||||||
|
{
|
||||||
|
Product = product,
|
||||||
|
Amount = amount
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return View(viewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST: /Cart/AddProductToCart
|
||||||
|
// Añade el producto al carrito y devuelve un JSON con el nuevo contador
|
||||||
|
[HttpPost]
|
||||||
|
public IActionResult AddProductToCart(int id)
|
||||||
|
{
|
||||||
|
var product = _context.Products.Find(id);
|
||||||
|
if (product == null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Añade a la sesión
|
||||||
|
CartSessionHelper.AddToCart(HttpContext.Session, id);
|
||||||
|
|
||||||
|
// Devuelve la cuenta actualizada para actualizar el badge
|
||||||
|
var productIds = CartSessionHelper.GetCartItems(HttpContext.Session);
|
||||||
|
int count = productIds.Count;
|
||||||
|
|
||||||
|
return Json(new { success = true, cartCount = count });
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST: /Cart/Remove
|
||||||
|
// Elimina un producto según su ID
|
||||||
|
[HttpPost]
|
||||||
|
public IActionResult Remove(int id)
|
||||||
|
{
|
||||||
|
CartSessionHelper.RemoveFromCart(HttpContext.Session, id);
|
||||||
|
return RedirectToAction("Index");
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST: /Cart/Clear
|
||||||
|
// Vacía todo el carrito (la sesión)
|
||||||
|
[HttpPost]
|
||||||
|
public IActionResult Clear()
|
||||||
|
{
|
||||||
|
CartSessionHelper.ClearCart(HttpContext.Session);
|
||||||
|
return RedirectToAction("Index");
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET: /Cart/Checkout
|
||||||
|
// (Opcional) Muestra un formulario o datos para la compra/pago.
|
||||||
|
public IActionResult Checkout()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,9 @@
|
|||||||
|
// Controllers/HomeController.cs
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Diagnostics;
|
using Microsoft.Extensions.Logging;
|
||||||
using WebVentaCoche.DataBase;
|
using WebVentaCoche.DataBase;
|
||||||
using WebVentaCoche.Models;
|
using WebVentaCoche.Models;
|
||||||
|
|
||||||
@ -11,45 +14,41 @@ namespace WebVentaCoche.Controllers
|
|||||||
private readonly ILogger<HomeController> _logger;
|
private readonly ILogger<HomeController> _logger;
|
||||||
private readonly ApplicationDbContext _context;
|
private readonly ApplicationDbContext _context;
|
||||||
|
|
||||||
|
|
||||||
public HomeController(ILogger<HomeController> logger, ApplicationDbContext context)
|
public HomeController(ILogger<HomeController> logger, ApplicationDbContext context)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_context = context;
|
_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IActionResult Index()
|
public IActionResult Index() => View();
|
||||||
{
|
|
||||||
return View();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IActionResult Privacy()
|
public IActionResult Privacy() => View();
|
||||||
{
|
|
||||||
return View();
|
|
||||||
}
|
|
||||||
|
|
||||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||||
public IActionResult Error()
|
public IActionResult Error()
|
||||||
|
=> View(new ErrorViewModel
|
||||||
{
|
{
|
||||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier
|
||||||
}
|
});
|
||||||
|
|
||||||
|
//GET:/Home/ProductsHome
|
||||||
public async Task<IActionResult> ProductsHome()
|
public async Task<IActionResult> ProductsHome()
|
||||||
{
|
{
|
||||||
var products = await _context.Products.ToListAsync();
|
var products = await _context.Products.AsNoTracking().ToListAsync();
|
||||||
return View(products);
|
return View(products);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//GET:/Home/ProductsDetailsHome/5
|
||||||
public async Task<IActionResult> ProductsDetailsHome(int id)
|
public async Task<IActionResult> ProductsDetailsHome(int id)
|
||||||
{
|
{
|
||||||
|
if (id <= 0)
|
||||||
|
return BadRequest();
|
||||||
|
|
||||||
var product = await _context.Products.FindAsync(id);
|
var product = await _context.Products.FindAsync(id);
|
||||||
if (product == null)
|
if (product == null)
|
||||||
{
|
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
|
||||||
|
|
||||||
return View(product);
|
return View(product);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ namespace WebVentaCoche.Controllers
|
|||||||
return View(order);
|
return View(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: Order/Edit/5
|
//GET:Order/Edit/{id}
|
||||||
public async Task<IActionResult> Edit(int? id)
|
public async Task<IActionResult> Edit(int? id)
|
||||||
{
|
{
|
||||||
if (id == null)
|
if (id == null)
|
||||||
@ -62,7 +62,7 @@ namespace WebVentaCoche.Controllers
|
|||||||
return View(order);
|
return View(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST: Order/Edit/5
|
//POST:Order/Edit/{id}
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> Edit(int id, Order order)
|
public async Task<IActionResult> Edit(int id, Order order)
|
||||||
@ -97,7 +97,6 @@ namespace WebVentaCoche.Controllers
|
|||||||
return View(order);
|
return View(order);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Método auxiliar para verificar si la orden existe
|
|
||||||
private bool OrderExists(int id)
|
private bool OrderExists(int id)
|
||||||
{
|
{
|
||||||
return _context.Orders.Any(e => e.Id == id);
|
return _context.Orders.Any(e => e.Id == id);
|
||||||
|
|||||||
@ -14,7 +14,6 @@ namespace WebVentaCoche.Controllers
|
|||||||
_context = context;
|
_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lista de productos paginada
|
|
||||||
public async Task<IActionResult> Index(int page = 1, int pageSize = 10)
|
public async Task<IActionResult> Index(int page = 1, int pageSize = 10)
|
||||||
{
|
{
|
||||||
var products = await _context.Products
|
var products = await _context.Products
|
||||||
@ -28,7 +27,6 @@ namespace WebVentaCoche.Controllers
|
|||||||
return View(products);
|
return View(products);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ver detalles de un producto
|
|
||||||
public async Task<IActionResult> Details(int id)
|
public async Task<IActionResult> Details(int id)
|
||||||
{
|
{
|
||||||
var product = await _context.Products.FindAsync(id);
|
var product = await _context.Products.FindAsync(id);
|
||||||
@ -39,14 +37,12 @@ namespace WebVentaCoche.Controllers
|
|||||||
return View(product);
|
return View(product);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Página para añadir producto
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public IActionResult Create()
|
public IActionResult Create()
|
||||||
{
|
{
|
||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Procesar el POST para añadir producto
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IActionResult> Create(Product product, IFormFile Image)
|
public async Task<IActionResult> Create(Product product, IFormFile Image)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -225,5 +225,8 @@ namespace WebVentaCoche.Controllers
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,5 +15,17 @@ namespace WebVentaCoche.DataBase
|
|||||||
public DbSet<User> Users { get; set; }
|
public DbSet<User> Users { get; set; }
|
||||||
public DbSet<Order> Orders { get; set; }
|
public DbSet<Order> Orders { get; set; }
|
||||||
public DbSet<OrderDetail> OrderDetails { get; set; }
|
public DbSet<OrderDetail> OrderDetails { get; set; }
|
||||||
|
public DbSet<Address> Addresses { get; set; }
|
||||||
|
protected override void OnModelCreating(ModelBuilder builder)
|
||||||
|
{
|
||||||
|
base.OnModelCreating(builder);
|
||||||
|
|
||||||
|
builder.Entity<Address>()
|
||||||
|
.HasOne(a => a.User)
|
||||||
|
.WithMany(u => u.Addresses)
|
||||||
|
.HasForeignKey(a => a.UserId)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
76
WebVentaCoche/Helpers/CartSessionHelper.cs
Normal file
76
WebVentaCoche/Helpers/CartSessionHelper.cs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace WebVentaCoche.Helpers
|
||||||
|
{
|
||||||
|
public static class CartSessionHelper
|
||||||
|
{
|
||||||
|
private const string CartSessionKey = "CartItems";
|
||||||
|
|
||||||
|
//Obtiene el diccionario: ProductId -> Quantity
|
||||||
|
public static Dictionary<int, int> GetCartItems(ISession session)
|
||||||
|
{
|
||||||
|
var json = session.GetString(CartSessionKey);
|
||||||
|
if (string.IsNullOrEmpty(json))
|
||||||
|
return new Dictionary<int, int>();
|
||||||
|
|
||||||
|
return JsonSerializer.Deserialize<Dictionary<int, int>>(json)
|
||||||
|
?? new Dictionary<int, int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Guarda el diccionario en la sesión
|
||||||
|
private static void SaveCartItems(ISession session, Dictionary<int, int> items)
|
||||||
|
{
|
||||||
|
var json = JsonSerializer.Serialize(items);
|
||||||
|
session.SetString(CartSessionKey, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Añade un producto al carrito
|
||||||
|
public static void AddToCart(ISession session, int productId)
|
||||||
|
{
|
||||||
|
var items = GetCartItems(session);
|
||||||
|
|
||||||
|
if (items.ContainsKey(productId))
|
||||||
|
items[productId]++;
|
||||||
|
else
|
||||||
|
items[productId] = 1;
|
||||||
|
|
||||||
|
SaveCartItems(session, items);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Actualiza la cantidad de un producto. Si la cantidad es 0 o menor, lo elimina
|
||||||
|
public static void UpdateQuantity(ISession session, int productId, int quantity)
|
||||||
|
{
|
||||||
|
var items = GetCartItems(session);
|
||||||
|
if (items.ContainsKey(productId))
|
||||||
|
{
|
||||||
|
if (quantity <= 0)
|
||||||
|
{
|
||||||
|
items.Remove(productId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
items[productId] = quantity;
|
||||||
|
}
|
||||||
|
SaveCartItems(session, items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Elimina un producto del carrito
|
||||||
|
public static void RemoveFromCart(ISession session, int productId)
|
||||||
|
{
|
||||||
|
var items = GetCartItems(session);
|
||||||
|
if (items.ContainsKey(productId))
|
||||||
|
{
|
||||||
|
items.Remove(productId);
|
||||||
|
}
|
||||||
|
SaveCartItems(session, items);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Vacía el carrito entero
|
||||||
|
public static void ClearCart(ISession session)
|
||||||
|
{
|
||||||
|
session.Remove(CartSessionKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,5 +16,6 @@ namespace WebVentaCoche.Helpers
|
|||||||
Task<string> GenerateEmailConfirmationTokenAsync(User user);
|
Task<string> GenerateEmailConfirmationTokenAsync(User user);
|
||||||
Task<IdentityResult> ConfirmEmailAsync(User user, string token);
|
Task<IdentityResult> ConfirmEmailAsync(User user, string token);
|
||||||
Task<bool> IsEmailConfirmedAsync(User user);
|
Task<bool> IsEmailConfirmedAsync(User user);
|
||||||
|
Task<User?> GetUserByIdAsync(string id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
WebVentaCoche/Helpers/MappingProfile.cs
Normal file
18
WebVentaCoche/Helpers/MappingProfile.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using WebVentaCoche.Models;
|
||||||
|
using WebVentaCoche.ViewModels;
|
||||||
|
|
||||||
|
namespace WebVentaCoche.Helpers
|
||||||
|
{
|
||||||
|
public class MappingProfile : Profile
|
||||||
|
{
|
||||||
|
public MappingProfile()
|
||||||
|
{
|
||||||
|
CreateMap<User, UserDetailsViewModel>()
|
||||||
|
.ForMember(dest => dest.Addresses,
|
||||||
|
opt => opt.MapFrom(src => src.Addresses));
|
||||||
|
|
||||||
|
CreateMap<Address, AddressViewModel>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -75,5 +75,10 @@ namespace WebVentaCoche.Helpers
|
|||||||
{
|
{
|
||||||
return await _userManager.IsEmailConfirmedAsync(user);
|
return await _userManager.IsEmailConfirmedAsync(user);
|
||||||
}
|
}
|
||||||
|
public async Task<User?> GetUserByIdAsync(string id)
|
||||||
|
{
|
||||||
|
return await _userManager.FindByIdAsync(id);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
418
WebVentaCoche/Migrations/20250424122629_ChangePriceToDecimal.Designer.cs
generated
Normal file
418
WebVentaCoche/Migrations/20250424122629_ChangePriceToDecimal.Designer.cs
generated
Normal file
@ -0,0 +1,418 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
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("20250424122629_ChangePriceToDecimal")]
|
||||||
|
partial class ChangePriceToDecimal
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
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<string>("Id")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("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<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("WebVentaCoche.Models.Order", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("OrderDate")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("ShippedDate")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("ShippingAddress")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("TotalAmount")
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Orders");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("WebVentaCoche.Models.OrderDetail", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<int>("OrderId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("ProductId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("Quantity")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("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<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ImagePath")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("LongDescription")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<decimal>("Price")
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<string>("ShortDescription")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Products");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("WebVentaCoche.Models.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("datetimeoffset");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Surname")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<int>("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<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("WebVentaCoche.Models.User", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("WebVentaCoche.Models.User", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", 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<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("WebVentaCoche.Models.User", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
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");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace WebVentaCoche.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class ChangePriceToDecimal : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
478
WebVentaCoche/Migrations/20250428182320_AddAddressEntity.Designer.cs
generated
Normal file
478
WebVentaCoche/Migrations/20250428182320_AddAddressEntity.Designer.cs
generated
Normal file
@ -0,0 +1,478 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
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("20250428182320_AddAddressEntity")]
|
||||||
|
partial class AddAddressEntity
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
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<string>("Id")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("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<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("WebVentaCoche.Models.Address", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("City")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("Country")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("State")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("Street")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ZipCode")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("nvarchar(20)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Addresses");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("WebVentaCoche.Models.Order", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("OrderDate")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("ShippedDate")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("ShippingAddress")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("TotalAmount")
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Orders");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("WebVentaCoche.Models.OrderDetail", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<int>("OrderId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("ProductId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("Quantity")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("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<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ImagePath")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("LongDescription")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<decimal>("Price")
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<string>("ShortDescription")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Products");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("WebVentaCoche.Models.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("datetimeoffset");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Surname")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<int>("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<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("WebVentaCoche.Models.User", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("WebVentaCoche.Models.User", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", 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<string>", 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
WebVentaCoche/Migrations/20250428182320_AddAddressEntity.cs
Normal file
50
WebVentaCoche/Migrations/20250428182320_AddAddressEntity.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace WebVentaCoche.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddAddressEntity : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Addresses",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "int", nullable: false)
|
||||||
|
.Annotation("SqlServer:Identity", "1, 1"),
|
||||||
|
Street = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
|
||||||
|
City = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
|
||||||
|
State = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
|
||||||
|
ZipCode = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false),
|
||||||
|
Country = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
|
||||||
|
UserId = table.Column<string>(type: "nvarchar(450)", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Addresses", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Addresses_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Addresses_UserId",
|
||||||
|
table: "Addresses",
|
||||||
|
column: "UserId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Addresses");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -155,6 +155,50 @@ namespace WebVentaCoche.Migrations
|
|||||||
b.ToTable("AspNetUserTokens", (string)null);
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("WebVentaCoche.Models.Address", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("City")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("Country")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("State")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("Street")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ZipCode")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("nvarchar(20)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Addresses");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("WebVentaCoche.Models.Order", b =>
|
modelBuilder.Entity("WebVentaCoche.Models.Order", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@ -375,6 +419,17 @@ namespace WebVentaCoche.Migrations
|
|||||||
.IsRequired();
|
.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 =>
|
modelBuilder.Entity("WebVentaCoche.Models.Order", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("WebVentaCoche.Models.User", "User")
|
b.HasOne("WebVentaCoche.Models.User", "User")
|
||||||
@ -409,6 +464,11 @@ namespace WebVentaCoche.Migrations
|
|||||||
{
|
{
|
||||||
b.Navigation("OrderDetails");
|
b.Navigation("OrderDetails");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("WebVentaCoche.Models.User", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Addresses");
|
||||||
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
WebVentaCoche/Models/Address.cs
Normal file
32
WebVentaCoche/Models/Address.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace WebVentaCoche.Models
|
||||||
|
{
|
||||||
|
public class Address
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Required, StringLength(100)]
|
||||||
|
public string Street { get; set; }
|
||||||
|
|
||||||
|
[Required, StringLength(50)]
|
||||||
|
public string City { get; set; }
|
||||||
|
|
||||||
|
[StringLength(50)]
|
||||||
|
public string State { get; set; }
|
||||||
|
|
||||||
|
[Required, StringLength(20)]
|
||||||
|
public string ZipCode { get; set; }
|
||||||
|
|
||||||
|
[Required, StringLength(50)]
|
||||||
|
public string Country { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string UserId { get; set; }
|
||||||
|
|
||||||
|
[ForeignKey(nameof(UserId))]
|
||||||
|
public User User { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
19
WebVentaCoche/Models/Cart.cs
Normal file
19
WebVentaCoche/Models/Cart.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
namespace WebVentaCoche.Models
|
||||||
|
{
|
||||||
|
public class Cart
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public string? UserId { get; set; }
|
||||||
|
|
||||||
|
public List<Product> Products { get; set; } = new List<Product>();
|
||||||
|
|
||||||
|
public decimal Total
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Products.Sum(p => p.Price);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
WebVentaCoche/Models/CartProduct.cs
Normal file
9
WebVentaCoche/Models/CartProduct.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace WebVentaCoche.Models
|
||||||
|
{
|
||||||
|
public class CartProduct
|
||||||
|
{
|
||||||
|
public Product Product { get; set; }
|
||||||
|
public int Amount { get; set; }
|
||||||
|
public decimal Subtotal => Product.Price * Amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
8
WebVentaCoche/Models/CartViewModel.cs
Normal file
8
WebVentaCoche/Models/CartViewModel.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace WebVentaCoche.Models
|
||||||
|
{
|
||||||
|
public class CartViewModel
|
||||||
|
{
|
||||||
|
public List<CartProduct> Products { get; set; } = new List<CartProduct>();
|
||||||
|
public decimal Total => Products.Sum(p => p.Subtotal);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,5 +8,6 @@ namespace WebVentaCoche.Models
|
|||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Surname { get; set; }
|
public string Surname { get; set; }
|
||||||
public UserType UserType { get; set; }
|
public UserType UserType { get; set; }
|
||||||
|
public ICollection<Address> Addresses { get; set; } = new List<Address>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,8 +1,10 @@
|
|||||||
|
// Program.cs
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using AutoMapper;
|
||||||
using WebVentaCoche.DataBase;
|
using WebVentaCoche.DataBase;
|
||||||
using WebVentaCoche.Helpers;
|
using WebVentaCoche.Helpers;
|
||||||
using WebVentaCoche.Models;
|
using WebVentaCoche.Models;
|
||||||
@ -10,77 +12,95 @@ using WebVentaCoche.Services;
|
|||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Add services to the container.
|
//Configuración de Entity Framework + SQL Server
|
||||||
builder.Services.AddControllersWithViews();
|
|
||||||
|
|
||||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
|
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))
|
||||||
|
.EnableSensitiveDataLogging()
|
||||||
|
);
|
||||||
|
|
||||||
builder.Services.AddScoped<IUserHelper, UserHelper>();
|
//Identity + JWT + Helpers
|
||||||
builder.Services.AddTransient<SeedDb>();
|
builder.Services.AddIdentity<User, IdentityRole>(opts =>
|
||||||
builder.Services.AddIdentity<User, IdentityRole>(x =>
|
|
||||||
{
|
{
|
||||||
x.User.RequireUniqueEmail = true;
|
opts.User.RequireUniqueEmail = true;
|
||||||
x.Password.RequireDigit = false;
|
opts.Password.RequireDigit = false;
|
||||||
x.Password.RequiredUniqueChars = 0;
|
opts.Password.RequireLowercase = false;
|
||||||
x.Password.RequireLowercase = false;
|
opts.Password.RequireNonAlphanumeric = false;
|
||||||
x.Password.RequireNonAlphanumeric = false;
|
opts.Password.RequireUppercase = false;
|
||||||
x.Password.RequireUppercase = false;
|
opts.Password.RequiredUniqueChars = 0;
|
||||||
})
|
})
|
||||||
.AddEntityFrameworkStores<ApplicationDbContext>()
|
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||||
.AddDefaultTokenProviders();
|
.AddDefaultTokenProviders();
|
||||||
|
|
||||||
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
.AddJwtBearer(x => x.TokenValidationParameters = new TokenValidationParameters
|
.AddJwtBearer(opts =>
|
||||||
|
{
|
||||||
|
opts.TokenValidationParameters = new TokenValidationParameters
|
||||||
{
|
{
|
||||||
ValidateIssuer = false,
|
ValidateIssuer = false,
|
||||||
ValidateAudience = false,
|
ValidateAudience = false,
|
||||||
ValidateLifetime = true,
|
ValidateLifetime = true,
|
||||||
ValidateIssuerSigningKey = true,
|
ValidateIssuerSigningKey = true,
|
||||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JWT:SecretToken"]!)),
|
IssuerSigningKey = new SymmetricSecurityKey(
|
||||||
|
Encoding.UTF8.GetBytes(builder.Configuration["JWT:SecretToken"]!)
|
||||||
|
),
|
||||||
ClockSkew = TimeSpan.Zero
|
ClockSkew = TimeSpan.Zero
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddScoped<IUserHelper, UserHelper>();
|
builder.Services.AddScoped<IUserHelper, UserHelper>();
|
||||||
builder.Services.AddScoped<VerificationService>();
|
builder.Services.AddScoped<VerificationService>();
|
||||||
|
builder.Services.AddTransient<SeedDb>();
|
||||||
builder.Services.AddTransient<EmailService>();
|
builder.Services.AddTransient<EmailService>();
|
||||||
|
|
||||||
|
//MVC + Session + Memoria
|
||||||
|
builder.Services.AddControllersWithViews();
|
||||||
builder.Services.AddMemoryCache();
|
builder.Services.AddMemoryCache();
|
||||||
|
builder.Services.AddSession(opts =>
|
||||||
|
{
|
||||||
|
opts.IdleTimeout = TimeSpan.FromMinutes(30);
|
||||||
|
opts.Cookie.HttpOnly = true;
|
||||||
|
// opts.Cookie.SecurePolicy = CookieSecurePolicy.Always; //produccion
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddAutoMapper(cfg =>
|
||||||
|
{
|
||||||
|
cfg.AddProfile<MappingProfile>();
|
||||||
|
});
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
SeedData(app);
|
//Pipeline de middleware
|
||||||
|
if (app.Environment.IsDevelopment())
|
||||||
void SeedData(WebApplication app)
|
|
||||||
{
|
{
|
||||||
IServiceScopeFactory? scopedFactory = app.Services.GetService<IServiceScopeFactory>();
|
// En dev vemos la excepción completa y stack-trace en el navegador
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
using (IServiceScope? scope = scopedFactory!.CreateScope())
|
|
||||||
{
|
|
||||||
SeedDb? service = scope.ServiceProvider.GetService<SeedDb>();
|
|
||||||
service!.SeedAsync().Wait();
|
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
// Configure the HTTP request pipeline.
|
|
||||||
if (!app.Environment.IsDevelopment())
|
|
||||||
{
|
{
|
||||||
app.UseExceptionHandler("/Home/Error");
|
app.UseExceptionHandler("/Home/Error");
|
||||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
|
||||||
app.UseHsts();
|
app.UseHsts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
|
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
||||||
|
app.UseSession();
|
||||||
|
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
app.MapControllerRoute(
|
app.MapControllerRoute(
|
||||||
name: "default",
|
name: "default",
|
||||||
pattern: "{controller=Home}/{action=Index}/{id?}");
|
pattern: "{controller=Home}/{action=Index}/{id?}"
|
||||||
|
);
|
||||||
|
|
||||||
|
// 5. (Opcional) Seed de datos
|
||||||
|
using (var scope = app.Services.CreateScope())
|
||||||
|
{
|
||||||
|
var seeder = scope.ServiceProvider.GetRequiredService<SeedDb>();
|
||||||
|
seeder.SeedAsync().Wait();
|
||||||
|
}
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|||||||
32
WebVentaCoche/ViewModels/SecurityViewModel.cs
Normal file
32
WebVentaCoche/ViewModels/SecurityViewModel.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace WebVentaCoche.ViewModels
|
||||||
|
{
|
||||||
|
public class SecurityViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Contraseña actual")]
|
||||||
|
public string CurrentPassword { get; set; } = null!;
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[StringLength(100, ErrorMessage = "La {0} debe tener al menos {2} y como máximo {1} caracteres.", MinimumLength = 6)]
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Nueva contraseña")]
|
||||||
|
public string NewPassword { get; set; } = null!;
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[DataType(DataType.Password)]
|
||||||
|
[Display(Name = "Confirmar nueva contraseña")]
|
||||||
|
[Compare("NewPassword", ErrorMessage = "La nueva contraseña y la confirmación no coinciden.")]
|
||||||
|
public string ConfirmNewPassword { get; set; } = null!;
|
||||||
|
|
||||||
|
[Display(Name = "Autenticación multifactor habilitada")]
|
||||||
|
public bool Is2faEnabled { get; set; }
|
||||||
|
|
||||||
|
//Opcional: codigo de verificación para MFA
|
||||||
|
[DataType(DataType.Text)]
|
||||||
|
[Display(Name = "Código de verificación MFA")]
|
||||||
|
public string? TwoFactorCode { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
37
WebVentaCoche/ViewModels/UserDetailsViewModel.cs
Normal file
37
WebVentaCoche/ViewModels/UserDetailsViewModel.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using WebVentaCoche.Enums;
|
||||||
|
|
||||||
|
namespace WebVentaCoche.ViewModels
|
||||||
|
{
|
||||||
|
public class UserDetailsViewModel
|
||||||
|
{
|
||||||
|
public string Id { get; set; } = null!;
|
||||||
|
|
||||||
|
public string Name { get; set; } = null!;
|
||||||
|
|
||||||
|
public string Surname { get; set; } = null!;
|
||||||
|
|
||||||
|
public string Email { get; set; } = null!;
|
||||||
|
|
||||||
|
public string PhoneNumber { get; set; } = null!;
|
||||||
|
|
||||||
|
public UserType UserType { get; set; }
|
||||||
|
|
||||||
|
public List<AddressViewModel> Addresses { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AddressViewModel
|
||||||
|
{
|
||||||
|
public int? Id { get; set; }
|
||||||
|
|
||||||
|
public string Street { get; set; } = null!;
|
||||||
|
|
||||||
|
public string City { get; set; } = null!;
|
||||||
|
|
||||||
|
public string State { get; set; } = null!;
|
||||||
|
|
||||||
|
public string ZipCode { get; set; } = null!;
|
||||||
|
|
||||||
|
public string Country { get; set; } = null!;
|
||||||
|
}
|
||||||
|
}
|
||||||
130
WebVentaCoche/Views/Account/Addresses.cshtml
Normal file
130
WebVentaCoche/Views/Account/Addresses.cshtml
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
@model IEnumerable<WebVentaCoche.ViewModels.AddressViewModel>
|
||||||
|
|
||||||
|
@{
|
||||||
|
Layout = "_AccountLayout";
|
||||||
|
var addresses = Model.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2 class="mb-4">Mis Direcciones</h2>
|
||||||
|
|
||||||
|
@if (!addresses.Any())
|
||||||
|
{
|
||||||
|
<p>No tienes direcciones registradas.</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<button class="btn btn-success btn-sm"
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#addressModal"
|
||||||
|
data-id="">
|
||||||
|
<i class="fa fa-plus me-1"></i>
|
||||||
|
@(!addresses.Any() ? "Añadir dirección" : "Agregar dirección")
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (addresses.Any())
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
@foreach (var addr in addresses)
|
||||||
|
{
|
||||||
|
<div class="col-md-6 mb-4">
|
||||||
|
<div class="card p-3" style="background-color: rgba(255,255,255,0.8); border-radius:10px;">
|
||||||
|
<p class="mb-3">
|
||||||
|
<strong>@addr.Street</strong><br />
|
||||||
|
@addr.City, @addr.State @addr.ZipCode<br />
|
||||||
|
@addr.Country
|
||||||
|
</p>
|
||||||
|
<button class="btn btn-warning btn-sm"
|
||||||
|
data-bs-toggle="modal"
|
||||||
|
data-bs-target="#addressModal"
|
||||||
|
data-id="@addr.Id"
|
||||||
|
data-street="@addr.Street"
|
||||||
|
data-city="@addr.City"
|
||||||
|
data-state="@addr.State"
|
||||||
|
data-zipcode="@addr.ZipCode"
|
||||||
|
data-country="@addr.Country">
|
||||||
|
<i class="fa fa-pencil-alt me-1"></i> Editar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="modal fade" id="addressModal" tabindex="-1" aria-labelledby="addressModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<form id="addressForm" method="post">
|
||||||
|
@Html.AntiForgeryToken()
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="addressModalLabel">Dirección</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="hidden" id="AddressId" name="Id" />
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="Street" class="form-label">Calle</label>
|
||||||
|
<input type="text" class="form-control" id="Street" name="Street" required />
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="City" class="form-label">Ciudad</label>
|
||||||
|
<input type="text" class="form-control" id="City" name="City" required />
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="State" class="form-label">Provincia / Estado</label>
|
||||||
|
<input type="text" class="form-control" id="State" name="State" />
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="ZipCode" class="form-label">Código Postal</label>
|
||||||
|
<input type="text" class="form-control" id="ZipCode" name="ZipCode" required />
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="Country" class="form-label">País</label>
|
||||||
|
<input type="text" class="form-control" id="Country" name="Country" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancelar</button>
|
||||||
|
<button type="submit" class="btn btn-primary btn-sm">Guardar</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
<script>
|
||||||
|
//Al cargar la página, mueve la <div id="addressModal"> al final de <body>
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const modalEl = document.getElementById('addressModal');
|
||||||
|
if (modalEl.parentNode !== document.body) {
|
||||||
|
document.body.appendChild(modalEl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('addressModal')
|
||||||
|
.addEventListener('show.bs.modal', event => {
|
||||||
|
const button = event.relatedTarget;
|
||||||
|
const form = document.getElementById('addressForm');
|
||||||
|
const title = document.getElementById('addressModalLabel');
|
||||||
|
const id = button.getAttribute('data-id');
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
title.textContent = 'Modificar Dirección';
|
||||||
|
form.action = '/Address/Edit/' + id;
|
||||||
|
document.getElementById('AddressId').value = id;
|
||||||
|
document.getElementById('Street').value = button.dataset.street;
|
||||||
|
document.getElementById('City').value = button.dataset.city;
|
||||||
|
document.getElementById('State').value = button.dataset.state;
|
||||||
|
document.getElementById('ZipCode').value = button.dataset.zipcode;
|
||||||
|
document.getElementById('Country').value = button.dataset.country;
|
||||||
|
} else {
|
||||||
|
title.textContent = 'Añadir Dirección';
|
||||||
|
form.action = '/Address/Create';
|
||||||
|
document.getElementById('AddressId').value = '';
|
||||||
|
form.reset();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
}
|
||||||
48
WebVentaCoche/Views/Account/Details.cshtml
Normal file
48
WebVentaCoche/Views/Account/Details.cshtml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
@model WebVentaCoche.ViewModels.UserDetailsViewModel
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Detalles de Cuenta";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<h2 class="text-center my-4">Detalles del Usuario</h2>
|
||||||
|
|
||||||
|
<div class="row justify-content-center mb-4">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card p-3" style="background-color: rgba(255,255,255,0.8); border-radius:10px;">
|
||||||
|
<h5>@Model.Name @Model.Surname</h5>
|
||||||
|
<dl class="row">
|
||||||
|
<dt class="col-sm-4">Email</dt>
|
||||||
|
<dd class="col-sm-8">@Model.Email</dd>
|
||||||
|
<dt class="col-sm-4">Teléfono</dt>
|
||||||
|
<dd class="col-sm-8">@Model.PhoneNumber</dd>
|
||||||
|
<dt class="col-sm-4">Tipo</dt>
|
||||||
|
<dd class="col-sm-8">@Model.UserType</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="text-center mb-3">Direcciones</h3>
|
||||||
|
@if (Model.Addresses.Any())
|
||||||
|
{
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
@foreach (var addr in Model.Addresses)
|
||||||
|
{
|
||||||
|
<div class="card mb-3 p-3" style="background: rgba(255,255,255,0.8); border-radius:10px;">
|
||||||
|
<p>
|
||||||
|
<strong>@addr.Street</strong><br />
|
||||||
|
@addr.City, @addr.State @addr.ZipCode<br />
|
||||||
|
@addr.Country
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<p class="text-center">No has agregado ninguna dirección todavía.</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
8
WebVentaCoche/Views/Account/Security.cshtml
Normal file
8
WebVentaCoche/Views/Account/Security.cshtml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
@model WebVentaCoche.ViewModels.SecurityViewModel
|
||||||
|
@{
|
||||||
|
Layout = "_AccountLayout";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>Seguridad de la Cuenta</h2>
|
||||||
|
<p>Aquí podrás cambiar tu contraseña, configurar MFA, etc.</p>
|
||||||
|
<!-- tu formulario de cambio de contraseña y las teselas de MFA -->
|
||||||
16
WebVentaCoche/Views/Account/Settings.cshtml
Normal file
16
WebVentaCoche/Views/Account/Settings.cshtml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
@model WebVentaCoche.ViewModels.UserDetailsViewModel
|
||||||
|
@{
|
||||||
|
Layout = "_AccountLayout";
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2>Mi Cuenta</h2>
|
||||||
|
<dl class="row">
|
||||||
|
<dt class="col-sm-3">Nombre</dt>
|
||||||
|
<dd class="col-sm-9">@Model.Name @Model.Surname</dd>
|
||||||
|
<dt class="col-sm-3">Email</dt>
|
||||||
|
<dd class="col-sm-9">@Model.Email</dd>
|
||||||
|
<dt class="col-sm-3">Teléfono</dt>
|
||||||
|
<dd class="col-sm-9">@Model.PhoneNumber</dd>
|
||||||
|
<dt class="col-sm-3">Tipo</dt>
|
||||||
|
<dd class="col-sm-9">@Model.UserType</dd>
|
||||||
|
</dl>
|
||||||
97
WebVentaCoche/Views/Cart/Index.cshtml
Normal file
97
WebVentaCoche/Views/Cart/Index.cshtml
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
@model WebVentaCoche.Models.CartViewModel
|
||||||
|
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Carrito de Compras";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="container mt-4">
|
||||||
|
<h1 class="mb-4">Carrito</h1>
|
||||||
|
|
||||||
|
@if (Model.Products == null || !Model.Products.Any())
|
||||||
|
{
|
||||||
|
<p>No hay productos en tu carrito.</p>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<p>Productos en el carrito: @Model.Products.Count</p>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Imagen</th>
|
||||||
|
<th>Producto</th>
|
||||||
|
<th>Precio</th>
|
||||||
|
<th>Cantidad</th>
|
||||||
|
<th>Subtotal</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var cartItem in Model.Products)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
@if (!string.IsNullOrEmpty(cartItem.Product.ImagePath))
|
||||||
|
{
|
||||||
|
<img src="@cartItem.Product.ImagePath"
|
||||||
|
alt="@cartItem.Product.Name"
|
||||||
|
class="img-thumbnail"
|
||||||
|
style="max-height:100px;" />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<img src="/images/default.png"
|
||||||
|
alt="Imagen no disponible"
|
||||||
|
class="img-thumbnail"
|
||||||
|
style="max-height:100px;" />
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>@cartItem.Product.Name</td>
|
||||||
|
|
||||||
|
<td>@cartItem.Product.Price €</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<form asp-action="UpdateQuantity" asp-controller="Cart" method="post">
|
||||||
|
<input type="hidden" name="productId" value="@cartItem.Product.Id" />
|
||||||
|
<input type="number" name="quantity" value="@cartItem.Amount"
|
||||||
|
min="1" class="form-control d-inline" style="width:70px;" />
|
||||||
|
<button type="submit" class="btn btn-sm btn-secondary ms-1">
|
||||||
|
Actualizar
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>@cartItem.Subtotal €</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<form asp-action="Remove" asp-controller="Cart" method="post">
|
||||||
|
<input type="hidden" name="id" value="@cartItem.Product.Id" />
|
||||||
|
<button type="submit" class="btn btn-sm btn-danger">
|
||||||
|
Eliminar
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="text-end">
|
||||||
|
<h4>Total: @Model.Total €</h4>
|
||||||
|
|
||||||
|
<div class="mt-3">
|
||||||
|
<form asp-action="Clear" asp-controller="Cart" method="post" class="d-inline">
|
||||||
|
<button type="submit" class="btn btn-warning">
|
||||||
|
Vaciar Carrito
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<a class="btn btn-primary ms-2" href="#">
|
||||||
|
Proceder al Pago
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
@ -4,19 +4,83 @@
|
|||||||
<h2 class="text-center my-4">@Model.Name</h2>
|
<h2 class="text-center my-4">@Model.Name</h2>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<div class="col-12 mb-4">
|
||||||
|
<div class="card" style="background-color: rgba(255, 255, 255, 0.8); border-radius: 10px;">
|
||||||
|
<div class="row g-0">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
|
<div class="card-body">
|
||||||
<p>@Model.LongDescription</p>
|
<p>@Model.LongDescription</p>
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
<p><strong>Precio:</strong> @Model.Price €</p>
|
<p><strong>Precio:</strong> @Model.Price €</p>
|
||||||
|
<!-- Formulario sin "submit" tradicional -->
|
||||||
|
<form id="addToCartForm">
|
||||||
|
<input type="hidden" name="id" value="@Model.Id" />
|
||||||
|
<button type="submit" class="btn btn-primary">Añadir al carrito</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 d-flex align-items-center">
|
||||||
@if (!string.IsNullOrEmpty(Model.ImagePath))
|
@if (!string.IsNullOrEmpty(Model.ImagePath))
|
||||||
{
|
{
|
||||||
<img src="@Model.ImagePath" class="img-fluid rounded" alt="@Model.Name" />
|
<img src="@Model.ImagePath" class="img-fluid rounded m-3" alt="@Model.Name" />
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<img src="/images/default.png" class="img-fluid rounded" alt="Imagen no disponible" />
|
<img src="/images/default.png" class="img-fluid rounded m-3" alt="Imagen no disponible" />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
const form = document.getElementById("addToCartForm");
|
||||||
|
form.addEventListener("submit", function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
let url = '@Url.Action("AddProductToCart", "Cart")';
|
||||||
|
let formData = new FormData(form);
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Error en la respuesta del servidor");
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
// Mensaje opcional
|
||||||
|
alert("¡Producto añadido al carrito!");
|
||||||
|
|
||||||
|
// 1) Obtener el span (o elemento) que muestra el contador
|
||||||
|
// Por ejemplo, un <span> con id="cartCountSpan".
|
||||||
|
const badgeElement = document.getElementById("cartCountSpan");
|
||||||
|
|
||||||
|
// 2) Actualizar el contenido del badge
|
||||||
|
badgeElement.innerText = data.cartCount;
|
||||||
|
|
||||||
|
// 3) Mostrarlo si está oculto (si data.cartCount > 0)
|
||||||
|
if (data.cartCount > 0) {
|
||||||
|
badgeElement.parentElement.classList.remove("d-none");
|
||||||
|
// Asumiendo que envuelves el badge en un contenedor con "d-none" cuando es 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
alert("Ocurrió un error al añadir el producto al carrito.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
39
WebVentaCoche/Views/Shared/_AccountLayout.cshtml
Normal file
39
WebVentaCoche/Views/Shared/_AccountLayout.cshtml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
@using System.Security.Claims
|
||||||
|
@inject IHttpContextAccessor HttpContextAccessor
|
||||||
|
|
||||||
|
@{
|
||||||
|
Layout = "_Layout";
|
||||||
|
|
||||||
|
var userId = HttpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
string currentAction = ViewContext.RouteData.Values["action"]?.ToString() ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="container-fluid mt-4">
|
||||||
|
<div class="row">
|
||||||
|
<nav class="col-md-3 col-lg-2 d-none d-md-block bg-light sidebar py-3">
|
||||||
|
<div class="nav flex-column nav-pills">
|
||||||
|
<a class="nav-link @(currentAction=="Settings" ? "active" : "")"
|
||||||
|
asp-controller="Account"
|
||||||
|
asp-action="Settings">
|
||||||
|
Cuenta
|
||||||
|
</a>
|
||||||
|
<a class="nav-link @(currentAction=="Addresses" ? "active" : "")"
|
||||||
|
asp-controller="Account"
|
||||||
|
asp-action="Addresses">
|
||||||
|
Direcciones
|
||||||
|
</a>
|
||||||
|
<a class="nav-link @(currentAction=="Security" ? "active" : "")"
|
||||||
|
asp-controller="Account"
|
||||||
|
asp-action="Security">
|
||||||
|
Seguridad
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
||||||
|
@RenderBody()
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@RenderSection("Scripts", required: false)
|
||||||
48
WebVentaCoche/Views/Shared/_AccountLayout.cshtml.css
Normal file
48
WebVentaCoche/Views/Shared/_AccountLayout.cshtml.css
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/* Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
||||||
|
for details on configuring this project to bundle and minify static web assets. */
|
||||||
|
|
||||||
|
a.navbar-brand {
|
||||||
|
white-space: normal;
|
||||||
|
text-align: center;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #0077cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #1b6ec2;
|
||||||
|
border-color: #1861ac;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #1b6ec2;
|
||||||
|
border-color: #1861ac;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-top {
|
||||||
|
border-top: 1px solid #e5e5e5;
|
||||||
|
}
|
||||||
|
.border-bottom {
|
||||||
|
border-bottom: 1px solid #e5e5e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-shadow {
|
||||||
|
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.accept-policy {
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 60px;
|
||||||
|
}
|
||||||
@ -1,21 +1,34 @@
|
|||||||
@{
|
@using WebVentaCoche.Helpers
|
||||||
|
@using WebVentaCoche.Models
|
||||||
|
@using System.Security.Claims
|
||||||
|
@inject IHttpContextAccessor HttpContextAccessor
|
||||||
|
|
||||||
|
|
||||||
|
@{
|
||||||
|
// Determina si es la página de gestión para ajustar la clase body
|
||||||
var isGestionPage = Context.Request.Path.ToString().StartsWith("/Products");
|
var isGestionPage = Context.Request.Path.ToString().StartsWith("/Products");
|
||||||
|
|
||||||
|
// Obtener lista de IDs de productos almacenados en sesión
|
||||||
|
var cartItemIds = CartSessionHelper.GetCartItems(HttpContextAccessor.HttpContext.Session);
|
||||||
|
int cartItemCount = cartItemIds.Count;
|
||||||
|
var userId = HttpContextAccessor.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
|
|
||||||
<link rel="stylesheet" href="~/css/site.css" />
|
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>@ViewData["Title"] - WebVentaCoche</title>
|
<title>@ViewData["Title"] - WebVentaCoche</title>
|
||||||
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" />
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||||
<link href="~/css/site.css" rel="stylesheet" />
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="~/css/site.css" />
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="@(isGestionPage ? "no-background" : "")">
|
<body class="@(isGestionPage ? "no-background" : "")">
|
||||||
<!-- Navbar -->
|
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="d-flex justify-content-center col-10">
|
<div class="d-flex justify-content-center col-10">
|
||||||
@ -38,17 +51,31 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="collapse navbar-collapse justify-content-center" id="navbarNav">
|
<div class="collapse navbar-collapse justify-content-center" id="navbarNav">
|
||||||
<ul class="navbar-nav ms-auto">
|
<ul class="navbar-nav ms-auto">
|
||||||
@if (User.Identity.IsAuthenticated)
|
@if (User.Identity.IsAuthenticated)
|
||||||
{
|
{
|
||||||
<!-- Menú Desplegable con el Icono de Usuario -->
|
<li class="nav-item me-2 position-relative">
|
||||||
|
<a class="nav-link" href="@Url.Action("Index", "Cart")">
|
||||||
|
<i class="fa fa-shopping-cart" style="font-size: 1.5rem;"></i>
|
||||||
|
|
||||||
|
@* Si cartItemCount es 0, le metemos clase "d-none" *@
|
||||||
|
<span id="cartBadge" class="position-absolute top-0 start-100 translate-middle badge
|
||||||
|
rounded-pill bg-danger @(cartItemCount == 0 ? "d-none" : "")">
|
||||||
|
<span id="cartCountSpan">@cartItemCount</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<!-- Menú desplegable de usuario -->
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="fa fa-user" style="font-size: 1.5rem;"></i>
|
<i class="fa fa-user" style="font-size: 1.5rem;"></i>
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
|
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
|
||||||
<li><a class="dropdown-item" href="@Url.Action("Details", "Account")">Detalles Cuenta</a></li>
|
<li> <a class="dropdown-item" asp-controller="Account" asp-action="Details" asp-route-id="@userId">Detalles Cuenta</a></li>
|
||||||
|
<li><a class="dropdown-item" asp-controller="Account" asp-action="Settings">Configuración</a></li>
|
||||||
<li><a class="dropdown-item" href="@Url.Action("Index", "Order")">Pedidos</a></li>
|
<li><a class="dropdown-item" href="@Url.Action("Index", "Order")">Pedidos</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li><hr class="dropdown-divider"></li>
|
||||||
<li>
|
<li>
|
||||||
@ -61,33 +88,33 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<!-- Mostrar "Iniciar Sesión" si el usuario no está autenticado -->
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="@Url.Action("Login", "User")">Iniciar Sesión</a>
|
<a class="nav-link" href="@Url.Action("Login", "User")">Iniciar Sesión</a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- Jumbotron -->
|
<div class="jumbotron jumbotron-fluid bg-image"
|
||||||
<div class="jumbotron jumbotron-fluid bg-image" style="background-image: url('/images/captura.jpg'); background-size: cover; background-position: center; height: 300px;">
|
style="background-image: url('/images/captura.jpg');
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
height: 300px;">
|
||||||
<div class="container text-white text-center">
|
<div class="container text-white text-center">
|
||||||
<h1 class="display-4">Bienvenido a WebVentaCoche</h1>
|
<h1 class="display-4">Bienvenido a WebVentaCoche</h1>
|
||||||
<p class="lead">Tu tienda de confianza para productos de coches.</p>
|
<p class="lead">Tu tienda de confianza para productos de coches.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Content -->
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@RenderBody()
|
@RenderBody()
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Footer -->
|
|
||||||
<footer class="bg-dark text-light text-center py-3 mt-4">
|
<footer class="bg-dark text-light text-center py-3 mt-4">
|
||||||
<p>© 2024 WebVentaCoche. Todos los derechos reservados.</p>
|
<p>© 2024 WebVentaCoche. Todos los derechos reservados.</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
@RenderSection("Scripts", required: false)
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.11" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.11" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.11" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.11" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.11" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.11" />
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user