-
-
Notifications
You must be signed in to change notification settings - Fork 234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add sales module sections and pages to Boilerplate (#9678) #9706
base: develop
Are you sure you want to change the base?
Add sales module sections and pages to Boilerplate (#9678) #9706
Conversation
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThe pull request introduces comprehensive changes to implement product-related features in the Boilerplate project template. The modifications span multiple files across client and server components, adding support for displaying products on the home page, introducing product image handling, and creating new API endpoints for product retrieval. The changes are specifically tailored for the "Sales" module, with conditional compilation ensuring modular functionality. Changes
Assessment against linked issues
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (14)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Products/Product.cs (1)
27-27
: Add validation and documentation for ImageFileName.Consider adding:
- MaxLength attribute to prevent overly long file names
- Documentation to describe the property's purpose and expected format
+ /// <summary> + /// The file name of the product's image. Null if no image is associated. + /// </summary> + [MaxLength(256)] public string? ImageFileName { get; set; }src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Products/IProductController.cs (2)
33-34
: Add documentation and consider removing default implementation.The method lacks XML documentation describing its purpose and behavior. Also, the default implementation is inconsistent with
GetHomeProducts
.+ /// <summary> + /// Retrieves products to be displayed in the home page carousel. + /// </summary> + /// <param name="cancellationToken">Cancellation token.</param> + /// <returns>List of products for carousel display.</returns> [HttpGet] - Task<List<ProductDto>> GetHomeCarouselProducts(CancellationToken cancellationToken) => default!; + Task<List<ProductDto>> GetHomeCarouselProducts(CancellationToken cancellationToken);
36-37
: Add documentation and parameter validation.The method lacks:
- XML documentation
- Validation attributes for skip/take parameters
+ /// <summary> + /// Retrieves a paginated list of products for the home page. + /// </summary> + /// <param name="skip">Number of items to skip.</param> + /// <param name="take">Number of items to take.</param> + /// <param name="cancellationToken">Cancellation token.</param> + /// <returns>Paginated list of products.</returns> [HttpGet("{skip}/{take}")] + [SwaggerOperation(Summary = "Get paginated products for home page")] Task<List<ProductDto>> GetHomeProducts( + [Range(0, int.MaxValue)] int skip, + [Range(1, 50)] int take, CancellationToken cancellationToken);src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Products/ProductDto.cs (1)
31-31
: Add validation, display name, and documentation for ImageFileName.Consider adding:
- MaxLength attribute to match the entity model
- Display attribute for localization
- Documentation to describe the property
+ /// <summary> + /// The file name of the product's image. + /// </summary> + [MaxLength(256, ErrorMessage = nameof(AppStrings.MaxLengthAttribute_InvalidMaxLength))] + [Display(Name = nameof(AppStrings.ProductImage))] public string? ImageFileName { get; set; }src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor.cs (1)
94-94
: Remove hardcoded delay.The hardcoded delay of 2000ms should be removed unless it serves a specific purpose.
- await Task.Delay(2000);
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Products/ProductController.cs (1)
120-124
: Consider enhancing the carousel products retrieval.The method could benefit from:
- Sorting products by relevance or date
- Filtering active/featured products
- Making the limit configurable
- return await Get().Take(10).ToListAsync(cancellationToken); + return await Get() + .OrderByDescending(p => p.CreatedOn) + .Take(AppSettings.HomeCarouselProductsLimit) + .ToListAsync(cancellationToken);src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs (1)
137-153
: Enhance image handling and error messaging.Consider the following improvements:
- Determine content type based on file extension
- Use consistent error messages
- Optimize file existence check
if (product?.ImageFileName is null) - throw new ResourceNotFoundException(); + throw new ResourceNotFoundException(Localizer[nameof(AppStrings.ProductImageCouldNotBeFound)]); var filePath = $"{AppSettings.ProductImagesDir}{product.ImageFileName}"; + var contentType = Path.GetExtension(product.ImageFileName).ToLower() switch + { + ".webp" => "image/webp", + ".jpg" or ".jpeg" => "image/jpeg", + ".png" => "image/png", + _ => "application/octet-stream" + }; if (await blobStorage.ExistsAsync(filePath, cancellationToken) is false) return new EmptyResult(); - return File(await blobStorage.OpenReadAsync(filePath, cancellationToken), "image/webp", enableRangeProcessing: true); + return File(await blobStorage.OpenReadAsync(filePath, cancellationToken), contentType, enableRangeProcessing: true);src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor (2)
22-41
: Enhance accessibility and user experience of the carousel.Consider the following improvements:
- Add alt text to BitImage for accessibility
- Increase AutoPlayInterval to give users more time to read product details
- Consider adding pause on hover functionality
<BitCard FullWidth> - <BitCarousel Style="height:350px" AutoPlay AutoPlayInterval="5000" HideNextPrev InfiniteScrolling> + <BitCarousel Style="height:350px" AutoPlay AutoPlayInterval="8000" PauseOnHover HideNextPrev InfiniteScrolling> @foreach (var product in products) { <BitCarouselItem> <BitStack Horizontal Class="carousel-stack"> - <BitImage Src="@GetProductImageUrl(product)" Width="50%" /> + <BitImage Src="@GetProductImageUrl(product)" Alt="@product.Name" Width="50%" /> <BitStack AutoHeight> <BitText Typography="BitTypography.H1">@product.Name</BitText> <BitText Typography="BitTypography.H6" Class="carousel-desc">@product.Description</BitText> </BitStack> </BitStack> </BitCarouselItem> } </BitCarousel> </BitCard>
45-62
: Improve accessibility and loading state feedback.Consider the following enhancements:
- Add alt text to BitImage for accessibility
- Add aria-label to the loading section
- Consider adding a loading message for better user feedback
<BitInfiniteScrolling ItemsProvider="LoadProducts" Class="products-inf-scr" ScrollerSelector="body"> <ItemTemplate Context="product"> <BitCard Background="BitColorKind.Tertiary" Class="product-item"> <BitStack Horizontal Class="product-stack"> - <BitImage Src="@GetProductImageUrl(product)" Width="75px" /> + <BitImage Src="@GetProductImageUrl(product)" Alt="@product.Name" Width="75px" /> <BitStack AutoHeight> <BitText>@product.Name</BitText> <BitText Typography="BitTypography.Body2" Class="product-desc">@product.Description</BitText> </BitStack> </BitStack> </BitCard> </ItemTemplate> <LoadingTemplate> - <BitStack Alignment="BitAlignment.Center"> + <BitStack Alignment="BitAlignment.Center" aria-label="Loading more products"> <BitEllipsisLoading /> + <BitText>Loading more products...</BitText> </BitStack> </LoadingTemplate> </BitInfiniteScrolling>src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor.scss (2)
16-27
: Consider using CSS custom properties for magic numbers.Extract magic numbers into CSS custom properties for better maintainability:
.carousel-stack { display: flex !important; @include lt-sm { flex-direction: column !important; .carousel-desc { overflow: hidden; - max-height: 60px; + max-height: var(--carousel-desc-max-height, 60px); } } }
36-57
: Extract repeated calculations into reusable variables.Consider using SCSS variables for the repeated calculations in width properties:
+$gap: 1rem; +$col-4: calc(25% - #{$gap * 0.75}); +$col-3: calc(33% - #{$gap * 0.67}); +$col-2: calc(50% - #{$gap * 0.5}); .product-item { height: 230px; overflow: hidden; - width: calc(25% - 0.75rem); + width: $col-4; @include lt-md { - width: calc(33% - 0.67rem); + width: $col-3; } @include lt-sm { - width: calc(50% - 0.5rem); + width: $col-2;src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.json (1)
47-47
: Add explanatory comment for ProductImagesDir setting.Consider adding a comment to explain the purpose and requirements of this directory, following the pattern used for other configuration entries.
Add the following line before the ProductImagesDir entry:
+ "ProductImagesDir_Comment": "Directory for storing product images. Ensure appropriate read/write permissions are set.", "ProductImagesDir": "attachments/products/",
🧰 Tools
🪛 Biome (1.9.4)
[error] 46-47: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
[error] 47-47: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
[error] 47-47: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
[error] 47-47: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
[error] 47-48: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.json (2)
65-65
: Ensure configuration synchronization between Web and API projects.The ProductImagesDir configuration is duplicated in both Web and API projects. Consider implementing a mechanism to keep these configurations synchronized to prevent divergence.
Options to consider:
- Move shared settings to a common configuration file
- Implement configuration validation at startup to ensure consistency
- Use environment variables to maintain a single source of truth
Would you like me to propose a specific implementation for any of these approaches?
🧰 Tools
🪛 Biome (1.9.4)
[error] 64-65: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
[error] 65-65: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
[error] 65-65: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
[error] 65-65: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
[error] 65-67: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
65-65
: Add explanatory comment for consistency with Web project.Similar to the Web project, add a comment explaining the purpose of this configuration.
Add the following line before the ProductImagesDir entry:
+ "ProductImagesDir_Comment": "Directory for storing product images. Ensure appropriate read/write permissions are set.", "ProductImagesDir": "attachments/products/",
🧰 Tools
🪛 Biome (1.9.4)
[error] 64-65: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
[error] 65-65: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
[error] 65-65: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
[error] 65-65: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
[error] 65-67: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/wwwroot/images/product-placeholder.png
is excluded by!**/*.png
📒 Files selected for processing (14)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor
(4 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor.cs
(4 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor.scss
(2 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs
(1 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Products/ProductController.cs
(1 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Product/ProductConfiguration.cs
(1 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Migrations/20250109120517_InitialMigration.cs
(2 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Migrations/AppDbContextModelSnapshot.cs
(14 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Models/Products/Product.cs
(1 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/ServerApiSettings.cs
(1 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.json
(1 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.json
(1 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Controllers/Products/IProductController.cs
(1 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Dtos/Products/ProductDto.cs
(1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.json
[error] 46-47: End of file expected
Use an array for a sequence of values: [1, 2]
(parse)
[error] 47-47: End of file expected
Use an array for a sequence of values: [1, 2]
(parse)
[error] 47-47: End of file expected
Use an array for a sequence of values: [1, 2]
(parse)
[error] 47-47: End of file expected
Use an array for a sequence of values: [1, 2]
(parse)
[error] 47-48: End of file expected
Use an array for a sequence of values: [1, 2]
(parse)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/appsettings.json
[error] 64-65: End of file expected
Use an array for a sequence of values: [1, 2]
(parse)
[error] 65-65: End of file expected
Use an array for a sequence of values: [1, 2]
(parse)
[error] 65-65: End of file expected
Use an array for a sequence of values: [1, 2]
(parse)
[error] 65-65: End of file expected
Use an array for a sequence of values: [1, 2]
(parse)
[error] 65-67: End of file expected
Use an array for a sequence of values: [1, 2]
(parse)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: build and test
🔇 Additional comments (6)
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Configurations/Product/ProductConfiguration.cs (1)
26-41
: LGTM! Product descriptions are well-structured and informative.The updated product entries maintain consistency in:
- Detailed descriptions
- Price ranges
- Category associations
- Timestamp patterns
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/HomePage.razor (1)
Line range hint
65-197
: LGTM! Clean module separation using conditional compilation.The conditional compilation directives effectively separate the sales and admin module content.
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Migrations/20250109120517_InitialMigration.cs (2)
100-101
: LGTM! Well-structured schema update.The ImageFileName column is properly added as nullable, which is appropriate for backward compatibility.
311-336
: LGTM! Comprehensive seed data update.The seed data is well-organized with descriptive product information.
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Data/Migrations/AppDbContextModelSnapshot.cs (1)
297-299
: LGTM! Correct model configuration.The ImageFileName property is properly configured in the model snapshot.
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Web/appsettings.json (1)
47-47
: Verify directory handling in the codebase.Ensure that the application properly handles the creation and permissions of the products directory.
Let's verify the directory handling implementation:
✅ Verification successful
Directory handling implementation is properly secured
The codebase implements robust directory handling with:
- Proper directory creation under App_Data
- Environment-aware path handling (Docker/non-Docker)
- Secure file storage through blob storage abstraction
- Proper directory existence checks and creation
The ProductImagesDir configuration is correctly integrated into the attachment handling system with appropriate security measures.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for directory creation/initialization code rg -l "ProductImagesDir|Directory\.(Create|Exists)" --type cs # Search for file operation patterns related to product images rg "Path\.(Combine|Join).*ProductImagesDir" --type csLength of output: 734
Script:
#!/bin/bash # Check directory initialization and configuration ast-grep --pattern 'class ServerApiSettings { $$$ }' src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/ServerApiSettings.cs echo "=== Attachment Controller Implementation ===" ast-grep --pattern 'class AttachmentController { $$$ }' src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/AttachmentController.cs echo "=== Program Services Configuration ===" rg "ProductImagesDir" src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Program.Services.cs -A 5Length of output: 634
Script:
#!/bin/bash # Search for directory handling and file operations echo "=== Directory Operations ===" rg "Directory\." --type cs -A 3 echo "=== File Operations ===" rg "IFormFile|SaveFileAsync|UploadFile" --type cs -A 3 echo "=== Configuration Settings ===" rg "ProductImagesDir" --type cs -A 3Length of output: 13041
🧰 Tools
🪛 Biome (1.9.4)
[error] 46-47: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
[error] 47-47: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
[error] 47-47: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
[error] 47-47: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
[error] 47-48: End of file expected
Use an array for a sequence of values:
[1, 2]
(parse)
closes #9678
Summary by CodeRabbit
New Features
Bug Fixes
Chores