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
|
||||
VisualStudioVersion = 17.11.35327.3
|
||||
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
|
||||
Global
|
||||
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.EntityFrameworkCore;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using WebVentaCoche.DataBase;
|
||||
using WebVentaCoche.Models;
|
||||
|
||||
@ -11,45 +14,41 @@ namespace WebVentaCoche.Controllers
|
||||
private readonly ILogger<HomeController> _logger;
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
|
||||
public HomeController(ILogger<HomeController> logger, ApplicationDbContext context)
|
||||
{
|
||||
_logger = logger;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
public IActionResult Index() => View();
|
||||
|
||||
public IActionResult Privacy()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
public IActionResult Privacy() => View();
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
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()
|
||||
{
|
||||
var products = await _context.Products.ToListAsync();
|
||||
var products = await _context.Products.AsNoTracking().ToListAsync();
|
||||
return View(products);
|
||||
}
|
||||
|
||||
//GET:/Home/ProductsDetailsHome/5
|
||||
public async Task<IActionResult> ProductsDetailsHome(int id)
|
||||
{
|
||||
if (id <= 0)
|
||||
return BadRequest();
|
||||
|
||||
var product = await _context.Products.FindAsync(id);
|
||||
if (product == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return View(product);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ namespace WebVentaCoche.Controllers
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// GET: Order/Index
|
||||
//GET:Order/Index
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var orders = await _context.Orders
|
||||
@ -25,7 +25,7 @@ namespace WebVentaCoche.Controllers
|
||||
return View(orders);
|
||||
}
|
||||
|
||||
// GET: Order/Details/5
|
||||
//GET:Order/Details/5
|
||||
public async Task<IActionResult> Details(int? id)
|
||||
{
|
||||
if (id == null)
|
||||
@ -45,7 +45,7 @@ namespace WebVentaCoche.Controllers
|
||||
return View(order);
|
||||
}
|
||||
|
||||
// GET: Order/Edit/5
|
||||
//GET:Order/Edit/{id}
|
||||
public async Task<IActionResult> Edit(int? id)
|
||||
{
|
||||
if (id == null)
|
||||
@ -62,7 +62,7 @@ namespace WebVentaCoche.Controllers
|
||||
return View(order);
|
||||
}
|
||||
|
||||
// POST: Order/Edit/5
|
||||
//POST:Order/Edit/{id}
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Edit(int id, Order order)
|
||||
@ -97,7 +97,6 @@ namespace WebVentaCoche.Controllers
|
||||
return View(order);
|
||||
}
|
||||
|
||||
// Método auxiliar para verificar si la orden existe
|
||||
private bool OrderExists(int id)
|
||||
{
|
||||
return _context.Orders.Any(e => e.Id == id);
|
||||
|
||||
@ -14,7 +14,6 @@ namespace WebVentaCoche.Controllers
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// Lista de productos paginada
|
||||
public async Task<IActionResult> Index(int page = 1, int pageSize = 10)
|
||||
{
|
||||
var products = await _context.Products
|
||||
@ -28,7 +27,6 @@ namespace WebVentaCoche.Controllers
|
||||
return View(products);
|
||||
}
|
||||
|
||||
// Ver detalles de un producto
|
||||
public async Task<IActionResult> Details(int id)
|
||||
{
|
||||
var product = await _context.Products.FindAsync(id);
|
||||
@ -39,14 +37,12 @@ namespace WebVentaCoche.Controllers
|
||||
return View(product);
|
||||
}
|
||||
|
||||
// Página para añadir producto
|
||||
[HttpGet]
|
||||
public IActionResult Create()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
// Procesar el POST para añadir producto
|
||||
[HttpPost]
|
||||
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<Order> Orders { 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<IdentityResult> ConfirmEmailAsync(User user, string token);
|
||||
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);
|
||||
}
|
||||
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);
|
||||
});
|
||||
|
||||
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")
|
||||
@ -375,6 +419,17 @@ namespace WebVentaCoche.Migrations
|
||||
.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")
|
||||
@ -409,6 +464,11 @@ namespace WebVentaCoche.Migrations
|
||||
{
|
||||
b.Navigation("OrderDetails");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("WebVentaCoche.Models.User", b =>
|
||||
{
|
||||
b.Navigation("Addresses");
|
||||
});
|
||||
#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 Surname { 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.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Text;
|
||||
using AutoMapper;
|
||||
using WebVentaCoche.DataBase;
|
||||
using WebVentaCoche.Helpers;
|
||||
using WebVentaCoche.Models;
|
||||
@ -10,77 +12,95 @@ using WebVentaCoche.Services;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddControllersWithViews();
|
||||
|
||||
//Configuración de Entity Framework + SQL Server
|
||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||||
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))
|
||||
.EnableSensitiveDataLogging()
|
||||
);
|
||||
|
||||
builder.Services.AddScoped<IUserHelper, UserHelper>();
|
||||
builder.Services.AddTransient<SeedDb>();
|
||||
builder.Services.AddIdentity<User, IdentityRole>(x =>
|
||||
//Identity + JWT + Helpers
|
||||
builder.Services.AddIdentity<User, IdentityRole>(opts =>
|
||||
{
|
||||
x.User.RequireUniqueEmail = true;
|
||||
x.Password.RequireDigit = false;
|
||||
x.Password.RequiredUniqueChars = 0;
|
||||
x.Password.RequireLowercase = false;
|
||||
x.Password.RequireNonAlphanumeric = false;
|
||||
x.Password.RequireUppercase = false;
|
||||
opts.User.RequireUniqueEmail = true;
|
||||
opts.Password.RequireDigit = false;
|
||||
opts.Password.RequireLowercase = false;
|
||||
opts.Password.RequireNonAlphanumeric = false;
|
||||
opts.Password.RequireUppercase = false;
|
||||
opts.Password.RequiredUniqueChars = 0;
|
||||
})
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(x => x.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
.AddJwtBearer(opts =>
|
||||
{
|
||||
opts.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = false,
|
||||
ValidateAudience = false,
|
||||
ValidateLifetime = 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
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
builder.Services.AddScoped<IUserHelper, UserHelper>();
|
||||
builder.Services.AddScoped<VerificationService>();
|
||||
builder.Services.AddTransient<SeedDb>();
|
||||
builder.Services.AddTransient<EmailService>();
|
||||
|
||||
//MVC + Session + Memoria
|
||||
builder.Services.AddControllersWithViews();
|
||||
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();
|
||||
|
||||
SeedData(app);
|
||||
|
||||
void SeedData(WebApplication app)
|
||||
//Pipeline de middleware
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
IServiceScopeFactory? scopedFactory = app.Services.GetService<IServiceScopeFactory>();
|
||||
|
||||
using (IServiceScope? scope = scopedFactory!.CreateScope())
|
||||
{
|
||||
SeedDb? service = scope.ServiceProvider.GetService<SeedDb>();
|
||||
service!.SeedAsync().Wait();
|
||||
}
|
||||
// En dev vemos la excepción completa y stack-trace en el navegador
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
// Configure the HTTP request pipeline.
|
||||
if (!app.Environment.IsDevelopment())
|
||||
else
|
||||
{
|
||||
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.UseHttpsRedirection();
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseSession();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllerRoute(
|
||||
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();
|
||||
|
||||
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>
|
||||
|
||||
<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="card-body">
|
||||
<p>@Model.LongDescription</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<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 class="col-md-4">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 d-flex align-items-center">
|
||||
@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
|
||||
{
|
||||
<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>
|
||||
|
||||
@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");
|
||||
|
||||
// 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>
|
||||
<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>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<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="~/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>
|
||||
</head>
|
||||
<body class="@(isGestionPage ? "no-background" : "")">
|
||||
<!-- Navbar -->
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container">
|
||||
<div class="d-flex justify-content-center col-10">
|
||||
@ -38,17 +51,31 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="collapse navbar-collapse justify-content-center" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
@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">
|
||||
<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>
|
||||
</a>
|
||||
<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><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
@ -61,33 +88,33 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<!-- Mostrar "Iniciar Sesión" si el usuario no está autenticado -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="@Url.Action("Login", "User")">Iniciar Sesión</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Jumbotron -->
|
||||
<div class="jumbotron jumbotron-fluid bg-image" style="background-image: url('/images/captura.jpg'); background-size: cover; background-position: center; height: 300px;">
|
||||
<div class="jumbotron jumbotron-fluid bg-image"
|
||||
style="background-image: url('/images/captura.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
height: 300px;">
|
||||
<div class="container text-white text-center">
|
||||
<h1 class="display-4">Bienvenido a WebVentaCoche</h1>
|
||||
<p class="lead">Tu tienda de confianza para productos de coches.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="container">
|
||||
@RenderBody()
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="bg-dark text-light text-center py-3 mt-4">
|
||||
<p>© 2024 WebVentaCoche. Todos los derechos reservados.</p>
|
||||
</footer>
|
||||
@RenderSection("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.11" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.11" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.11" />
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user