From 8794ec1a28b362900da8f0639745e131ada89db4 Mon Sep 17 00:00:00 2001 From: "Jorge.cool" Date: Wed, 4 Dec 2024 23:18:52 -0400 Subject: [PATCH 01/18] feat: basic configuration of api --- .../Configuration/ConfigDb.cs | 17 ++++++ .../Configuration/ConfigRepositories.cs | 13 +++++ .../Controllers/ProductReviewController.cs | 24 ++++++++ .../ReviewService.Api/Program.cs | 56 +++++++++++++++++++ .../Properties/launchSettings.json | 41 ++++++++++++++ .../ReviewService.Api.csproj | 32 +++++++++++ .../ReviewService.Api/appsettings.json | 11 ++++ .../ReviewService.Api/validationSettings.json | 9 +++ .../Handlers/CreateProductReviewHandler.cs | 29 ++++++++++ .../Commands/Requests/CreateProductReview.cs | 10 ++++ .../Handlers/GetProductReviewByIdHandler.cs | 13 +++++ .../Queries/Requests/GetProductReviewById.cs | 9 +++ .../ProductReview/CreateProductReviewDto.cs | 9 +++ .../Dtos/ProductReview/ProductReviewDto.cs | 8 +++ .../Dtos/Review/CreateReviewDto.cs | 9 +++ .../Profiles/ReviewServiceProfile.cs | 17 ++++++ .../ReviewService.Application/Program.cs | 3 + .../ReviewService.Application.csproj | 31 ++++++++++ .../ProductReviewSettings.cs | 6 ++ .../ValidationSettings/ReviewSettings.cs | 11 ++++ .../ValidationSettings/ValidationSettings.cs | 6 ++ .../CreateProductReviewDtoValidator.cs | 16 ++++++ .../Review/CreateReviewDtoValidator.cs | 25 +++++++++ .../Validators/Review/ReviewValidator.cs | 24 ++++++++ .../ReviewService.Domain/Bases/BaseEntity.cs | 14 +++++ .../Bases/BaseRegister.cs | 16 ++++++ .../ReviewService.Domain/Class1.cs | 5 ++ .../Concretes/ProductReview.cs | 13 +++++ .../ReviewService.Domain/Concretes/Review.cs | 24 ++++++++ .../Interfaces/IEntity.cs | 7 +++ .../Interfaces/IRegister.cs | 8 +++ .../ReviewService.Domain.csproj | 14 +++++ .../Context/ReviewContextMongoDb.cs | 27 +++++++++ .../Maps/ProductReviewMap.cs | 30 ++++++++++ .../ReviewService.Infrastructure/Program.cs | 3 + .../Repositories/Bases/BaseRepository.cs | 53 ++++++++++++++++++ .../Concretes/ProductReviewRepository.cs | 11 ++++ .../Repositories/Interfaces/IRepository.cs | 13 +++++ .../ReviewService.Infrastructure.csproj | 19 +++++++ .../ReviewService/ReviewService.sln | 34 +++++++++++ apps/merchant-api/docker-compose.yml | 15 +++++ .../src/components/RatingSelector.tsx | 18 ++++-- .../components/contact-us/ContactUsForm.tsx | 14 +---- .../product-detail/ProductDetail.tsx | 3 +- .../src/components/reviews/ReviewModal.tsx | 56 +++++++++++++++++++ .../src/schemes/reviews/ReviewFormSchema.ts | 16 ++++++ apps/merchant-web/src/utils/ZodFunctions.ts | 11 ++++ 47 files changed, 836 insertions(+), 17 deletions(-) create mode 100644 apps/merchant-api/ReviewService/ReviewService.Api/Configuration/ConfigDb.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Api/Configuration/ConfigRepositories.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Api/Controllers/ProductReviewController.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Api/Program.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Api/Properties/launchSettings.json create mode 100644 apps/merchant-api/ReviewService/ReviewService.Api/ReviewService.Api.csproj create mode 100644 apps/merchant-api/ReviewService/ReviewService.Api/appsettings.json create mode 100644 apps/merchant-api/ReviewService/ReviewService.Api/validationSettings.json create mode 100644 apps/merchant-api/ReviewService/ReviewService.Application/CommandsQueries/ProductReview/Commands/Handlers/CreateProductReviewHandler.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Application/CommandsQueries/ProductReview/Commands/Requests/CreateProductReview.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Application/CommandsQueries/ProductReview/Queries/Handlers/GetProductReviewByIdHandler.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Application/CommandsQueries/ProductReview/Queries/Requests/GetProductReviewById.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Application/Dtos/ProductReview/CreateProductReviewDto.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Application/Dtos/ProductReview/ProductReviewDto.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Application/Dtos/Review/CreateReviewDto.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Application/Profiles/ReviewServiceProfile.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Application/Program.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Application/ReviewService.Application.csproj create mode 100644 apps/merchant-api/ReviewService/ReviewService.Application/ValidationSettings/ProductReviewSettings.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Application/ValidationSettings/ReviewSettings.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Application/ValidationSettings/ValidationSettings.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Application/Validators/ProductReview/CreateProductReviewDtoValidator.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Application/Validators/Review/CreateReviewDtoValidator.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Application/Validators/Review/ReviewValidator.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Domain/Bases/BaseEntity.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Domain/Bases/BaseRegister.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Domain/Class1.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Domain/Concretes/ProductReview.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Domain/Concretes/Review.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Domain/Interfaces/IEntity.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Domain/Interfaces/IRegister.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Domain/ReviewService.Domain.csproj create mode 100644 apps/merchant-api/ReviewService/ReviewService.Infrastructure/Context/ReviewContextMongoDb.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Infrastructure/Maps/ProductReviewMap.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Infrastructure/Program.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Infrastructure/Repositories/Bases/BaseRepository.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Infrastructure/Repositories/Concretes/ProductReviewRepository.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Infrastructure/Repositories/Interfaces/IRepository.cs create mode 100644 apps/merchant-api/ReviewService/ReviewService.Infrastructure/ReviewService.Infrastructure.csproj create mode 100644 apps/merchant-api/ReviewService/ReviewService.sln create mode 100644 apps/merchant-web/src/components/reviews/ReviewModal.tsx create mode 100644 apps/merchant-web/src/schemes/reviews/ReviewFormSchema.ts create mode 100644 apps/merchant-web/src/utils/ZodFunctions.ts diff --git a/apps/merchant-api/ReviewService/ReviewService.Api/Configuration/ConfigDb.cs b/apps/merchant-api/ReviewService/ReviewService.Api/Configuration/ConfigDb.cs new file mode 100644 index 00000000..7348fae7 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Api/Configuration/ConfigDb.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore; +using MongoDB.Driver; +using ReviewService.Concretes; +using ReviewService.Infrastructure.Context; + +namespace ReviewService.Api.Configuration; + +public static class ConfigDb +{ + public static IServiceCollection ConfigDbSet(this IServiceCollection services, string connectionString) + { + var client = new MongoClient(connectionString); + var db = ReviewContextMongoDb.Create(client.GetDatabase("reviews_db")); + services.AddScoped>(_ => db.Reviews); + return services; + } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Api/Configuration/ConfigRepositories.cs b/apps/merchant-api/ReviewService/ReviewService.Api/Configuration/ConfigRepositories.cs new file mode 100644 index 00000000..4f07f368 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Api/Configuration/ConfigRepositories.cs @@ -0,0 +1,13 @@ +using ReviewService.Concretes; +using ReviewService.Infrastructure.Repositories.Concretes; +using ReviewService.Infrastructure.Repositories.Interfaces; + +namespace ReviewService.Api.Configuration; + +public static class ConfigRepositories +{ + public static IServiceCollection AddRepositories(this IServiceCollection services) + { + return services.AddScoped, ProductReviewRepository>(); + } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Api/Controllers/ProductReviewController.cs b/apps/merchant-api/ReviewService/ReviewService.Api/Controllers/ProductReviewController.cs new file mode 100644 index 00000000..6ec64801 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Api/Controllers/ProductReviewController.cs @@ -0,0 +1,24 @@ +using Commons.ResponseHandler.Responses.Concretes; + +using MediatR; +using Microsoft.AspNetCore.Mvc; +using ReviewService.Application.CommandsQueries.ProductReview.Commands.Requests; +using ReviewService.Application.Dtos.ProductReview; +using ReviewService.Concretes; + +namespace ReviewService.Api.Controllers; + +[ApiController] +[Route("api/review/[controller]")] +public class ProductReviewController(IMediator mediator) : ControllerBase +{ + [HttpPost] + public async Task Create(CreateProductReviewDto productReview) + { + var result = await mediator.Send(new CreateProductReview(productReview)); + if (result is ErrorResponse errorResponse) + return StatusCode(errorResponse.StatusCode, errorResponse); + var successResponse = (SuccessResponse)result; + return StatusCode(successResponse.StatusCode, successResponse.Data); + } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Api/Program.cs b/apps/merchant-api/ReviewService/ReviewService.Api/Program.cs new file mode 100644 index 00000000..c2684238 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Api/Program.cs @@ -0,0 +1,56 @@ +using Commons.ResponseHandler.Handler.Concretes; +using Commons.ResponseHandler.Handler.Interfaces; +using DotNetEnv; +using FluentValidation; +using ReviewService.Api.Configuration; +using ReviewService.Application.Profiles; +using ReviewService.Application.ValidationSettings; + +Env.Load("../../../.env"); +var builder = WebApplication.CreateBuilder(args); +builder.Configuration.AddEnvironmentVariables(); + +builder.Configuration.AddJsonFile("validationSettings.json", optional: false, reloadOnChange: true); +builder.Services.Configure(builder.Configuration); + +var apiGateway = builder.Configuration["ApiGatewayUrl"] ?? "http://localhost:5001"; +builder.Services.AddCors(options => +{ + options.AddPolicy("AllowedLocalhost", + policyBuilder => policyBuilder + .WithOrigins(apiGateway) + .AllowAnyHeader() + .AllowAnyMethod()); +}); + +var connectionString = builder.Configuration["MONGODB_URI"]; +if (connectionString == null) +{ + Console.WriteLine("You must set your 'MONGODB_URI' environment variable. To learn how to set it, see https://www.mongodb.com/docs/drivers/csharp/current/quick-start/#set-your-connection-string"); + Environment.Exit(0); +} + +builder.Services.AddControllers(); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +builder.Services.AddRepositories(); +builder.Services.ConfigDbSet(connectionString); +builder.Services.AddValidatorsFromAssembly(typeof(ReviewServiceProfile).Assembly); +builder.Services.AddAutoMapper(typeof(ReviewServiceProfile)); +builder.Services.AddMediatR(cfg => + cfg.RegisterServicesFromAssemblies(typeof(ReviewServiceProfile).Assembly)); +builder.Services.AddScoped(); + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.MapControllers(); +app.UseCors("AllowApiGateway"); +app.UseHttpsRedirection(); +app.Run(); \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Api/Properties/launchSettings.json b/apps/merchant-api/ReviewService/ReviewService.Api/Properties/launchSettings.json new file mode 100644 index 00000000..a1d823f5 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Api/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:12039", + "sslPort": 44349 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5039", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7269;http://localhost:5039", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/apps/merchant-api/ReviewService/ReviewService.Api/ReviewService.Api.csproj b/apps/merchant-api/ReviewService/ReviewService.Api/ReviewService.Api.csproj new file mode 100644 index 00000000..4ee43847 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Api/ReviewService.Api.csproj @@ -0,0 +1,32 @@ + + + + net8.0 + enable + enable + ReviewService.Api + + + + + + + + + + + + + + + + + + + + + ..\..\MerchantCommon\Commons\bin\Debug\net8.0\Commons.dll + + + + diff --git a/apps/merchant-api/ReviewService/ReviewService.Api/appsettings.json b/apps/merchant-api/ReviewService/ReviewService.Api/appsettings.json new file mode 100644 index 00000000..5984f2e5 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Api/appsettings.json @@ -0,0 +1,11 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ApiGatewayUrl": "http://localhost:5001", + "MONGODB_URI": "mongodb://localhost:27017/" +} diff --git a/apps/merchant-api/ReviewService/ReviewService.Api/validationSettings.json b/apps/merchant-api/ReviewService/ReviewService.Api/validationSettings.json new file mode 100644 index 00000000..cc652315 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Api/validationSettings.json @@ -0,0 +1,9 @@ +{ + "Review": { + "MinLengthName": 3, + "MaxLengthName": 24, + "MaxLengthComment": 500, + "MinRatingRange": 0, + "MaxRatingRange": 5 + } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Application/CommandsQueries/ProductReview/Commands/Handlers/CreateProductReviewHandler.cs b/apps/merchant-api/ReviewService/ReviewService.Application/CommandsQueries/ProductReview/Commands/Handlers/CreateProductReviewHandler.cs new file mode 100644 index 00000000..4d962cd9 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Application/CommandsQueries/ProductReview/Commands/Handlers/CreateProductReviewHandler.cs @@ -0,0 +1,29 @@ +using AutoMapper; +using Commons.ResponseHandler.Handler.Concretes; +using Commons.ResponseHandler.Handler.Interfaces; +using Commons.ResponseHandler.Responses.Bases; +using FluentValidation; +using MediatR; +using ReviewService.Application.CommandsQueries.ProductReview.Commands.Requests; +using ReviewService.Application.Dtos.ProductReview; +using ReviewService.Infrastructure.Repositories.Interfaces; + +namespace ReviewService.Application.CommandsQueries.ProductReview.Commands.Handlers; + +public class CreateProductReviewHandler( + IRepository repository, + IValidator validator, + IResponseHandlingHelper responseHandlingHelper, + IMapper mapper) + : IRequestHandler +{ + public async Task Handle(CreateProductReview request, CancellationToken cancellationToken) + { + var result = await validator.ValidateAsync(request.ProductReview, cancellationToken); + if (!result.IsValid) return responseHandlingHelper.BadRequest("Validations failed"); + + var productReview = mapper.Map(request.ProductReview); + await repository.AddAsync(productReview); + return responseHandlingHelper.Created("Product review successfully added", productReview); + } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Application/CommandsQueries/ProductReview/Commands/Requests/CreateProductReview.cs b/apps/merchant-api/ReviewService/ReviewService.Application/CommandsQueries/ProductReview/Commands/Requests/CreateProductReview.cs new file mode 100644 index 00000000..e106425b --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Application/CommandsQueries/ProductReview/Commands/Requests/CreateProductReview.cs @@ -0,0 +1,10 @@ +using Commons.ResponseHandler.Responses.Bases; +using MediatR; +using ReviewService.Application.Dtos.ProductReview; + +namespace ReviewService.Application.CommandsQueries.ProductReview.Commands.Requests; + +public class CreateProductReview(CreateProductReviewDto productReview) : IRequest +{ + public CreateProductReviewDto ProductReview { get; set; } = productReview; +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Application/CommandsQueries/ProductReview/Queries/Handlers/GetProductReviewByIdHandler.cs b/apps/merchant-api/ReviewService/ReviewService.Application/CommandsQueries/ProductReview/Queries/Handlers/GetProductReviewByIdHandler.cs new file mode 100644 index 00000000..f70133cd --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Application/CommandsQueries/ProductReview/Queries/Handlers/GetProductReviewByIdHandler.cs @@ -0,0 +1,13 @@ +using Commons.ResponseHandler.Responses.Bases; +using MediatR; +using ReviewService.Application.Dtos.ProductReview; + +namespace ReviewService.Application.Queries.Handlers; + +public class GetProductReviewByIdHandler : IRequestHandler +{ + public Task Handle(GetProductReviewById request, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Application/CommandsQueries/ProductReview/Queries/Requests/GetProductReviewById.cs b/apps/merchant-api/ReviewService/ReviewService.Application/CommandsQueries/ProductReview/Queries/Requests/GetProductReviewById.cs new file mode 100644 index 00000000..c77f5716 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Application/CommandsQueries/ProductReview/Queries/Requests/GetProductReviewById.cs @@ -0,0 +1,9 @@ +using Commons.ResponseHandler.Responses.Bases; +using MediatR; + +namespace ReviewService.Application.Queries; + +public class GetProductReviewById : IRequest +{ + public Guid ProductId { get; set; } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Application/Dtos/ProductReview/CreateProductReviewDto.cs b/apps/merchant-api/ReviewService/ReviewService.Application/Dtos/ProductReview/CreateProductReviewDto.cs new file mode 100644 index 00000000..482e1652 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Application/Dtos/ProductReview/CreateProductReviewDto.cs @@ -0,0 +1,9 @@ +using ReviewService.Application.Dtos.Review; + +namespace ReviewService.Application.Dtos.ProductReview; + +public class CreateProductReviewDto +{ + public required Guid ProductId { get; set; } + public required List Reviews { get; set; } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Application/Dtos/ProductReview/ProductReviewDto.cs b/apps/merchant-api/ReviewService/ReviewService.Application/Dtos/ProductReview/ProductReviewDto.cs new file mode 100644 index 00000000..51f6d075 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Application/Dtos/ProductReview/ProductReviewDto.cs @@ -0,0 +1,8 @@ +using ReviewService.Concretes; + +namespace ReviewService.Application.Dtos.ProductReview; + +public class ProductReviewDto +{ + public required List Reviews { get; set; } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Application/Dtos/Review/CreateReviewDto.cs b/apps/merchant-api/ReviewService/ReviewService.Application/Dtos/Review/CreateReviewDto.cs new file mode 100644 index 00000000..b4e908d6 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Application/Dtos/Review/CreateReviewDto.cs @@ -0,0 +1,9 @@ +namespace ReviewService.Application.Dtos.Review; + +public class CreateReviewDto +{ + public required Guid ClientId { get; set; } + public required string ClientName { get; set; } + public required uint Rating { get; set; } + public string? Comment { get; set; } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Application/Profiles/ReviewServiceProfile.cs b/apps/merchant-api/ReviewService/ReviewService.Application/Profiles/ReviewServiceProfile.cs new file mode 100644 index 00000000..f83ef978 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Application/Profiles/ReviewServiceProfile.cs @@ -0,0 +1,17 @@ +using AutoMapper; +using ReviewService.Application.CommandsQueries.ProductReview.Commands.Requests; +using ReviewService.Application.Dtos.ProductReview; +using ReviewService.Application.Dtos.Review; +using ReviewService.Concretes; + +namespace ReviewService.Application.Profiles; + +public class ReviewServiceProfile : Profile +{ + public ReviewServiceProfile() + { + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Application/Program.cs b/apps/merchant-api/ReviewService/ReviewService.Application/Program.cs new file mode 100644 index 00000000..e5dff12b --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Application/Program.cs @@ -0,0 +1,3 @@ +// See https://aka.ms/new-console-template for more information + +Console.WriteLine("Hello, World!"); \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Application/ReviewService.Application.csproj b/apps/merchant-api/ReviewService/ReviewService.Application/ReviewService.Application.csproj new file mode 100644 index 00000000..b756e3b8 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Application/ReviewService.Application.csproj @@ -0,0 +1,31 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + + ..\..\MerchantCommon\Commons\bin\Debug\net8.0\Commons.dll + + + + + + + + + + + + + diff --git a/apps/merchant-api/ReviewService/ReviewService.Application/ValidationSettings/ProductReviewSettings.cs b/apps/merchant-api/ReviewService/ReviewService.Application/ValidationSettings/ProductReviewSettings.cs new file mode 100644 index 00000000..816fa938 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Application/ValidationSettings/ProductReviewSettings.cs @@ -0,0 +1,6 @@ +namespace ReviewService.Application.ValidationSettings; + +public class ProductReviewSettings +{ + +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Application/ValidationSettings/ReviewSettings.cs b/apps/merchant-api/ReviewService/ReviewService.Application/ValidationSettings/ReviewSettings.cs new file mode 100644 index 00000000..802548b4 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Application/ValidationSettings/ReviewSettings.cs @@ -0,0 +1,11 @@ +namespace ReviewService.Application.ValidationSettings; + +public class ReviewSettings +{ + public int MinLengthName { get; set; } + public int MaxLengthName { get; set; } + + public int MaxLengthComment { get; set; } + public int MinRatingRange { get; set; } + public int MaxRatingRange { get; set; } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Application/ValidationSettings/ValidationSettings.cs b/apps/merchant-api/ReviewService/ReviewService.Application/ValidationSettings/ValidationSettings.cs new file mode 100644 index 00000000..570302be --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Application/ValidationSettings/ValidationSettings.cs @@ -0,0 +1,6 @@ +namespace ReviewService.Application.ValidationSettings; + +public class ValidationSettings +{ + public ReviewSettings Review { get; set; } = new(); +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Application/Validators/ProductReview/CreateProductReviewDtoValidator.cs b/apps/merchant-api/ReviewService/ReviewService.Application/Validators/ProductReview/CreateProductReviewDtoValidator.cs new file mode 100644 index 00000000..d2dd6579 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Application/Validators/ProductReview/CreateProductReviewDtoValidator.cs @@ -0,0 +1,16 @@ +using FluentValidation; +using Microsoft.Extensions.Options; +using ReviewService.Application.Dtos.ProductReview; +using ReviewService.Application.Validators.Review; + +namespace ReviewService.Application.Validators.ProductReview; + +public class CreateProductReviewDtoValidator : AbstractValidator +{ + public CreateProductReviewDtoValidator(IOptions validationSettings) + { + RuleFor(e => e.ProductId).NotNull(); + RuleForEach(e => e.Reviews) + .SetValidator(new CreateReviewDtoValidator(validationSettings)); + } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Application/Validators/Review/CreateReviewDtoValidator.cs b/apps/merchant-api/ReviewService/ReviewService.Application/Validators/Review/CreateReviewDtoValidator.cs new file mode 100644 index 00000000..8d5b04a7 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Application/Validators/Review/CreateReviewDtoValidator.cs @@ -0,0 +1,25 @@ +using FluentValidation; +using Microsoft.Extensions.Options; +using ReviewService.Application.Dtos.Review; + +namespace ReviewService.Application.Validators.Review; + +public class CreateReviewDtoValidator : AbstractValidator +{ + public CreateReviewDtoValidator(IOptions validationSettings) + { + var reviewSettings = validationSettings.Value.Review; + RuleFor(r => r.ClientId).NotNull(); + RuleFor(r => r.ClientName) + .NotNull() + .MinimumLength(reviewSettings.MinLengthName) + .MaximumLength(reviewSettings.MaxLengthName); + RuleFor(r => r.Comment) + .NotEmpty() + .MaximumLength(reviewSettings.MaxLengthComment); + RuleFor(r => r.Rating) + .NotNull() + .Must(r => r <= reviewSettings.MaxRatingRange).WithMessage($"The rating should be smaller than {reviewSettings.MaxRatingRange}") + .Must(r => r >= reviewSettings.MinRatingRange).WithMessage($"The rating should be bigger than {reviewSettings.MinRatingRange}"); + } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Application/Validators/Review/ReviewValidator.cs b/apps/merchant-api/ReviewService/ReviewService.Application/Validators/Review/ReviewValidator.cs new file mode 100644 index 00000000..59ae0c2a --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Application/Validators/Review/ReviewValidator.cs @@ -0,0 +1,24 @@ +using FluentValidation; +using Microsoft.Extensions.Options; + +namespace ReviewService.Application.Validators.Review; + +public class ReviewValidator : AbstractValidator +{ + public ReviewValidator(IOptions validationSettings) + { + var reviewSettings = validationSettings.Value.Review; + RuleFor(r => r.ClientId).NotNull(); + RuleFor(r => r.ClientName) + .NotNull() + .MinimumLength(reviewSettings.MinLengthName) + .MaximumLength(reviewSettings.MaxLengthName); + RuleFor(r => r.Comment) + .NotEmpty() + .MaximumLength(reviewSettings.MaxLengthComment); + RuleFor(r => r.Rating) + .NotNull() + .Must(r => r <= reviewSettings.MaxRatingRange).WithMessage($"The rating should be smaller than {reviewSettings.MaxRatingRange}") + .Must(r => r >= reviewSettings.MinRatingRange).WithMessage($"The rating should be bigger than {reviewSettings.MinRatingRange}"); + } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Domain/Bases/BaseEntity.cs b/apps/merchant-api/ReviewService/ReviewService.Domain/Bases/BaseEntity.cs new file mode 100644 index 00000000..f0bee029 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Domain/Bases/BaseEntity.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; +using MongoDB.Bson.Serialization.Attributes; +using ReviewService.Interfaces; + +namespace ReviewService.Bases; + +public class BaseEntity : BaseRegister, IEntity +{ + [BsonId, Key] + public Guid Id { get; } = Guid.NewGuid(); + + [BsonElement("is_active")] + public bool IsActive { get; set; } = true; +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Domain/Bases/BaseRegister.cs b/apps/merchant-api/ReviewService/ReviewService.Domain/Bases/BaseRegister.cs new file mode 100644 index 00000000..b671ba3d --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Domain/Bases/BaseRegister.cs @@ -0,0 +1,16 @@ +using MongoDB.Bson.Serialization.Attributes; +using ReviewService.Interfaces; + +namespace ReviewService.Bases; + +public class BaseRegister : IRegister +{ + [BsonElement("created_at")] + public DateTime CreatedAt { get; } = DateTime.Now; + + [BsonElement("updated_at")] + public DateTime? UpdatedAt { get; set; } + + [BsonElement("deleted_at")] + public DateTime? DeletedAt { get; set; } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Domain/Class1.cs b/apps/merchant-api/ReviewService/ReviewService.Domain/Class1.cs new file mode 100644 index 00000000..e3238f8d --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Domain/Class1.cs @@ -0,0 +1,5 @@ +namespace ReviewService; + +public class Class1 +{ +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Domain/Concretes/ProductReview.cs b/apps/merchant-api/ReviewService/ReviewService.Domain/Concretes/ProductReview.cs new file mode 100644 index 00000000..99c963d7 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Domain/Concretes/ProductReview.cs @@ -0,0 +1,13 @@ +using MongoDB.Bson.Serialization.Attributes; +using ReviewService.Bases; + +namespace ReviewService.Concretes; + +public class ProductReview : BaseEntity +{ + [BsonElement("product_id")] + public required Guid ProductId { get; set; } + + [BsonElement("reviews")] + public required List Reviews { get; set; } = new(); +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Domain/Concretes/Review.cs b/apps/merchant-api/ReviewService/ReviewService.Domain/Concretes/Review.cs new file mode 100644 index 00000000..ed3311de --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Domain/Concretes/Review.cs @@ -0,0 +1,24 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.EntityFrameworkCore; +using MongoDB.Bson.Serialization.Attributes; +using ReviewService.Bases; + +namespace ReviewService.Concretes; + +public class Review +{ + [Key] + public Guid Id { get; set; } + + [BsonElement("client_id")] + public required Guid ClientId { get; set; } + + [BsonElement("client_name")] + public required string ClientName { get; set; } + + [BsonElement("rating")] + public required int Rating { get; set; } + + [BsonElement("comment")] + public string? Comment { get; set; } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Domain/Interfaces/IEntity.cs b/apps/merchant-api/ReviewService/ReviewService.Domain/Interfaces/IEntity.cs new file mode 100644 index 00000000..e8028fb3 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Domain/Interfaces/IEntity.cs @@ -0,0 +1,7 @@ +namespace ReviewService.Interfaces; + +public interface IEntity : IRegister +{ + Guid Id { get; } + bool IsActive { get; set; } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Domain/Interfaces/IRegister.cs b/apps/merchant-api/ReviewService/ReviewService.Domain/Interfaces/IRegister.cs new file mode 100644 index 00000000..ddbe4ee7 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Domain/Interfaces/IRegister.cs @@ -0,0 +1,8 @@ +namespace ReviewService.Interfaces; + +public interface IRegister +{ + DateTime CreatedAt { get; } + DateTime? UpdatedAt { get; set; } + DateTime? DeletedAt { get; set; } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Domain/ReviewService.Domain.csproj b/apps/merchant-api/ReviewService/ReviewService.Domain/ReviewService.Domain.csproj new file mode 100644 index 00000000..3861ce9b --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Domain/ReviewService.Domain.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + ReviewService + + + + + + + diff --git a/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Context/ReviewContextMongoDb.cs b/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Context/ReviewContextMongoDb.cs new file mode 100644 index 00000000..09b51d69 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Context/ReviewContextMongoDb.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore; +using MongoDB.Driver; +using ReviewService.Concretes; +using ReviewService.Infrastructure.Maps; + +namespace ReviewService.Infrastructure.Context; + +public class ReviewContextMongoDb : DbContext +{ + public DbSet Reviews { get; init; } + + public static ReviewContextMongoDb Create(IMongoDatabase database) => + new(new DbContextOptionsBuilder() + .UseMongoDB(database.Client, database.DatabaseNamespace.DatabaseName) + .Options); + public ReviewContextMongoDb(DbContextOptions options) + : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.ApplyConfiguration(new ProductReviewMap()); + } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Maps/ProductReviewMap.cs b/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Maps/ProductReviewMap.cs new file mode 100644 index 00000000..65496a5b --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Maps/ProductReviewMap.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using MongoDB.EntityFrameworkCore.Extensions; +using ReviewService.Concretes; + +namespace ReviewService.Infrastructure.Maps; + +public class ProductReviewMap : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToCollection("ProductReview"); + + builder.HasKey(pr => pr.Id); + builder.Property(pr => pr.Id) + .HasElementName("_id") + .ValueGeneratedOnAdd(); + + builder.Property(pr => pr.ProductId).IsRequired(); + builder.Property(pr => pr.IsActive); + builder.Property(pr => pr.CreatedAt); + builder.Property(pr => pr.DeletedAt); + builder.Property(pr => pr.UpdatedAt); + + builder.HasMany(p => p.Reviews) + .WithOne() + .HasForeignKey("ProductReviewId"); + } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Program.cs b/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Program.cs new file mode 100644 index 00000000..e5dff12b --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Program.cs @@ -0,0 +1,3 @@ +// See https://aka.ms/new-console-template for more information + +Console.WriteLine("Hello, World!"); \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Repositories/Bases/BaseRepository.cs b/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Repositories/Bases/BaseRepository.cs new file mode 100644 index 00000000..6f2e3530 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Repositories/Bases/BaseRepository.cs @@ -0,0 +1,53 @@ +using Microsoft.EntityFrameworkCore; +using ReviewService.Bases; +using ReviewService.Infrastructure.Repositories.Interfaces; + +namespace ReviewService.Infrastructure.Repositories.Bases; + +public class BaseRepository(DbSet dbSet) : IRepository where T : BaseEntity +{ + public async Task GetByIdAsync(Guid id) + { + var entity = await dbSet.FindAsync(id); + return entity is { IsActive: true } ? entity : null; + } + + public ICollection GetAllAsync(int pageNumber, int pageSize) + { + var collection = dbSet + .Where(e => e.IsActive) + .OrderBy(e => e.CreatedAt) + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize).ToList(); + return collection; + } + + public uint CountAsync() + { + var quantity = dbSet.Count(e => e.IsActive); + return (uint)quantity; + } + + public async Task AddAsync(T entity) + { + var response = await dbSet.AddAsync(entity); + return response.Entity; + } + + public async Task Update(T entity) + { + entity.UpdatedAt = DateTime.UtcNow; + var response = dbSet.Update(entity); + return response.Entity; + } + + public async Task DeleteAsync(Guid id) + { + var entity = await GetByIdAsync(id); + if (entity == null) return null; + + entity.DeletedAt = DateTime.UtcNow; + entity.IsActive = false; + return entity; + } +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Repositories/Concretes/ProductReviewRepository.cs b/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Repositories/Concretes/ProductReviewRepository.cs new file mode 100644 index 00000000..b6125646 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Repositories/Concretes/ProductReviewRepository.cs @@ -0,0 +1,11 @@ +using Microsoft.EntityFrameworkCore; +using ReviewService.Concretes; +using ReviewService.Infrastructure.Repositories.Bases; +using ReviewService.Infrastructure.Repositories.Interfaces; + +namespace ReviewService.Infrastructure.Repositories.Concretes; + +public class ProductReviewRepository(DbSet dbSet) : BaseRepository(dbSet) +{ + +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Repositories/Interfaces/IRepository.cs b/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Repositories/Interfaces/IRepository.cs new file mode 100644 index 00000000..1c1bf6c7 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Infrastructure/Repositories/Interfaces/IRepository.cs @@ -0,0 +1,13 @@ +using ReviewService.Interfaces; + +namespace ReviewService.Infrastructure.Repositories.Interfaces; + +public interface IRepository where T : IEntity +{ + Task GetByIdAsync(Guid id); + ICollection GetAllAsync(int pageNumber, int pageSize); + uint CountAsync(); + Task AddAsync(T entity); + Task Update(T entity); + Task DeleteAsync(Guid id); +} \ No newline at end of file diff --git a/apps/merchant-api/ReviewService/ReviewService.Infrastructure/ReviewService.Infrastructure.csproj b/apps/merchant-api/ReviewService/ReviewService.Infrastructure/ReviewService.Infrastructure.csproj new file mode 100644 index 00000000..75866854 --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.Infrastructure/ReviewService.Infrastructure.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/apps/merchant-api/ReviewService/ReviewService.sln b/apps/merchant-api/ReviewService/ReviewService.sln new file mode 100644 index 00000000..8522da9e --- /dev/null +++ b/apps/merchant-api/ReviewService/ReviewService.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReviewService.Domain", "ReviewService.Domain\ReviewService.Domain.csproj", "{2046F6C4-AF4D-4441-96B1-79F90D1C862F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReviewService.Api", "ReviewService.Api\ReviewService.Api.csproj", "{CF10A497-F9F1-4626-9962-091C2C6E4779}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReviewService.Application", "ReviewService.Application\ReviewService.Application.csproj", "{6AB05192-4910-46C4-BB2E-095079CA260E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReviewService.Infrastructure", "ReviewService.Infrastructure\ReviewService.Infrastructure.csproj", "{2B99609F-BB80-4D09-A6E2-757A2EE88BE0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2046F6C4-AF4D-4441-96B1-79F90D1C862F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2046F6C4-AF4D-4441-96B1-79F90D1C862F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2046F6C4-AF4D-4441-96B1-79F90D1C862F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2046F6C4-AF4D-4441-96B1-79F90D1C862F}.Release|Any CPU.Build.0 = Release|Any CPU + {CF10A497-F9F1-4626-9962-091C2C6E4779}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CF10A497-F9F1-4626-9962-091C2C6E4779}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CF10A497-F9F1-4626-9962-091C2C6E4779}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CF10A497-F9F1-4626-9962-091C2C6E4779}.Release|Any CPU.Build.0 = Release|Any CPU + {6AB05192-4910-46C4-BB2E-095079CA260E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6AB05192-4910-46C4-BB2E-095079CA260E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6AB05192-4910-46C4-BB2E-095079CA260E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6AB05192-4910-46C4-BB2E-095079CA260E}.Release|Any CPU.Build.0 = Release|Any CPU + {2B99609F-BB80-4D09-A6E2-757A2EE88BE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2B99609F-BB80-4D09-A6E2-757A2EE88BE0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2B99609F-BB80-4D09-A6E2-757A2EE88BE0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2B99609F-BB80-4D09-A6E2-757A2EE88BE0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/apps/merchant-api/docker-compose.yml b/apps/merchant-api/docker-compose.yml index f34f6c89..b6bff525 100644 --- a/apps/merchant-api/docker-compose.yml +++ b/apps/merchant-api/docker-compose.yml @@ -148,6 +148,19 @@ services: depends_on: rabbitmq: condition: service_healthy + + reviews_db: + image: mongo:8.0.3 + restart: always + ports: + - "${REVIEW_DB_PORT}:${REVIEW_DB_PORT}" + environment: + MONGO_INITDB_ROOT_USERNAME: ${REVIEW_DB_USER} + MONGO_INITDB_ROOT_PASSWORD: ${REVIEW_DB_PASS} + volumes: + - mongodb_data:/data/db + networks: + - merchant-network networks: merchant-network: @@ -156,3 +169,5 @@ networks: volumes: postgres_data: rabbitmq_data: + mongodb_data: + driver: local diff --git a/apps/merchant-web/src/components/RatingSelector.tsx b/apps/merchant-web/src/components/RatingSelector.tsx index 4d8c8fbc..3e4de1f7 100644 --- a/apps/merchant-web/src/components/RatingSelector.tsx +++ b/apps/merchant-web/src/components/RatingSelector.tsx @@ -8,10 +8,11 @@ import { useState } from "react"; interface RatingSelectorProps { rating: number; + handleChange?: () => void; } export default function RatingSelector({ - rating, + rating, handleChange }: Readonly) { const [currentRating, setCurrentRating] = useState(rating); const size = 24; @@ -34,7 +35,10 @@ export default function RatingSelector({ key={index} size={size} color={color} - onClick={() => handleStarClick(index)} + onClick={() => { + handleStarClick(index); + handleChange(); + }} /> ); } else if (index === fullStars && hasHalfStar) { @@ -43,7 +47,10 @@ export default function RatingSelector({ key={index} size={size} color={color} - onClick={() => handleStarClick(index)} + onClick={() => { + handleStarClick(index) + handleChange(); + }} /> ); } else { @@ -52,7 +59,10 @@ export default function RatingSelector({ key={index} size={size} color={color} - onClick={() => handleStarClick(index)} + onClick={() => { + handleStarClick(index) + handleChange(); + }} /> ); } diff --git a/apps/merchant-web/src/components/contact-us/ContactUsForm.tsx b/apps/merchant-web/src/components/contact-us/ContactUsForm.tsx index c92a0b9a..7b96338f 100644 --- a/apps/merchant-web/src/components/contact-us/ContactUsForm.tsx +++ b/apps/merchant-web/src/components/contact-us/ContactUsForm.tsx @@ -8,17 +8,7 @@ import { ContactFormData } from "@/schemes/contact-us-form/ContactUsDto"; import styles from "@/styles/contact-us/ContactUsForm.module.css" import {createContactUsMessage} from "@/request/ContactUsMessageRequest"; import {ContactFormSchema, defaultContactData} from "@/schemes/contact-us-form/ContactFormSchema"; -import { z } from "zod"; - -const validateWithZod = (schema: z.ZodSchema) => (values: any) => { - try { - schema.parse(values); - return {}; - } catch (error) { - const zodErrors = (error as z.ZodError).flatten(); - return zodErrors.fieldErrors; - } -}; +import {validateWithZod} from "@/utils/ZodFunctions"; const ContactForm: React.FC = () => { const formik: FormikProps = useFormik({ @@ -107,4 +97,4 @@ const ContactForm: React.FC = () => { ); }; -export default ContactForm; \ No newline at end of file +export default ContactForm; diff --git a/apps/merchant-web/src/components/product-detail/ProductDetail.tsx b/apps/merchant-web/src/components/product-detail/ProductDetail.tsx index 308623d6..b7399857 100644 --- a/apps/merchant-web/src/components/product-detail/ProductDetail.tsx +++ b/apps/merchant-web/src/components/product-detail/ProductDetail.tsx @@ -13,6 +13,7 @@ import axiosInstance from "@/request/AxiosConfig"; import { useShoppingCart } from "@/contexts/ShoppingCartContext"; import { AttributeSelector } from "./AttributeSelector"; import styles from "@/styles/products/ProductDetails.module.css"; +import ReviewModal from "@/components/reviews/ReviewModal"; export const ProductDetail = () => { const [isFavorite, setIsFavorite] = useState(false); @@ -111,7 +112,7 @@ export const ProductDetail = () => {

{product.name}

- +