diff --git a/src/Imprink.Application/Domains/ProductVariants/CreateProductVariantHandler.cs b/src/Imprink.Application/Domains/ProductVariants/CreateProductVariantHandler.cs index 9a6aef9..d1ab5fd 100644 --- a/src/Imprink.Application/Domains/ProductVariants/CreateProductVariantHandler.cs +++ b/src/Imprink.Application/Domains/ProductVariants/CreateProductVariantHandler.cs @@ -1,3 +1,4 @@ +using AutoMapper; using Imprink.Application.Products.Dtos; using Imprink.Domain.Entities.Product; using MediatR; @@ -16,7 +17,7 @@ public class CreateProductVariantCommand : IRequest public bool IsActive { get; set; } = true; } -public class CreateProductVariantHandler(IUnitOfWork unitOfWork) +public class CreateProductVariantHandler(IUnitOfWork unitOfWork, IMapper mapper) : IRequestHandler { public async Task Handle(CreateProductVariantCommand request, CancellationToken cancellationToken) @@ -25,36 +26,16 @@ public class CreateProductVariantHandler(IUnitOfWork unitOfWork) try { - var productVariant = new ProductVariant - { - ProductId = request.ProductId, - Size = request.Size, - Color = request.Color, - Price = request.Price, - ImageUrl = request.ImageUrl, - Sku = request.Sku, - StockQuantity = request.StockQuantity, - IsActive = request.IsActive, - Product = null! - }; + var productVariant = mapper.Map(request); + + productVariant.Product = null!; var createdVariant = await unitOfWork.ProductVariantRepository.AddAsync(productVariant, cancellationToken); + + await unitOfWork.SaveAsync(cancellationToken); await unitOfWork.CommitTransactionAsync(cancellationToken); - - return new ProductVariantDto - { - Id = createdVariant.Id, - ProductId = createdVariant.ProductId, - Size = createdVariant.Size, - Color = createdVariant.Color, - Price = createdVariant.Price, - ImageUrl = createdVariant.ImageUrl, - Sku = createdVariant.Sku, - StockQuantity = createdVariant.StockQuantity, - IsActive = createdVariant.IsActive, - CreatedAt = createdVariant.CreatedAt, - ModifiedAt = createdVariant.ModifiedAt - }; + + return mapper.Map(createdVariant); } catch { diff --git a/src/Imprink.Application/Domains/ProductVariants/DeleteProductVariantHandler.cs b/src/Imprink.Application/Domains/ProductVariants/DeleteProductVariantHandler.cs index 10e257e..7fdf8a9 100644 --- a/src/Imprink.Application/Domains/ProductVariants/DeleteProductVariantHandler.cs +++ b/src/Imprink.Application/Domains/ProductVariants/DeleteProductVariantHandler.cs @@ -16,6 +16,7 @@ public class DeleteProductVariantHandler(IUnitOfWork unitOfWork) : IRequestHandl try { var exists = await unitOfWork.ProductVariantRepository.ExistsAsync(request.Id, cancellationToken); + if (!exists) { await unitOfWork.RollbackTransactionAsync(cancellationToken); @@ -23,7 +24,10 @@ public class DeleteProductVariantHandler(IUnitOfWork unitOfWork) : IRequestHandl } await unitOfWork.ProductVariantRepository.DeleteAsync(request.Id, cancellationToken); + + await unitOfWork.SaveAsync(cancellationToken); await unitOfWork.CommitTransactionAsync(cancellationToken); + return true; } catch diff --git a/src/Imprink.Application/Domains/ProductVariants/GetProductVariantsHandler.cs b/src/Imprink.Application/Domains/ProductVariants/GetProductVariantsHandler.cs index 609036b..c572305 100644 --- a/src/Imprink.Application/Domains/ProductVariants/GetProductVariantsHandler.cs +++ b/src/Imprink.Application/Domains/ProductVariants/GetProductVariantsHandler.cs @@ -1,6 +1,8 @@ +using AutoMapper; using Imprink.Application.Products.Dtos; using Imprink.Domain.Entities.Product; using MediatR; +using Microsoft.Extensions.Logging; namespace Imprink.Application.Domains.ProductVariants; @@ -11,7 +13,7 @@ public class GetProductVariantsQuery : IRequest> public bool InStockOnly { get; set; } = false; } -public class GetProductVariantsHandler(IUnitOfWork unitOfWork) +public class GetProductVariantsHandler(IUnitOfWork unitOfWork, IMapper mapper, ILogger logger) : IRequestHandler> { public async Task> Handle(GetProductVariantsQuery request, CancellationToken cancellationToken) @@ -37,33 +39,7 @@ public class GetProductVariantsHandler(IUnitOfWork unitOfWork) { variants = new List(); } - - return variants.Select(pv => new ProductVariantDto - { - Id = pv.Id, - ProductId = pv.ProductId, - Size = pv.Size, - Color = pv.Color, - Price = pv.Price, - ImageUrl = pv.ImageUrl, - Sku = pv.Sku, - StockQuantity = pv.StockQuantity, - IsActive = pv.IsActive, - Product = new ProductDto - { - Id = pv.Product.Id, - Name = pv.Product.Name, - Description = pv.Product.Description, - BasePrice = pv.Product.BasePrice, - IsCustomizable = pv.Product.IsCustomizable, - IsActive = pv.Product.IsActive, - ImageUrl = pv.Product.ImageUrl, - CategoryId = pv.Product.CategoryId, - CreatedAt = pv.Product.CreatedAt, - ModifiedAt = pv.Product.ModifiedAt - }, - CreatedAt = pv.CreatedAt, - ModifiedAt = pv.ModifiedAt - }); + + return mapper.Map>(variants); } } \ No newline at end of file diff --git a/src/Imprink.Application/Domains/ProductVariants/UpdateProductVariantHandler.cs b/src/Imprink.Application/Domains/ProductVariants/UpdateProductVariantHandler.cs index b7574b1..07a7724 100644 --- a/src/Imprink.Application/Domains/ProductVariants/UpdateProductVariantHandler.cs +++ b/src/Imprink.Application/Domains/ProductVariants/UpdateProductVariantHandler.cs @@ -1,3 +1,4 @@ +using AutoMapper; using Imprink.Application.Exceptions; using Imprink.Application.Products.Dtos; using MediatR; @@ -17,7 +18,7 @@ public class UpdateProductVariantCommand : IRequest public bool IsActive { get; set; } } -public class UpdateProductVariantHandler(IUnitOfWork unitOfWork) +public class UpdateProductVariantHandler(IUnitOfWork unitOfWork, IMapper mapper) : IRequestHandler { public async Task Handle(UpdateProductVariantCommand request, CancellationToken cancellationToken) @@ -29,36 +30,16 @@ public class UpdateProductVariantHandler(IUnitOfWork unitOfWork) var existingVariant = await unitOfWork.ProductVariantRepository.GetByIdAsync(request.Id, cancellationToken); if (existingVariant == null) - { throw new NotFoundException($"Product variant with ID {request.Id} not found."); - } - - existingVariant.ProductId = request.ProductId; - existingVariant.Size = request.Size; - existingVariant.Color = request.Color; - existingVariant.Price = request.Price; - existingVariant.ImageUrl = request.ImageUrl; - existingVariant.Sku = request.Sku; - existingVariant.StockQuantity = request.StockQuantity; - existingVariant.IsActive = request.IsActive; + + mapper.Map(request, existingVariant); var updatedVariant = await unitOfWork.ProductVariantRepository.UpdateAsync(existingVariant, cancellationToken); + + await unitOfWork.SaveAsync(cancellationToken); await unitOfWork.CommitTransactionAsync(cancellationToken); - return new ProductVariantDto - { - Id = updatedVariant.Id, - ProductId = updatedVariant.ProductId, - Size = updatedVariant.Size, - Color = updatedVariant.Color, - Price = updatedVariant.Price, - ImageUrl = updatedVariant.ImageUrl, - Sku = updatedVariant.Sku, - StockQuantity = updatedVariant.StockQuantity, - IsActive = updatedVariant.IsActive, - CreatedAt = updatedVariant.CreatedAt, - ModifiedAt = updatedVariant.ModifiedAt - }; + return mapper.Map(updatedVariant); } catch { diff --git a/src/Imprink.Application/Domains/Products/CreateProductHandler.cs b/src/Imprink.Application/Domains/Products/CreateProductHandler.cs index a9a0df7..2e84304 100644 --- a/src/Imprink.Application/Domains/Products/CreateProductHandler.cs +++ b/src/Imprink.Application/Domains/Products/CreateProductHandler.cs @@ -1,3 +1,4 @@ +using AutoMapper; using Imprink.Application.Products.Dtos; using Imprink.Domain.Entities.Product; using MediatR; @@ -15,7 +16,7 @@ public class CreateProductCommand : IRequest public Guid? CategoryId { get; set; } } -public class CreateProductHandler(IUnitOfWork unitOfWork) : IRequestHandler +public class CreateProductHandler(IUnitOfWork unitOfWork, IMapper mapper) : IRequestHandler { public async Task Handle(CreateProductCommand request, CancellationToken cancellationToken) { @@ -23,49 +24,18 @@ public class CreateProductHandler(IUnitOfWork unitOfWork) : IRequestHandler(request); var createdProduct = await unitOfWork.ProductRepository.AddAsync(product, cancellationToken); - - var categoryDto = new CategoryDto + + if (createdProduct.CategoryId.HasValue) { - Id = createdProduct.Category.Id, - Name = createdProduct.Category.Name, - Description = createdProduct.Category.Description, - ImageUrl = createdProduct.Category.ImageUrl, - SortOrder = createdProduct.Category.SortOrder, - IsActive = createdProduct.Category.IsActive, - ParentCategoryId = createdProduct.Category.ParentCategoryId, - CreatedAt = createdProduct.Category.CreatedAt, - ModifiedAt = createdProduct.Category.ModifiedAt - }; + createdProduct.Category = (await unitOfWork.CategoryRepository.GetByIdAsync(createdProduct.CategoryId.Value, cancellationToken))!; + } await unitOfWork.CommitTransactionAsync(cancellationToken); - return new ProductDto - { - Id = createdProduct.Id, - Name = createdProduct.Name, - Description = createdProduct.Description, - BasePrice = createdProduct.BasePrice, - IsCustomizable = createdProduct.IsCustomizable, - IsActive = createdProduct.IsActive, - ImageUrl = createdProduct.ImageUrl, - CategoryId = createdProduct.CategoryId, - Category = categoryDto, - CreatedAt = createdProduct.CreatedAt, - ModifiedAt = createdProduct.ModifiedAt - }; + return mapper.Map(createdProduct); } catch { diff --git a/src/Imprink.Application/Domains/Users/DeleteUserRoleHandler.cs b/src/Imprink.Application/Domains/Users/DeleteUserRoleHandler.cs index 28bed6d..7755d01 100644 --- a/src/Imprink.Application/Domains/Users/DeleteUserRoleHandler.cs +++ b/src/Imprink.Application/Domains/Users/DeleteUserRoleHandler.cs @@ -1,3 +1,4 @@ +using AutoMapper; using Imprink.Application.Exceptions; using Imprink.Application.Users.Dtos; using MediatR; @@ -6,7 +7,7 @@ namespace Imprink.Application.Domains.Users; public record DeleteUserRoleCommand(string Sub, Guid RoleId) : IRequest; -public class DeleteUserRoleHandler(IUnitOfWork uw) : IRequestHandler +public class DeleteUserRoleHandler(IUnitOfWork uw, IMapper mapper) : IRequestHandler { public async Task Handle(DeleteUserRoleCommand request, CancellationToken cancellationToken) { @@ -27,11 +28,7 @@ public class DeleteUserRoleHandler(IUnitOfWork uw) : IRequestHandler(removedRole); } catch { diff --git a/src/Imprink.Application/Domains/Users/GetAllRolesHandler.cs b/src/Imprink.Application/Domains/Users/GetAllRolesHandler.cs new file mode 100644 index 0000000..12d3df2 --- /dev/null +++ b/src/Imprink.Application/Domains/Users/GetAllRolesHandler.cs @@ -0,0 +1,17 @@ +using AutoMapper; +using Imprink.Application.Users.Dtos; +using MediatR; + +namespace Imprink.Application.Domains.Users; + +public record GetAllRolesCommand : IRequest>; + +public class GetAllRolesHandler(IUnitOfWork uw, IMapper mapper): IRequestHandler> +{ + public async Task> Handle(GetAllRolesCommand request, CancellationToken cancellationToken) + { + var roles = await uw.RoleRepository.GetAllRolesAsync(cancellationToken); + + return mapper.Map>(roles); + } +} \ No newline at end of file diff --git a/src/Imprink.Application/Domains/Users/GetUserRolesHandler.cs b/src/Imprink.Application/Domains/Users/GetUserRolesHandler.cs index acd95a2..902900f 100644 --- a/src/Imprink.Application/Domains/Users/GetUserRolesHandler.cs +++ b/src/Imprink.Application/Domains/Users/GetUserRolesHandler.cs @@ -1,3 +1,4 @@ +using AutoMapper; using Imprink.Application.Exceptions; using Imprink.Application.Users.Dtos; using MediatR; @@ -6,7 +7,7 @@ namespace Imprink.Application.Domains.Users; public record GetUserRolesCommand(string Sub) : IRequest>; -public class GetUserRolesHandler(IUnitOfWork uw): IRequestHandler> +public class GetUserRolesHandler(IUnitOfWork uw, IMapper mapper): IRequestHandler> { public async Task> Handle(GetUserRolesCommand request, CancellationToken cancellationToken) { @@ -15,10 +16,6 @@ public class GetUserRolesHandler(IUnitOfWork uw): IRequestHandler new RoleDto - { - RoleId = role.Id, - RoleName = role.RoleName - }).ToList(); + return mapper.Map>(roles); } } \ No newline at end of file diff --git a/src/Imprink.Application/Domains/Users/SetUserFullNameHandler.cs b/src/Imprink.Application/Domains/Users/SetUserFullNameHandler.cs index b8b4ce1..7fa91ce 100644 --- a/src/Imprink.Application/Domains/Users/SetUserFullNameHandler.cs +++ b/src/Imprink.Application/Domains/Users/SetUserFullNameHandler.cs @@ -1,3 +1,4 @@ +using AutoMapper; using Imprink.Application.Exceptions; using Imprink.Application.Services; using Imprink.Application.Users.Dtos; @@ -7,7 +8,7 @@ namespace Imprink.Application.Domains.Users; public record SetUserFullNameCommand(string FirstName, string LastName) : IRequest; -public class SetUserFullNameHandler(IUnitOfWork uw, ICurrentUserService userService) : IRequestHandler +public class SetUserFullNameHandler(IUnitOfWork uw, IMapper mapper, ICurrentUserService userService) : IRequestHandler { public async Task Handle(SetUserFullNameCommand request, CancellationToken cancellationToken) { @@ -16,28 +17,19 @@ public class SetUserFullNameHandler(IUnitOfWork uw, ICurrentUserService userServ try { var currentUser = userService.GetCurrentUserId(); + if (currentUser == null) throw new NotFoundException("User token could not be accessed."); var user = await uw.UserRepository.SetUserFullNameAsync(currentUser, request.FirstName, request.LastName, cancellationToken); + if (user == null) throw new DataUpdateException("User name could not be updated."); await uw.SaveAsync(cancellationToken); await uw.CommitTransactionAsync(cancellationToken); - return new UserDto - { - Id = user.Id, - Name = user.Name, - Nickname = user.Nickname, - Email = user.Email, - EmailVerified = user.EmailVerified, - FirstName = user.FirstName, - LastName = user.LastName, - PhoneNumber = user.PhoneNumber, - IsActive = user.IsActive - }; + return mapper.Map(user); } catch { diff --git a/src/Imprink.Application/Domains/Users/SetUserPhoneHandler.cs b/src/Imprink.Application/Domains/Users/SetUserPhoneHandler.cs index b7b5289..369c15d 100644 --- a/src/Imprink.Application/Domains/Users/SetUserPhoneHandler.cs +++ b/src/Imprink.Application/Domains/Users/SetUserPhoneHandler.cs @@ -1,3 +1,4 @@ +using AutoMapper; using Imprink.Application.Exceptions; using Imprink.Application.Services; using Imprink.Application.Users.Dtos; @@ -7,7 +8,7 @@ namespace Imprink.Application.Domains.Users; public record SetUserPhoneCommand(string PhoneNumber) : IRequest; -public class SetUserPhoneHandler(IUnitOfWork uw, ICurrentUserService userService) : IRequestHandler +public class SetUserPhoneHandler(IUnitOfWork uw, IMapper mapper, ICurrentUserService userService) : IRequestHandler { public async Task Handle(SetUserPhoneCommand request, CancellationToken cancellationToken) { @@ -16,28 +17,19 @@ public class SetUserPhoneHandler(IUnitOfWork uw, ICurrentUserService userService try { var currentUser = userService.GetCurrentUserId(); + if (currentUser == null) throw new NotFoundException("User token could not be accessed."); var user = await uw.UserRepository.SetUserPhoneAsync(currentUser, request.PhoneNumber, cancellationToken); + if (user == null) throw new DataUpdateException("User phone could not be updated."); await uw.SaveAsync(cancellationToken); await uw.CommitTransactionAsync(cancellationToken); - return new UserDto - { - Id = user.Id, - Name = user.Name, - Nickname = user.Nickname, - Email = user.Email, - EmailVerified = user.EmailVerified, - FirstName = user.FirstName, - LastName = user.LastName, - PhoneNumber = user.PhoneNumber, - IsActive = user.IsActive - }; + return mapper.Map(user); } catch { diff --git a/src/Imprink.Application/Domains/Users/SetUserRoleHandler.cs b/src/Imprink.Application/Domains/Users/SetUserRoleHandler.cs index 426eb8b..d681363 100644 --- a/src/Imprink.Application/Domains/Users/SetUserRoleHandler.cs +++ b/src/Imprink.Application/Domains/Users/SetUserRoleHandler.cs @@ -1,3 +1,4 @@ +using AutoMapper; using Imprink.Application.Exceptions; using Imprink.Application.Users.Dtos; using Imprink.Domain.Entities.Users; @@ -7,7 +8,7 @@ namespace Imprink.Application.Domains.Users; public record SetUserRoleCommand(string Sub, Guid RoleId) : IRequest; -public class SetUserRoleHandler(IUnitOfWork uw) : IRequestHandler +public class SetUserRoleHandler(IUnitOfWork uw, IMapper mapper) : IRequestHandler { public async Task Handle(SetUserRoleCommand request, CancellationToken cancellationToken) { @@ -29,11 +30,7 @@ public class SetUserRoleHandler(IUnitOfWork uw) : IRequestHandler(addedRole); } catch { diff --git a/src/Imprink.Application/Domains/Users/SyncUserHandler.cs b/src/Imprink.Application/Domains/Users/SyncUserHandler.cs index 503521a..ae2cac8 100644 --- a/src/Imprink.Application/Domains/Users/SyncUserHandler.cs +++ b/src/Imprink.Application/Domains/Users/SyncUserHandler.cs @@ -1,3 +1,4 @@ +using AutoMapper; using Imprink.Application.Users.Dtos; using Imprink.Domain.Models; using MediatR; @@ -6,7 +7,7 @@ namespace Imprink.Application.Domains.Users; public record SyncUserCommand(Auth0User User) : IRequest; -public class SyncUserHandler(IUnitOfWork uw): IRequestHandler +public class SyncUserHandler(IUnitOfWork uw, IMapper mapper): IRequestHandler { public async Task Handle(SyncUserCommand request, CancellationToken cancellationToken) { @@ -16,23 +17,13 @@ public class SyncUserHandler(IUnitOfWork uw): IRequestHandler(user); } catch { diff --git a/src/Imprink.Application/Imprink.Application.csproj b/src/Imprink.Application/Imprink.Application.csproj index 5a13bfc..17480c4 100644 --- a/src/Imprink.Application/Imprink.Application.csproj +++ b/src/Imprink.Application/Imprink.Application.csproj @@ -19,8 +19,4 @@ - - - - diff --git a/src/Imprink.Application/Mappings/MappingProfile.cs b/src/Imprink.Application/Mappings/MappingProfile.cs new file mode 100644 index 0000000..572be15 --- /dev/null +++ b/src/Imprink.Application/Mappings/MappingProfile.cs @@ -0,0 +1,5 @@ +using AutoMapper; + +namespace Imprink.Application.Mappings; + +public abstract class MappingProfile : Profile { } \ No newline at end of file diff --git a/src/Imprink.Application/Mappings/ProductMappingProfile.cs b/src/Imprink.Application/Mappings/ProductMappingProfile.cs new file mode 100644 index 0000000..e58e2cc --- /dev/null +++ b/src/Imprink.Application/Mappings/ProductMappingProfile.cs @@ -0,0 +1,42 @@ +using AutoMapper; +using Imprink.Application.Domains.Products; +using Imprink.Application.Domains.ProductVariants; +using Imprink.Application.Products.Dtos; +using Imprink.Domain.Entities.Product; + +namespace Imprink.Application.Mappings; + +public class ProductMappingProfile: Profile +{ + public ProductMappingProfile() + { + CreateMap() + .ForMember(dest => dest.Id, opt => opt.Ignore()) + .ForMember(dest => dest.CreatedAt, opt => opt.Ignore()) + .ForMember(dest => dest.ModifiedAt, opt => opt.Ignore()) + .ForMember(dest => dest.CreatedBy, opt => opt.Ignore()) + .ForMember(dest => dest.ModifiedBy, opt => opt.Ignore()) + .ForMember(dest => dest.Product, opt => opt.Ignore()) + .ForMember(dest => dest.OrderItems, opt => opt.Ignore()); + + CreateMap(); + CreateMap() + .ForMember(dest => dest.CreatedBy, opt => opt.Ignore()) + .ForMember(dest => dest.ModifiedBy, opt => opt.Ignore()) + .ForMember(dest => dest.OrderItems, opt => opt.Ignore()); + + CreateMap(); + + CreateMap() + .ForMember(dest => dest.Id, opt => opt.Ignore()) + .ForMember(dest => dest.CreatedAt, opt => opt.Ignore()) + .ForMember(dest => dest.ModifiedAt, opt => opt.Ignore()) + .ForMember(dest => dest.CreatedBy, opt => opt.Ignore()) + .ForMember(dest => dest.ModifiedBy, opt => opt.Ignore()) + .ForMember(dest => dest.Category, opt => opt.Ignore()) + .ForMember(dest => dest.ProductVariants, opt => opt.Ignore()) + .ForMember(dest => dest.OrderItems, opt => opt.Ignore()); + + CreateMap(); + } +} \ No newline at end of file diff --git a/src/Imprink.Application/Mappings/UserMappingProfile.cs b/src/Imprink.Application/Mappings/UserMappingProfile.cs new file mode 100644 index 0000000..8eda701 --- /dev/null +++ b/src/Imprink.Application/Mappings/UserMappingProfile.cs @@ -0,0 +1,42 @@ +using System.Security.Claims; +using AutoMapper; +using Imprink.Application.Users.Dtos; +using Imprink.Domain.Entities.Users; +using Imprink.Domain.Models; + +namespace Imprink.Application.Mappings; + +public class UserMappingProfile: Profile +{ + public UserMappingProfile() + { + CreateMap() + .ForMember(dest => dest.DefaultAddress, opt => opt.Ignore()) + .ForMember(dest => dest.Roles, opt => opt.Ignore()); + + CreateMap() + .ForMember(dest => dest.DefaultAddress, opt => opt.Ignore()) + .ForMember(dest => dest.Roles, opt => opt.Ignore()); + + CreateMap(); + CreateMap(); + + CreateMap() + .ForMember(dest => dest.RoleId, opt => opt.MapFrom(src => src.Id)); + CreateMap() + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.RoleId)) + .ForMember(dest => dest.UserRoles, opt => opt.Ignore()); + + CreateMap() + .ForMember(dest => dest.Sub, opt => opt.MapFrom(src => + src.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)!.Value)) + .ForMember(dest => dest.Name, opt => opt.MapFrom(src => + src.Claims.FirstOrDefault(c => c.Type == "name")!.Value)) + .ForMember(dest => dest.Nickname, opt => opt.MapFrom(src => + src.Claims.FirstOrDefault(c => c.Type == "nickname")!.Value)) + .ForMember(dest => dest.Email, opt => opt.MapFrom(src => + src.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)!.Value)) + .ForMember(dest => dest.EmailVerified, opt => opt.MapFrom(src => + src.Claims.FirstOrDefault(c => c.Type == "email_verified")!.Value == "true")); + } +} \ No newline at end of file diff --git a/src/Imprink.WebApi/Controllers/Users/UsersController.cs b/src/Imprink.WebApi/Controllers/Users/UsersController.cs index 53b6830..6819114 100644 --- a/src/Imprink.WebApi/Controllers/Users/UsersController.cs +++ b/src/Imprink.WebApi/Controllers/Users/UsersController.cs @@ -1,5 +1,7 @@ using System.Security.Claims; +using AutoMapper; using Imprink.Application.Domains.Users; +using Imprink.Application.Users.Dtos; using Imprink.Domain.Models; using MediatR; using Microsoft.AspNetCore.Authorization; @@ -9,30 +11,21 @@ namespace Imprink.WebApi.Controllers.Users; [ApiController] [Route("/api/users")] -public class UsersController(IMediator mediator) : ControllerBase +public class UsersController(IMediator mediator, IMapper mapper) : ControllerBase { [Authorize] [HttpPost("me/sync")] public async Task SyncMyProfile() { - var claims = User.Claims as Claim[] ?? User.Claims.ToArray(); - - var auth0User = new Auth0User - { - Sub = claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value ?? string.Empty, - Name = claims.FirstOrDefault(c => c.Type == "name")?.Value ?? string.Empty, - Nickname = claims.FirstOrDefault(c => c.Type == "nickname")?.Value ?? string.Empty, - Email = claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value ?? string.Empty, - EmailVerified = bool.TryParse(claims.FirstOrDefault(c => c.Type == "email_verified")?.Value, out var emailVerified) && emailVerified - }; + var auth0User = mapper.Map(User); await mediator.Send(new SyncUserCommand(auth0User)); - return Ok("User profile synchronized."); + return Ok("Synced"); } [Authorize] [HttpGet("me/roles")] - public async Task GetMyRoles() + public async Task>> GetMyRoles() { var sub = User.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty; return Ok(await mediator.Send(new GetUserRolesCommand(sub))); @@ -40,21 +33,29 @@ public class UsersController(IMediator mediator) : ControllerBase [Authorize] [HttpPut("me/phone")] - public async Task UpdateMyPhone([FromBody] SetUserPhoneCommand command) + public async Task> UpdateMyPhone([FromBody] SetUserPhoneCommand command) { return Ok(await mediator.Send(command)); } [Authorize] [HttpPut("me/fullname")] - public async Task UpdateMyFullName([FromBody] SetUserFullNameCommand command) + public async Task> UpdateMyFullName([FromBody] SetUserFullNameCommand command) { return Ok(await mediator.Send(command)); } + [Authorize] + [HttpGet("roles")] + public async Task> GetAllRoles() + { + var command = new GetAllRolesCommand(); + return Ok(await mediator.Send(command)); + } + [Authorize(Roles = "Admin")] [HttpPut("{userId}/roles/{roleId:guid}")] - public async Task AddUserRole(string userId, Guid roleId) + public async Task> AddUserRole(string userId, Guid roleId) { var command = new SetUserRoleCommand(userId, roleId); return Ok(await mediator.Send(command)); @@ -62,7 +63,7 @@ public class UsersController(IMediator mediator) : ControllerBase [Authorize(Roles = "Admin")] [HttpDelete("{userId}/roles/{roleId:guid}")] - public async Task RemoveUserRole(string userId, Guid roleId) + public async Task> RemoveUserRole(string userId, Guid roleId) { var command = new DeleteUserRoleCommand(userId, roleId); return Ok(await mediator.Send(command)); diff --git a/src/Imprink.WebApi/Middleware/ExceptionHandlingMiddleware.cs b/src/Imprink.WebApi/Middleware/ExceptionHandlingMiddleware.cs index eb70eab..9e88443 100644 --- a/src/Imprink.WebApi/Middleware/ExceptionHandlingMiddleware.cs +++ b/src/Imprink.WebApi/Middleware/ExceptionHandlingMiddleware.cs @@ -61,6 +61,7 @@ public class ExceptionHandlingMiddleware( return exception switch { NotFoundException => (HttpStatusCode.NotFound, exception.Message, false), + DataUpdateException => (HttpStatusCode.Conflict, exception.Message, false), _ => (HttpStatusCode.InternalServerError, "An internal server error occurred", true) }; } diff --git a/src/Imprink.WebApi/Program.cs b/src/Imprink.WebApi/Program.cs index 3992d18..120a9d8 100644 --- a/src/Imprink.WebApi/Program.cs +++ b/src/Imprink.WebApi/Program.cs @@ -13,5 +13,3 @@ var app = builder.Build(); Startup.Configure(app, app.Environment); app.Run(); - -public partial class Program { } \ No newline at end of file diff --git a/src/Imprink.WebApi/Startup.cs b/src/Imprink.WebApi/Startup.cs index 0f7403d..eb5ead3 100644 --- a/src/Imprink.WebApi/Startup.cs +++ b/src/Imprink.WebApi/Startup.cs @@ -2,6 +2,7 @@ using System.Security.Claims; using FluentValidation; using Imprink.Application; using Imprink.Application.Domains.Products; +using Imprink.Application.Mappings; using Imprink.Application.Products; using Imprink.Application.Services; using Imprink.Application.Validation.Models; @@ -39,8 +40,9 @@ public static class Startup services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); + + services.AddAutoMapper(typeof(MappingProfile).Assembly); services.AddHttpContextAccessor(); diff --git a/webui/src/app/page.js b/webui/src/app/page.js index c2d8331..141ace0 100644 --- a/webui/src/app/page.js +++ b/webui/src/app/page.js @@ -1,423 +1,192 @@ -'use client' +'use client'; -import {useEffect, useState} from 'react'; -import axios from 'axios'; -import { - Alert, - AppBar, - Badge, - Box, - Button, - Card, - CardActions, - CardContent, - CardMedia, - Chip, - Container, - Fab, - Grid, - IconButton, - Skeleton, - Toolbar, - Typography -} from '@mui/material'; -import {createTheme, ThemeProvider} from '@mui/material/styles'; -import CssBaseline from '@mui/material/CssBaseline'; -import {Menu, Palette, Search, ShoppingCart} from 'lucide-react'; +import { useUser } from "@auth0/nextjs-auth0"; +import {useEffect, useState} from "react"; -const theme = createTheme({ - palette: { - mode: 'dark', - primary: { - main: '#D0BCFF', - light: '#EADDFF', - dark: '#9A82DB', - contrastText: '#21005D' - }, - secondary: { - main: '#CCC2DC', - light: '#E8DEF8', - dark: '#A8A2BA', - contrastText: '#332D41' - }, - background: { - default: '#101418', - paper: '#1D1B20' - }, - surface: { - main: '#1D1B20', - variant: '#49454F' - }, - error: { - main: '#F2B8B5', - light: '#FFDAD6', - dark: '#BA1A1A', - contrastText: '#410002' - }, - success: { - main: '#A6D4A3', - light: '#C4F0B8', - dark: '#52B788' - }, - text: { - primary: '#E6E0E9', - secondary: '#CAC4D0' - } - }, - shape: { - borderRadius: 16 - }, - typography: { - fontFamily: '"Google Sans", "Roboto", "Helvetica", "Arial", sans-serif', - h4: { - fontWeight: 400, - fontSize: '2rem' - }, - h6: { - fontWeight: 500, - fontSize: '1.25rem' - }, - body1: { - fontSize: '1rem', - lineHeight: 1.5 - }, - body2: { - fontSize: '0.875rem', - lineHeight: 1.43 - } - }, - components: { - MuiCard: { - styleOverrides: { - root: { - backgroundImage: 'none', - backgroundColor: '#1D1B20', - border: '1px solid #49454F', - transition: 'all 0.3s ease', - '&:hover': { - transform: 'translateY(-4px)', - borderColor: '#D0BCFF', - boxShadow: '0 8px 32px rgba(208, 188, 255, 0.1)' - } - } - } - }, - MuiButton: { - styleOverrides: { - root: { - textTransform: 'none', - fontWeight: 500, - borderRadius: 20, - paddingLeft: 24, - paddingRight: 24, - height: 40 - } - } - }, - MuiTextField: { - styleOverrides: { - root: { - '& .MuiOutlinedInput-root': { - borderRadius: 12, - backgroundColor: '#1D1B20', - '& fieldset': { - borderColor: '#49454F' - }, - '&:hover fieldset': { - borderColor: '#CAC4D0' - } - } - } - } - }, - MuiChip: { - styleOverrides: { - root: { - borderRadius: 8 - } - } - } - } -}); - -const ProductCard = ({ product }) => { - const [imageLoaded, setImageLoaded] = useState(false); - - return ( - - - {!imageLoaded && ( - - )} - setImageLoaded(true)} - sx={{ - display: imageLoaded ? 'block' : 'none', - objectFit: 'cover', - transition: 'transform 0.3s ease', - '&:hover': { - transform: 'scale(1.05)' - } - }} - /> - - {product.isCustomizable && ( - } - label="Customizable" - size="small" - color="primary" - sx={{ fontSize: '0.75rem' }} - /> - )} - - - - - - - {product.name} - - - {product.description} - - - ${product.basePrice.toFixed(2)} - - - - - - - - ); -}; - -const LoadingSkeleton = () => ( - - {[...Array(8)].map((_, index) => ( - - - - - - - - - - - - ))} - -); - -const ImprintLanding = () => { - const [products, setProducts] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - const [cartCount, setCartCount] = useState(0); +export default function Home() { + const { user, error, isLoading } = useUser(); useEffect(() => { - const fetchProducts = async () => { - try { - setLoading(true); - const response = await axios.get('https://impr.ink/api/products', { - params: { - PageNumber: 1, - PageSize: 20 - } - }); - setProducts(response.data.items); - } catch (err) { - setError('Failed to load products. Please try again later.'); - console.error('Error fetching products:', err); - } finally { - setLoading(false); + const fetchAccessToken = async () => { + if (user) { + try { + await fetch('/token'); + } catch (error) { + console.error("Error fetching token"); + } + } else { + try { + await fetch('/untoken'); + } catch (e) { + console.error('Error in /api/untoken:', e); + } } }; - fetchProducts().then(r => console.log(r)); - }, []); + fetchAccessToken().then(r => console.log(r)); + }, [user]); + + if (isLoading) { + return ( +
+
+
+
+
+
+
+
+ ); + } + + if (error) { + return ( + + ); + } - const featuredProducts = products.slice(0, 4); return ( - - - - {/* App Bar */} - - - - - - - impr.ink - - - - - - - - - - - +
+
+ {user ? ( +
+
+
+ {user.picture ? ( + Profile + ) : ( +
+ {user.name?.charAt(0) || user.email?.charAt(0) || '👤'} +
+ )} +
+

+ Just testing :P +

+
- {/* Hero Section */} - - - - - Custom Printing Made Beautiful - - - High-quality custom printing solutions for caps, flyers, coasters, and more. - Premium materials with excellent print adhesion and longevity. - -
+ + + + Check + + + - Load More Products - - - )} - - - {/* Floating Action Button */} - - - - - - - +
+ + Sign Out + +
+
+
+ ) : ( +
+ )} + + ); -}; - -export default ImprintLanding; \ No newline at end of file +} \ No newline at end of file