Dynamic Sidebar implemented, with appropriate icons and separately populated category pages
This commit is contained in:
parent
1ce7b31d8e
commit
6023073b3a
|
@ -74,5 +74,42 @@ namespace ShopOnline.Api.Controllers
|
||||||
"Error retrieving data from the database.");
|
"Error retrieving data from the database.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route(nameof(GetProductCategories))]
|
||||||
|
public async Task<ActionResult<IEnumerable<ProductCategoryDto>>> GetProductCategories()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var productCategories = await productRepository.GetCategories();
|
||||||
|
var productCategoryDtos = productCategories.ConvertToDto();
|
||||||
|
|
||||||
|
return Ok(productCategoryDtos);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError,
|
||||||
|
"Error retrieving data from the database.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("{categoryId}/GetItemsByCategory")]
|
||||||
|
public async Task<ActionResult<IEnumerable<ProductDto>>> GetItemsByCategory(int categoryId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var products = await productRepository.GetItemsByCategory(categoryId);
|
||||||
|
var productCategories = await productRepository.GetCategories();
|
||||||
|
var productDtos = products.ConvertToDto(productCategories);
|
||||||
|
|
||||||
|
return Ok(productDtos);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError,
|
||||||
|
"Error retrieving data from the database.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,22 +292,27 @@ namespace ShopOnline.Api.Data
|
||||||
modelBuilder.Entity<ProductCategory>().HasData(new ProductCategory
|
modelBuilder.Entity<ProductCategory>().HasData(new ProductCategory
|
||||||
{
|
{
|
||||||
Id = 1,
|
Id = 1,
|
||||||
Name = "Beauty"
|
Name = "Beauty",
|
||||||
|
IconCSS = "fas fa-spa"
|
||||||
});
|
});
|
||||||
modelBuilder.Entity<ProductCategory>().HasData(new ProductCategory
|
modelBuilder.Entity<ProductCategory>().HasData(new ProductCategory
|
||||||
{
|
{
|
||||||
Id = 2,
|
Id = 2,
|
||||||
Name = "Furniture"
|
Name = "Furniture",
|
||||||
|
IconCSS = "fas fa-couch"
|
||||||
});
|
});
|
||||||
modelBuilder.Entity<ProductCategory>().HasData(new ProductCategory
|
modelBuilder.Entity<ProductCategory>().HasData(new ProductCategory
|
||||||
{
|
{
|
||||||
Id = 3,
|
Id = 3,
|
||||||
Name = "Electronics"
|
Name = "Electronics",
|
||||||
|
IconCSS = "fas fa-headphones"
|
||||||
|
|
||||||
});
|
});
|
||||||
modelBuilder.Entity<ProductCategory>().HasData(new ProductCategory
|
modelBuilder.Entity<ProductCategory>().HasData(new ProductCategory
|
||||||
{
|
{
|
||||||
Id = 4,
|
Id = 4,
|
||||||
Name = "Shoes"
|
Name = "Shoes",
|
||||||
|
IconCSS = "fas fa-shoe-prints"
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,6 @@
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
public string IconCSS { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,17 @@ namespace ShopOnline.Api.Extensions
|
||||||
{
|
{
|
||||||
public static class DtoConversions
|
public static class DtoConversions
|
||||||
{
|
{
|
||||||
|
public static IEnumerable<ProductCategoryDto> ConvertToDto(this IEnumerable<ProductCategory> productCategories)
|
||||||
|
{
|
||||||
|
return (from productCategory in productCategories
|
||||||
|
select new ProductCategoryDto
|
||||||
|
{
|
||||||
|
Id = productCategory.Id,
|
||||||
|
Name = productCategory.Name,
|
||||||
|
IconCSS = productCategory.IconCSS
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
public static IEnumerable<ProductDto> ConvertToDto(this IEnumerable<Product> products,
|
public static IEnumerable<ProductDto> ConvertToDto(this IEnumerable<Product> products,
|
||||||
IEnumerable<ProductCategory> productCategories)
|
IEnumerable<ProductCategory> productCategories)
|
||||||
{
|
{
|
||||||
|
|
420
ShopOnline.Api/Migrations/20220916171516_AddProductCategoryIcons.Designer.cs
generated
Normal file
420
ShopOnline.Api/Migrations/20220916171516_AddProductCategoryIcons.Designer.cs
generated
Normal file
|
@ -0,0 +1,420 @@
|
||||||
|
// <auto-generated />
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using ShopOnline.Api.Data;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace ShopOnline.Api.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ShopOnlineDbContext))]
|
||||||
|
[Migration("20220916171516_AddProductCategoryIcons")]
|
||||||
|
partial class AddProductCategoryIcons
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "6.0.8")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||||
|
|
||||||
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);
|
||||||
|
|
||||||
|
modelBuilder.Entity("ShopOnline.Api.Entities.Cart", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Carts");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
UserId = 1
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
UserId = 2
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ShopOnline.Api.Entities.CartItem", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
|
||||||
|
|
||||||
|
b.Property<int>("CartId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("ProductId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("Quantity")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("CartItems");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ShopOnline.Api.Entities.Product", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
|
||||||
|
|
||||||
|
b.Property<int>("CategoryId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ImageURL")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<decimal>("Price")
|
||||||
|
.HasColumnType("decimal(18,2)");
|
||||||
|
|
||||||
|
b.Property<int>("Quantity")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Products");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
CategoryId = 1,
|
||||||
|
Description = "A kit provided by Glossier, containing skin care, hair care and makeup products",
|
||||||
|
ImageURL = "/Images/Beauty/Beauty1.png",
|
||||||
|
Name = "Glossier - Beauty Kit",
|
||||||
|
Price = 100m,
|
||||||
|
Quantity = 100
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
CategoryId = 1,
|
||||||
|
Description = "A kit provided by Curology, containing skin care products",
|
||||||
|
ImageURL = "/Images/Beauty/Beauty2.png",
|
||||||
|
Name = "Curology - Skin Care Kit",
|
||||||
|
Price = 50m,
|
||||||
|
Quantity = 45
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 3,
|
||||||
|
CategoryId = 1,
|
||||||
|
Description = "A kit provided by Curology, containing skin care products",
|
||||||
|
ImageURL = "/Images/Beauty/Beauty3.png",
|
||||||
|
Name = "Cocooil - Organic Coconut Oil",
|
||||||
|
Price = 20m,
|
||||||
|
Quantity = 30
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 4,
|
||||||
|
CategoryId = 1,
|
||||||
|
Description = "A kit provided by Schwarzkopf, containing skin care and hair care products",
|
||||||
|
ImageURL = "/Images/Beauty/Beauty4.png",
|
||||||
|
Name = "Schwarzkopf - Hair Care and Skin Care Kit",
|
||||||
|
Price = 50m,
|
||||||
|
Quantity = 60
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 5,
|
||||||
|
CategoryId = 1,
|
||||||
|
Description = "Skin Care Kit, containing skin care and hair care products",
|
||||||
|
ImageURL = "/Images/Beauty/Beauty5.png",
|
||||||
|
Name = "Skin Care Kit",
|
||||||
|
Price = 30m,
|
||||||
|
Quantity = 85
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 6,
|
||||||
|
CategoryId = 3,
|
||||||
|
Description = "Air Pods - in-ear wireless headphones",
|
||||||
|
ImageURL = "/Images/Electronic/Electronics1.png",
|
||||||
|
Name = "Air Pods",
|
||||||
|
Price = 100m,
|
||||||
|
Quantity = 120
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 7,
|
||||||
|
CategoryId = 3,
|
||||||
|
Description = "On-ear Golden Headphones - these headphones are not wireless",
|
||||||
|
ImageURL = "/Images/Electronic/Electronics2.png",
|
||||||
|
Name = "On-ear Golden Headphones",
|
||||||
|
Price = 40m,
|
||||||
|
Quantity = 200
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 8,
|
||||||
|
CategoryId = 3,
|
||||||
|
Description = "On-ear Black Headphones - these headphones are not wireless",
|
||||||
|
ImageURL = "/Images/Electronic/Electronics3.png",
|
||||||
|
Name = "On-ear Black Headphones",
|
||||||
|
Price = 40m,
|
||||||
|
Quantity = 300
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 9,
|
||||||
|
CategoryId = 3,
|
||||||
|
Description = "Sennheiser Digital Camera - High quality digital camera provided by Sennheiser - includes tripod",
|
||||||
|
ImageURL = "/Images/Electronic/Electronic4.png",
|
||||||
|
Name = "Sennheiser Digital Camera with Tripod",
|
||||||
|
Price = 600m,
|
||||||
|
Quantity = 20
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 10,
|
||||||
|
CategoryId = 3,
|
||||||
|
Description = "Canon Digital Camera - High quality digital camera provided by Canon",
|
||||||
|
ImageURL = "/Images/Electronic/Electronic5.png",
|
||||||
|
Name = "Canon Digital Camera",
|
||||||
|
Price = 500m,
|
||||||
|
Quantity = 15
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 11,
|
||||||
|
CategoryId = 3,
|
||||||
|
Description = "Gameboy - Provided by Nintendo",
|
||||||
|
ImageURL = "/Images/Electronic/technology6.png",
|
||||||
|
Name = "Nintendo Gameboy",
|
||||||
|
Price = 100m,
|
||||||
|
Quantity = 60
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 12,
|
||||||
|
CategoryId = 2,
|
||||||
|
Description = "Very comfortable black leather office chair",
|
||||||
|
ImageURL = "/Images/Furniture/Furniture1.png",
|
||||||
|
Name = "Black Leather Office Chair",
|
||||||
|
Price = 50m,
|
||||||
|
Quantity = 212
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 13,
|
||||||
|
CategoryId = 2,
|
||||||
|
Description = "Very comfortable pink leather office chair",
|
||||||
|
ImageURL = "/Images/Furniture/Furniture2.png",
|
||||||
|
Name = "Pink Leather Office Chair",
|
||||||
|
Price = 50m,
|
||||||
|
Quantity = 112
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 14,
|
||||||
|
CategoryId = 2,
|
||||||
|
Description = "Very comfortable lounge chair",
|
||||||
|
ImageURL = "/Images/Furniture/Furniture3.png",
|
||||||
|
Name = "Lounge Chair",
|
||||||
|
Price = 70m,
|
||||||
|
Quantity = 90
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 15,
|
||||||
|
CategoryId = 2,
|
||||||
|
Description = "Very comfortable Silver lounge chair",
|
||||||
|
ImageURL = "/Images/Furniture/Furniture4.png",
|
||||||
|
Name = "Silver Lounge Chair",
|
||||||
|
Price = 120m,
|
||||||
|
Quantity = 95
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 16,
|
||||||
|
CategoryId = 2,
|
||||||
|
Description = "White and blue Porcelain Table Lamp",
|
||||||
|
ImageURL = "/Images/Furniture/Furniture6.png",
|
||||||
|
Name = "Porcelain Table Lamp",
|
||||||
|
Price = 15m,
|
||||||
|
Quantity = 100
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 17,
|
||||||
|
CategoryId = 2,
|
||||||
|
Description = "Office Table Lamp",
|
||||||
|
ImageURL = "/Images/Furniture/Furniture7.png",
|
||||||
|
Name = "Office Table Lamp",
|
||||||
|
Price = 20m,
|
||||||
|
Quantity = 73
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 18,
|
||||||
|
CategoryId = 4,
|
||||||
|
Description = "Comfortable Puma Sneakers in most sizes",
|
||||||
|
ImageURL = "/Images/Shoes/Shoes1.png",
|
||||||
|
Name = "Puma Sneakers",
|
||||||
|
Price = 100m,
|
||||||
|
Quantity = 50
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 19,
|
||||||
|
CategoryId = 4,
|
||||||
|
Description = "Colorful trainsers - available in most sizes",
|
||||||
|
ImageURL = "/Images/Shoes/Shoes2.png",
|
||||||
|
Name = "Colorful Trainers",
|
||||||
|
Price = 150m,
|
||||||
|
Quantity = 60
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 20,
|
||||||
|
CategoryId = 4,
|
||||||
|
Description = "Blue Nike Trainers - available in most sizes",
|
||||||
|
ImageURL = "/Images/Shoes/Shoes3.png",
|
||||||
|
Name = "Blue Nike Trainers",
|
||||||
|
Price = 200m,
|
||||||
|
Quantity = 70
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 21,
|
||||||
|
CategoryId = 4,
|
||||||
|
Description = "Colorful Hummel Trainers - available in most sizes",
|
||||||
|
ImageURL = "/Images/Shoes/Shoes4.png",
|
||||||
|
Name = "Colorful Hummel Trainers",
|
||||||
|
Price = 120m,
|
||||||
|
Quantity = 120
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 22,
|
||||||
|
CategoryId = 4,
|
||||||
|
Description = "Red Nike Trainers - available in most sizes",
|
||||||
|
ImageURL = "/Images/Shoes/Shoes5.png",
|
||||||
|
Name = "Red Nike Trainers",
|
||||||
|
Price = 200m,
|
||||||
|
Quantity = 100
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 23,
|
||||||
|
CategoryId = 4,
|
||||||
|
Description = "Birkenstock Sandles - available in most sizes",
|
||||||
|
ImageURL = "/Images/Shoes/Shoes6.png",
|
||||||
|
Name = "Birkenstock Sandles",
|
||||||
|
Price = 50m,
|
||||||
|
Quantity = 150
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ShopOnline.Api.Entities.ProductCategory", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
|
||||||
|
|
||||||
|
b.Property<string>("IconCSS")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ProductCategories");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
IconCSS = "fas fa-spa",
|
||||||
|
Name = "Beauty"
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
IconCSS = "fas fa-couch",
|
||||||
|
Name = "Furniture"
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 3,
|
||||||
|
IconCSS = "fas fa-headphones",
|
||||||
|
Name = "Electronics"
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 4,
|
||||||
|
IconCSS = "fas fa-shoe-prints",
|
||||||
|
Name = "Shoes"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ShopOnline.Api.Entities.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
|
||||||
|
b.HasData(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
UserName = "Bob"
|
||||||
|
},
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Id = 2,
|
||||||
|
UserName = "Sarah"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace ShopOnline.Api.Migrations
|
||||||
|
{
|
||||||
|
public partial class AddProductCategoryIcons : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "IconCSS",
|
||||||
|
table: "ProductCategories",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "ProductCategories",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 1,
|
||||||
|
column: "IconCSS",
|
||||||
|
value: "fas fa-spa");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "ProductCategories",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 2,
|
||||||
|
column: "IconCSS",
|
||||||
|
value: "fas fa-couch");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "ProductCategories",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 3,
|
||||||
|
column: "IconCSS",
|
||||||
|
value: "fas fa-headphones");
|
||||||
|
|
||||||
|
migrationBuilder.UpdateData(
|
||||||
|
table: "ProductCategories",
|
||||||
|
keyColumn: "Id",
|
||||||
|
keyValue: 4,
|
||||||
|
column: "IconCSS",
|
||||||
|
value: "fas fa-shoe-prints");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "IconCSS",
|
||||||
|
table: "ProductCategories");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -345,6 +345,10 @@ namespace ShopOnline.Api.Migrations
|
||||||
|
|
||||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
|
||||||
|
|
||||||
|
b.Property<string>("IconCSS")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("nvarchar(max)");
|
.HasColumnType("nvarchar(max)");
|
||||||
|
@ -357,21 +361,25 @@ namespace ShopOnline.Api.Migrations
|
||||||
new
|
new
|
||||||
{
|
{
|
||||||
Id = 1,
|
Id = 1,
|
||||||
|
IconCSS = "fas fa-spa",
|
||||||
Name = "Beauty"
|
Name = "Beauty"
|
||||||
},
|
},
|
||||||
new
|
new
|
||||||
{
|
{
|
||||||
Id = 2,
|
Id = 2,
|
||||||
|
IconCSS = "fas fa-couch",
|
||||||
Name = "Furniture"
|
Name = "Furniture"
|
||||||
},
|
},
|
||||||
new
|
new
|
||||||
{
|
{
|
||||||
Id = 3,
|
Id = 3,
|
||||||
|
IconCSS = "fas fa-headphones",
|
||||||
Name = "Electronics"
|
Name = "Electronics"
|
||||||
},
|
},
|
||||||
new
|
new
|
||||||
{
|
{
|
||||||
Id = 4,
|
Id = 4,
|
||||||
|
IconCSS = "fas fa-shoe-prints",
|
||||||
Name = "Shoes"
|
Name = "Shoes"
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,6 +8,8 @@ namespace ShopOnline.Api.Repositories.Contracts
|
||||||
Task<Product> GetItem(int id);
|
Task<Product> GetItem(int id);
|
||||||
|
|
||||||
Task<IEnumerable<ProductCategory>> GetCategories();
|
Task<IEnumerable<ProductCategory>> GetCategories();
|
||||||
|
|
||||||
Task<ProductCategory> GetCategory(int id);
|
Task<ProductCategory> GetCategory(int id);
|
||||||
|
Task<IEnumerable<Product>> GetItemsByCategory(int id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,5 +36,14 @@ namespace ShopOnline.Api.Repositories
|
||||||
|
|
||||||
return products;
|
return products;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Product>> GetItemsByCategory(int id)
|
||||||
|
{
|
||||||
|
var products = await (from product in shopOnlineDbContext.Products
|
||||||
|
where product.CategoryId == id
|
||||||
|
select product).ToListAsync();
|
||||||
|
|
||||||
|
return products;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace ShopOnline.Models.Dtos
|
||||||
|
{
|
||||||
|
public class ProductCategoryDto
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string IconCSS { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1 @@
|
||||||
<div class="lds-spin"><i class="custom fa-regular fa-heart fa-10x" style="background: transparent;"></i></div>
|
<div class="lds-spin"><i class="custom fa-regular fa-heart fa-9x" style="background: transparent;"></i></div>
|
|
@ -22,7 +22,7 @@ else
|
||||||
@foreach (var prodGroup in GetGroupedProductsByCategory())
|
@foreach (var prodGroup in GetGroupedProductsByCategory())
|
||||||
{
|
{
|
||||||
<div class="row mt-3">
|
<div class="row mt-3">
|
||||||
<h4>@prodGroup.FirstOrDefault(pg=>pg.CategoryId == prodGroup.Key).CategoryName</h4>
|
<h3>@prodGroup.FirstOrDefault(pg=>pg.CategoryId == prodGroup.Key).CategoryName</h3>
|
||||||
<DisplayProducts Products="@prodGroup.Take(4)"></DisplayProducts>
|
<DisplayProducts Products="@prodGroup.Take(4)"></DisplayProducts>
|
||||||
</div>
|
</div>
|
||||||
<hr class="mb-3" />
|
<hr class="mb-3" />
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
@page "/ProductsByCategory/{CategoryId:int}"
|
||||||
|
@inherits ProductsByCategoryBase
|
||||||
|
|
||||||
|
@if (Products == null && ErrorMessage == null)
|
||||||
|
{
|
||||||
|
<div class="row d-flex justify-content-center">
|
||||||
|
<div class="col-md-1">
|
||||||
|
<DisplayCustomSpinner />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else if (ErrorMessage != null)
|
||||||
|
{
|
||||||
|
<DisplayError ErrorMessage="@ErrorMessage" />
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
<h2>@CategoryName</h2>
|
||||||
|
@if(Products.Count() > 0)
|
||||||
|
{
|
||||||
|
<div class="row mt-3">
|
||||||
|
<DisplayProducts Products="@Products"></DisplayProducts>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using ShopOnline.Models.Dtos;
|
||||||
|
using ShopOnline.Web.Services.Contracts;
|
||||||
|
|
||||||
|
namespace ShopOnline.Web.Pages
|
||||||
|
{
|
||||||
|
public class ProductsByCategoryBase:ComponentBase
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public int CategoryId { get; set; }
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
public IProductService ProductService { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<ProductDto> Products { get; set; }
|
||||||
|
public string CategoryName { get; set; }
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
protected override async Task OnParametersSetAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Products = await ProductService.GetItemsByCategory(CategoryId);
|
||||||
|
if(Products != null && Products.Count() > 0)
|
||||||
|
{
|
||||||
|
var productDto = Products.FirstOrDefault(p => p.CategoryId == CategoryId);
|
||||||
|
if(productDto != null)
|
||||||
|
{
|
||||||
|
CategoryName = productDto.CategoryName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ErrorMessage = ex.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ namespace ShopOnline.Web.Services.Contracts
|
||||||
{
|
{
|
||||||
Task<IEnumerable<ProductDto>> GetItems();
|
Task<IEnumerable<ProductDto>> GetItems();
|
||||||
Task<ProductDto> GetItem(int id);
|
Task<ProductDto> GetItem(int id);
|
||||||
|
Task<IEnumerable<ProductCategoryDto>> GetProductCategories();
|
||||||
|
Task<IEnumerable<ProductDto>> GetItemsByCategory(int categoryId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,5 +65,59 @@ namespace ShopOnline.Web.Services
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<ProductDto>> GetItemsByCategory(int categoryId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await httpClient.GetAsync($"api/Product/{categoryId}/GetItemsByCategory");
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
if (response.StatusCode == System.Net.HttpStatusCode.NoContent)
|
||||||
|
{
|
||||||
|
return Enumerable.Empty<ProductDto>();
|
||||||
|
}
|
||||||
|
return await response.Content.ReadFromJsonAsync<IEnumerable<ProductDto>>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var message = await response.Content.ReadAsStringAsync();
|
||||||
|
throw new Exception($"Http Status Code - {response.StatusCode} Message - {message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<ProductCategoryDto>> GetProductCategories()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await httpClient.GetAsync("api/Product/GetProductCategories");
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
if(response.StatusCode == System.Net.HttpStatusCode.NoContent)
|
||||||
|
{
|
||||||
|
return Enumerable.Empty<ProductCategoryDto>();
|
||||||
|
}
|
||||||
|
return await response.Content.ReadFromJsonAsync<IEnumerable<ProductCategoryDto>>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var message = await response.Content.ReadAsStringAsync();
|
||||||
|
throw new Exception($"Http Status Code - {response.StatusCode} Message - {message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
<div class="top-row ps-3 navbar navbar-dark">
|
<div class="top-row ps-3 navbar navbar-dark">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="navbar-brand" href="">ShopOnline.Web</a>
|
<a class="navbar-brand" href=""><span class="fas fa-shopping-cart" aria-hidden="true"></span> ShopOnline</a>
|
||||||
<button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
|
<button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
|
@ -14,22 +14,15 @@
|
||||||
<nav class="flex-column">
|
<nav class="flex-column">
|
||||||
<div class="nav-item px-3">
|
<div class="nav-item px-3">
|
||||||
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
||||||
<span class="oi oi-home" aria-hidden="true"></span> Home
|
<span class="fas fa-home" aria-hidden="true"></span> Home
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
<div class="nav-item px-3">
|
|
||||||
<NavLink class="nav-link" href="counter">
|
|
||||||
<span class="oi oi-plus" aria-hidden="true"></span> Counter
|
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
<div class="nav-item px-3">
|
|
||||||
<NavLink class="nav-link" href="fetchdata">
|
|
||||||
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
|
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ProductCategoriesNavMenu />
|
||||||
|
|
||||||
<div class="nav-item px-3 d-sm-none">
|
<div class="nav-item px-3 d-sm-none">
|
||||||
<NavLink class="nav-link" href="ShoppingCart">
|
<NavLink class="nav-link" href="ShoppingCart">
|
||||||
<span class="fas fa-shopping-cart" aria-hidden="true"></span> Shopping Cart (<b>@shoppingCartItemsCount</b>)
|
<span class="fas fa-shopping-cart" aria-hidden="true"></span> Shopping Cart (<b>@shoppingCartItemsCount</b>)
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
@inherits ProductCategoriesNavMenuBase
|
||||||
|
|
||||||
|
@if(ProductCategoryDtos == null && ErrorMessage == null)
|
||||||
|
{
|
||||||
|
<DisplaySpinner />
|
||||||
|
}
|
||||||
|
else if(ErrorMessage != null)
|
||||||
|
{
|
||||||
|
<DisplayError ErrorMessage="@ErrorMessage" />
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
@foreach(var productCategory in ProductCategoryDtos)
|
||||||
|
{
|
||||||
|
var link = "/ProductsByCategory/" + productCategory.Id;
|
||||||
|
<div class="nav-item px-3">
|
||||||
|
<NavLink class="nav-link" href="@link">
|
||||||
|
<span class="@productCategory.IconCSS"></span> @productCategory.Name
|
||||||
|
</NavLink>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
.navbar-toggler {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-row {
|
||||||
|
height: 3.5rem;
|
||||||
|
background-color: rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-brand {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.oi {
|
||||||
|
width: 2rem;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
vertical-align: text-top;
|
||||||
|
top: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item:first-of-type {
|
||||||
|
padding-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item:last-of-type {
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item ::deep a {
|
||||||
|
color: #d7d7d7;
|
||||||
|
border-radius: 4px;
|
||||||
|
height: 3rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item ::deep a.active {
|
||||||
|
background-color: rgba(255,255,255,0.25);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item ::deep a:hover {
|
||||||
|
background-color: rgba(255,255,255,0.1);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 641px) {
|
||||||
|
.navbar-toggler {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapse {
|
||||||
|
/* Never collapse the sidebar for wide screens */
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using ShopOnline.Models.Dtos;
|
||||||
|
using ShopOnline.Web.Services.Contracts;
|
||||||
|
|
||||||
|
namespace ShopOnline.Web.Shared
|
||||||
|
{
|
||||||
|
public class ProductCategoriesNavMenuBase:ComponentBase
|
||||||
|
{
|
||||||
|
[Inject]
|
||||||
|
public IProductService ProductService { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<ProductCategoryDto> ProductCategoryDtos { get; set; }
|
||||||
|
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ProductCategoryDtos = await ProductService.GetProductCategories();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
ErrorMessage = ex.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,3 +10,4 @@
|
||||||
@using ShopOnline.Web.Shared
|
@using ShopOnline.Web.Shared
|
||||||
@using ShopOnline.Models.Dtos
|
@using ShopOnline.Models.Dtos
|
||||||
@using ShopOnline.Web.Services.Contracts
|
@using ShopOnline.Web.Services.Contracts
|
||||||
|
@using ShopOnline.Web.Pages
|
||||||
|
|
Loading…
Reference in New Issue