├── .dockerignore ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── documenation-request.md │ └── feature_request.md └── workflows │ └── dotnet.yml ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── BlazorHero.CleanArchitecture.sln ├── Features.md ├── LICENSE ├── README.md ├── docker-compose.dcproj ├── docker-compose.yml └── src ├── Application ├── Application.csproj ├── Configurations │ ├── AppConfiguration.cs │ └── MailConfiguration.cs ├── Enums │ ├── AuditType.cs │ └── UploadType.cs ├── Exceptions │ └── ApiException.cs ├── Extensions │ ├── EnumExtensions.cs │ ├── ExpressionExtensions.cs │ ├── QueryableExtensions.cs │ └── ServiceCollectionExtensions.cs ├── Features │ ├── Brands │ │ ├── Commands │ │ │ ├── AddEdit │ │ │ │ └── AddEditBrandCommand.cs │ │ │ ├── Delete │ │ │ │ └── DeleteBrandCommand.cs │ │ │ └── Import │ │ │ │ └── ImportBrandsCommand.cs │ │ └── Queries │ │ │ ├── Export │ │ │ └── ExportBrandsQuery.cs │ │ │ ├── GetAll │ │ │ ├── GetAllBrandsQuery.cs │ │ │ └── GetAllBrandsResponse.cs │ │ │ └── GetById │ │ │ ├── GetBrandByIdQuery.cs │ │ │ └── GetBrandByIdResponse.cs │ ├── Dashboards │ │ └── Queries │ │ │ └── GetData │ │ │ ├── DashboardDataResponse.cs │ │ │ └── GetDashboardDataQuery.cs │ ├── DocumentTypes │ │ ├── Commands │ │ │ ├── AddEdit │ │ │ │ └── AddEditDocumentTypeCommand.cs │ │ │ └── Delete │ │ │ │ └── DeleteDocumentTypeCommand.cs │ │ └── Queries │ │ │ ├── Export │ │ │ └── ExportDocumentTypesQuery.cs │ │ │ ├── GetAll │ │ │ ├── GetAllDocumentTypesQuery.cs │ │ │ └── GetAllDocumentTypesResponse.cs │ │ │ └── GetById │ │ │ ├── GetDocumentTypeByIdQuery.cs │ │ │ └── GetDocumentTypeByIdResponse.cs │ ├── Documents │ │ ├── Commands │ │ │ ├── AddEdit │ │ │ │ └── AddEditDocumentCommand.cs │ │ │ └── Delete │ │ │ │ └── DeleteDocumentCommand.cs │ │ └── Queries │ │ │ ├── GetAll │ │ │ ├── GetAllDocumentsQuery.cs │ │ │ └── GetAllDocumentsResponse.cs │ │ │ └── GetById │ │ │ ├── GetDocumentByIdQuery.cs │ │ │ └── GetDocumentByIdResponse.cs │ ├── ExtendedAttributes │ │ ├── Commands │ │ │ ├── AddEdit │ │ │ │ └── AddEditExtendedAttributeCommand.cs │ │ │ └── Delete │ │ │ │ └── DeleteExtendedAttributeCommand.cs │ │ └── Queries │ │ │ ├── Export │ │ │ └── ExportExtendedAttributesQuery.cs │ │ │ ├── GetAll │ │ │ ├── GetAllExtendedAttributesQuery.cs │ │ │ └── GetAllExtendedAttributesResponse.cs │ │ │ ├── GetAllByEntityId │ │ │ ├── GetAllExtendedAttributesByEntityIdQuery.cs │ │ │ └── GetAllExtendedAttributesByEntityIdResponse.cs │ │ │ └── GetById │ │ │ ├── GetExtendedAttributeByIdQuery.cs │ │ │ └── GetExtendedAttributeByIdResponse.cs │ └── Products │ │ ├── Commands │ │ ├── AddEdit │ │ │ └── AddEditProductCommand.cs │ │ └── Delete │ │ │ └── DeleteProductCommand.cs │ │ └── Queries │ │ ├── Export │ │ └── ExportProductsQuery.cs │ │ ├── GetAllPaged │ │ ├── GetAllPagedProductsResponse.cs │ │ └── GetAllProductsQuery.cs │ │ └── GetProductImage │ │ ├── GetProductImageQuery.cs │ │ └── GetProductImageResponse.cs ├── Interfaces │ ├── Chat │ │ ├── IChatHistory.cs │ │ └── IChatUser.cs │ ├── Common │ │ ├── IScopedService.cs │ │ ├── IService.cs │ │ └── ISingletonService.cs │ ├── Repositories │ │ ├── IBrandRepository.cs │ │ ├── IDocumentRepository.cs │ │ ├── IDocumentTypeRepository.cs │ │ ├── IExtendedAttributeUnitOfWork.cs │ │ ├── IProductRepository.cs │ │ ├── IRepositoryAsync.cs │ │ └── IUnitOfWork.cs │ ├── Serialization │ │ ├── Options │ │ │ └── IJsonSerializerOptions.cs │ │ ├── Serializers │ │ │ └── IJsonSerializer.cs │ │ └── Settings │ │ │ └── IJsonSerializerSettings.cs │ └── Services │ │ ├── Account │ │ └── IAccountService.cs │ │ ├── IAuditService.cs │ │ ├── IChatService.cs │ │ ├── ICurrentUserService.cs │ │ ├── IDatabaseSeeder.cs │ │ ├── IDateTimeService.cs │ │ ├── IExcelService.cs │ │ ├── IMailService.cs │ │ ├── IUploadService.cs │ │ ├── Identity │ │ ├── IRoleClaimService.cs │ │ ├── IRoleService.cs │ │ ├── ITokenService.cs │ │ └── IUserService.cs │ │ └── Storage │ │ ├── ChangedEventArgs.cs │ │ ├── ChangingEventArgs.cs │ │ ├── IServerStorageService.cs │ │ ├── ISyncServerStorageService.cs │ │ └── Provider │ │ └── IServerStorageProvider.cs ├── Mappings │ ├── BrandProfile.cs │ ├── DocumentProfile.cs │ ├── DocumentTypeProfile.cs │ ├── ExtendedAttributeProfile.cs │ └── ProductProfile.cs ├── Models │ └── Chat │ │ ├── ChatHistory.cs │ │ └── Message.cs ├── Requests │ ├── Catalog │ │ └── GetAllPagedProductsRequest.cs │ ├── Documents │ │ └── GetAllPagedDocumentsRequest.cs │ ├── Identity │ │ ├── ChangePasswordRequest.cs │ │ ├── ForgotPasswordRequest.cs │ │ ├── PermissionRequest.cs │ │ ├── RefreshTokenRequest.cs │ │ ├── RegisterRequest.cs │ │ ├── ResetPasswordRequest.cs │ │ ├── RoleClaimRequest.cs │ │ ├── RoleRequest.cs │ │ ├── ToggleUserStatusRequest.cs │ │ ├── TokenRequest.cs │ │ ├── UpdateProfilePictureRequest.cs │ │ ├── UpdateProfileRequest.cs │ │ └── UpdateUserRolesRequest.cs │ ├── Mail │ │ └── MailRequest.cs │ ├── PagedRequest.cs │ └── UploadRequest.cs ├── Resources │ ├── Features │ │ ├── Brands │ │ │ ├── Commands │ │ │ │ ├── AddEdit │ │ │ │ │ ├── AddEditBrandCommandHandler.ar.resx │ │ │ │ │ ├── AddEditBrandCommandHandler.en.resx │ │ │ │ │ ├── AddEditBrandCommandHandler.es.resx │ │ │ │ │ ├── AddEditBrandCommandHandler.fr.resx │ │ │ │ │ ├── AddEditBrandCommandHandler.it.resx │ │ │ │ │ ├── AddEditBrandCommandHandler.km.resx │ │ │ │ │ ├── AddEditBrandCommandHandler.nl.resx │ │ │ │ │ └── AddEditBrandCommandHandler.ru.resx │ │ │ │ ├── Delete │ │ │ │ │ ├── DeleteBrandCommandHandler.ar.resx │ │ │ │ │ ├── DeleteBrandCommandHandler.de.nl.resx │ │ │ │ │ ├── DeleteBrandCommandHandler.de.resx │ │ │ │ │ ├── DeleteBrandCommandHandler.en.resx │ │ │ │ │ ├── DeleteBrandCommandHandler.es.resx │ │ │ │ │ ├── DeleteBrandCommandHandler.fr.resx │ │ │ │ │ ├── DeleteBrandCommandHandler.id.resx │ │ │ │ │ ├── DeleteBrandCommandHandler.it.resx │ │ │ │ │ ├── DeleteBrandCommandHandler.km.resx │ │ │ │ │ ├── DeleteBrandCommandHandler.ru.resx │ │ │ │ │ └── DeleteBrandCommandHandler.sv.resx │ │ │ │ └── Import │ │ │ │ │ ├── ImportBrandsCommandHandler.ar.resx │ │ │ │ │ ├── ImportBrandsCommandHandler.de.resx │ │ │ │ │ ├── ImportBrandsCommandHandler.en.resx │ │ │ │ │ ├── ImportBrandsCommandHandler.es.resx │ │ │ │ │ ├── ImportBrandsCommandHandler.fr.resx │ │ │ │ │ ├── ImportBrandsCommandHandler.id.resx │ │ │ │ │ ├── ImportBrandsCommandHandler.it.resx │ │ │ │ │ ├── ImportBrandsCommandHandler.km.resx │ │ │ │ │ ├── ImportBrandsCommandHandler.nl.resx │ │ │ │ │ ├── ImportBrandsCommandHandler.ru.resx │ │ │ │ │ └── ImportBrandsCommandHandler.sv.resx │ │ │ └── Queries │ │ │ │ └── Export │ │ │ │ ├── ExportBrandsQueryHandler.ar.resx │ │ │ │ ├── ExportBrandsQueryHandler.en.resx │ │ │ │ ├── ExportBrandsQueryHandler.es.resx │ │ │ │ ├── ExportBrandsQueryHandler.fr.resx │ │ │ │ ├── ExportBrandsQueryHandler.km.resx │ │ │ │ ├── ExportBrandsQueryHandler.nl.resx │ │ │ │ └── ExportBrandsQueryHandler.ru.resx │ │ ├── Dashboards │ │ │ └── Queries │ │ │ │ └── GetData │ │ │ │ ├── GetDashboardDataQueryHandler.ar.resx │ │ │ │ ├── GetDashboardDataQueryHandler.en.resx │ │ │ │ ├── GetDashboardDataQueryHandler.es.resx │ │ │ │ ├── GetDashboardDataQueryHandler.fr.resx │ │ │ │ ├── GetDashboardDataQueryHandler.it.resx │ │ │ │ ├── GetDashboardDataQueryHandler.km.resx │ │ │ │ ├── GetDashboardDataQueryHandler.nl.resx │ │ │ │ └── GetDashboardDataQueryHandler.ru.resx │ │ ├── DocumentTypes │ │ │ ├── Commands │ │ │ │ ├── AddEdit │ │ │ │ │ ├── AddEditDocumentTypeCommandHandler.ar.resx │ │ │ │ │ ├── AddEditDocumentTypeCommandHandler.en.nl.resx │ │ │ │ │ ├── AddEditDocumentTypeCommandHandler.en.resx │ │ │ │ │ ├── AddEditDocumentTypeCommandHandler.es.resx │ │ │ │ │ ├── AddEditDocumentTypeCommandHandler.fr.resx │ │ │ │ │ ├── AddEditDocumentTypeCommandHandler.km.resx │ │ │ │ │ └── AddEditDocumentTypeCommandHandler.ru.resx │ │ │ │ └── Delete │ │ │ │ │ ├── DeleteDocumentTypeCommandHandler.ar.resx │ │ │ │ │ ├── DeleteDocumentTypeCommandHandler.en.nl.resx │ │ │ │ │ ├── DeleteDocumentTypeCommandHandler.en.resx │ │ │ │ │ ├── DeleteDocumentTypeCommandHandler.es.resx │ │ │ │ │ ├── DeleteDocumentTypeCommandHandler.fr.resx │ │ │ │ │ ├── DeleteDocumentTypeCommandHandler.km.resx │ │ │ │ │ └── DeleteDocumentTypeCommandHandler.ru.resx │ │ │ └── Queries │ │ │ │ └── Export │ │ │ │ ├── ExportDocumentTypesQueryHandler.ar.resx │ │ │ │ ├── ExportDocumentTypesQueryHandler.en.resx │ │ │ │ ├── ExportDocumentTypesQueryHandler.es.resx │ │ │ │ ├── ExportDocumentTypesQueryHandler.fr.resx │ │ │ │ ├── ExportDocumentTypesQueryHandler.km.resx │ │ │ │ ├── ExportDocumentTypesQueryHandler.nl.resx │ │ │ │ └── ExportDocumentTypesQueryHandler.ru.resx │ │ ├── Documents │ │ │ └── Commands │ │ │ │ ├── AddEdit │ │ │ │ ├── AddEditDocumentCommandHandler.ar.resx │ │ │ │ ├── AddEditDocumentCommandHandler.en.nl.resx │ │ │ │ ├── AddEditDocumentCommandHandler.en.resx │ │ │ │ ├── AddEditDocumentCommandHandler.es.resx │ │ │ │ ├── AddEditDocumentCommandHandler.fr.resx │ │ │ │ ├── AddEditDocumentCommandHandler.it.resx │ │ │ │ ├── AddEditDocumentCommandHandler.km.resx │ │ │ │ └── AddEditDocumentCommandHandler.ru.resx │ │ │ │ └── Delete │ │ │ │ ├── DeleteDocumentCommandHandler.ar.resx │ │ │ │ ├── DeleteDocumentCommandHandler.en.nl.resx │ │ │ │ ├── DeleteDocumentCommandHandler.en.resx │ │ │ │ ├── DeleteDocumentCommandHandler.es.resx │ │ │ │ ├── DeleteDocumentCommandHandler.fr.resx │ │ │ │ ├── DeleteDocumentCommandHandler.it.resx │ │ │ │ ├── DeleteDocumentCommandHandler.km.resx │ │ │ │ └── DeleteDocumentCommandHandler.ru.resx │ │ ├── ExtendedAttributes │ │ │ ├── Commands │ │ │ │ ├── AddEdit │ │ │ │ │ ├── AddEditExtendedAttributeCommandLocalization.ar.resx │ │ │ │ │ ├── AddEditExtendedAttributeCommandLocalization.de.nl.resx │ │ │ │ │ ├── AddEditExtendedAttributeCommandLocalization.de.resx │ │ │ │ │ ├── AddEditExtendedAttributeCommandLocalization.en.resx │ │ │ │ │ ├── AddEditExtendedAttributeCommandLocalization.es.resx │ │ │ │ │ ├── AddEditExtendedAttributeCommandLocalization.fr.resx │ │ │ │ │ ├── AddEditExtendedAttributeCommandLocalization.id.resx │ │ │ │ │ ├── AddEditExtendedAttributeCommandLocalization.it.resx │ │ │ │ │ ├── AddEditExtendedAttributeCommandLocalization.km.resx │ │ │ │ │ ├── AddEditExtendedAttributeCommandLocalization.ru.resx │ │ │ │ │ └── AddEditExtendedAttributeCommandLocalization.sv.resx │ │ │ │ └── Delete │ │ │ │ │ ├── DeleteExtendedAttributeCommandLocalization.ar.resx │ │ │ │ │ ├── DeleteExtendedAttributeCommandLocalization.de.nl.resx │ │ │ │ │ ├── DeleteExtendedAttributeCommandLocalization.de.resx │ │ │ │ │ ├── DeleteExtendedAttributeCommandLocalization.en.resx │ │ │ │ │ ├── DeleteExtendedAttributeCommandLocalization.es.resx │ │ │ │ │ ├── DeleteExtendedAttributeCommandLocalization.fr.resx │ │ │ │ │ ├── DeleteExtendedAttributeCommandLocalization.id.resx │ │ │ │ │ ├── DeleteExtendedAttributeCommandLocalization.it.resx │ │ │ │ │ ├── DeleteExtendedAttributeCommandLocalization.km.resx │ │ │ │ │ ├── DeleteExtendedAttributeCommandLocalization.ru.resx │ │ │ │ │ └── DeleteExtendedAttributeCommandLocalization.sv.resx │ │ │ └── Queries │ │ │ │ └── Export │ │ │ │ ├── ExportExtendedAttributesQueryLocalization.ar.resx │ │ │ │ ├── ExportExtendedAttributesQueryLocalization.de.resx │ │ │ │ ├── ExportExtendedAttributesQueryLocalization.en.resx │ │ │ │ ├── ExportExtendedAttributesQueryLocalization.es.resx │ │ │ │ ├── ExportExtendedAttributesQueryLocalization.fr.resx │ │ │ │ ├── ExportExtendedAttributesQueryLocalization.id.resx │ │ │ │ ├── ExportExtendedAttributesQueryLocalization.it.resx │ │ │ │ ├── ExportExtendedAttributesQueryLocalization.km.resx │ │ │ │ ├── ExportExtendedAttributesQueryLocalization.nl.resx │ │ │ │ ├── ExportExtendedAttributesQueryLocalization.ru.resx │ │ │ │ └── ExportExtendedAttributesQueryLocalization.sv.resx │ │ └── Products │ │ │ ├── Commands │ │ │ ├── AddEdit │ │ │ │ ├── AddEditProductCommandHandler.ar.resx │ │ │ │ ├── AddEditProductCommandHandler.en.nl.resx │ │ │ │ ├── AddEditProductCommandHandler.en.resx │ │ │ │ ├── AddEditProductCommandHandler.es.resx │ │ │ │ ├── AddEditProductCommandHandler.fr.resx │ │ │ │ ├── AddEditProductCommandHandler.it.resx │ │ │ │ ├── AddEditProductCommandHandler.km.resx │ │ │ │ └── AddEditProductCommandHandler.ru.resx │ │ │ └── Delete │ │ │ │ ├── DeleteProductCommandHandler.ar.resx │ │ │ │ ├── DeleteProductCommandHandler.de.nl.resx │ │ │ │ ├── DeleteProductCommandHandler.de.resx │ │ │ │ ├── DeleteProductCommandHandler.en.resx │ │ │ │ ├── DeleteProductCommandHandler.es.resx │ │ │ │ ├── DeleteProductCommandHandler.fr.resx │ │ │ │ ├── DeleteProductCommandHandler.id.resx │ │ │ │ ├── DeleteProductCommandHandler.it.resx │ │ │ │ ├── DeleteProductCommandHandler.km.resx │ │ │ │ ├── DeleteProductCommandHandler.ru.resx │ │ │ │ └── DeleteProductCommandHandler.sv.resx │ │ │ └── Queries │ │ │ └── Export │ │ │ ├── ExportProductsQueryHandler.ar.resx │ │ │ ├── ExportProductsQueryHandler.en.resx │ │ │ ├── ExportProductsQueryHandler.es.resx │ │ │ ├── ExportProductsQueryHandler.fr.resx │ │ │ ├── ExportProductsQueryHandler.it.resx │ │ │ ├── ExportProductsQueryHandler.km.resx │ │ │ ├── ExportProductsQueryHandler.nl.resx │ │ │ └── ExportProductsQueryHandler.ru.resx │ └── Validators │ │ ├── Features │ │ ├── Brands │ │ │ └── Commands │ │ │ │ └── AddEdit │ │ │ │ ├── AddEditBrandCommandValidator.ar.resx │ │ │ │ ├── AddEditBrandCommandValidator.de.resx │ │ │ │ ├── AddEditBrandCommandValidator.en.resx │ │ │ │ ├── AddEditBrandCommandValidator.es.resx │ │ │ │ ├── AddEditBrandCommandValidator.fr.resx │ │ │ │ ├── AddEditBrandCommandValidator.id.resx │ │ │ │ ├── AddEditBrandCommandValidator.it.resx │ │ │ │ ├── AddEditBrandCommandValidator.km.resx │ │ │ │ ├── AddEditBrandCommandValidator.nl.resx │ │ │ │ ├── AddEditBrandCommandValidator.ru.resx │ │ │ │ └── AddEditBrandCommandValidator.sv.resx │ │ ├── DocumentTypes │ │ │ └── Commands │ │ │ │ └── AddEdit │ │ │ │ ├── AddEditDocumentTypeCommandValidator.ar.resx │ │ │ │ ├── AddEditDocumentTypeCommandValidator.de.resx │ │ │ │ ├── AddEditDocumentTypeCommandValidator.en.resx │ │ │ │ ├── AddEditDocumentTypeCommandValidator.es.resx │ │ │ │ ├── AddEditDocumentTypeCommandValidator.fr.resx │ │ │ │ ├── AddEditDocumentTypeCommandValidator.id.resx │ │ │ │ ├── AddEditDocumentTypeCommandValidator.it.resx │ │ │ │ ├── AddEditDocumentTypeCommandValidator.km.resx │ │ │ │ ├── AddEditDocumentTypeCommandValidator.nl.resx │ │ │ │ ├── AddEditDocumentTypeCommandValidator.ru.resx │ │ │ │ └── AddEditDocumentTypeCommandValidator.sv.resx │ │ ├── Documents │ │ │ └── Commands │ │ │ │ └── AddEdit │ │ │ │ ├── AddEditDocumentCommandValidator.ar.resx │ │ │ │ ├── AddEditDocumentCommandValidator.de.resx │ │ │ │ ├── AddEditDocumentCommandValidator.en.resx │ │ │ │ ├── AddEditDocumentCommandValidator.es.resx │ │ │ │ ├── AddEditDocumentCommandValidator.fr.resx │ │ │ │ ├── AddEditDocumentCommandValidator.id.resx │ │ │ │ ├── AddEditDocumentCommandValidator.it.resx │ │ │ │ ├── AddEditDocumentCommandValidator.km.resx │ │ │ │ ├── AddEditDocumentCommandValidator.nl.resx │ │ │ │ ├── AddEditDocumentCommandValidator.ru.resx │ │ │ │ └── AddEditDocumentCommandValidator.sv.resx │ │ ├── ExtendedAttributes │ │ │ └── Commands │ │ │ │ └── AddEdit │ │ │ │ ├── AddEditExtendedAttributeCommandValidatorLocalization.ar.resx │ │ │ │ ├── AddEditExtendedAttributeCommandValidatorLocalization.de.resx │ │ │ │ ├── AddEditExtendedAttributeCommandValidatorLocalization.en.resx │ │ │ │ ├── AddEditExtendedAttributeCommandValidatorLocalization.es.resx │ │ │ │ ├── AddEditExtendedAttributeCommandValidatorLocalization.fr.resx │ │ │ │ ├── AddEditExtendedAttributeCommandValidatorLocalization.id.resx │ │ │ │ ├── AddEditExtendedAttributeCommandValidatorLocalization.it.resx │ │ │ │ ├── AddEditExtendedAttributeCommandValidatorLocalization.km.resx │ │ │ │ ├── AddEditExtendedAttributeCommandValidatorLocalization.nl.resx │ │ │ │ ├── AddEditExtendedAttributeCommandValidatorLocalization.ru.resx │ │ │ │ └── AddEditExtendedAttributeCommandValidatorLocalization.sv.resx │ │ └── Products │ │ │ └── Commands │ │ │ └── AddEdit │ │ │ ├── AddEditProductCommandValidator.ar.resx │ │ │ ├── AddEditProductCommandValidator.de.resx │ │ │ ├── AddEditProductCommandValidator.en.resx │ │ │ ├── AddEditProductCommandValidator.es.resx │ │ │ ├── AddEditProductCommandValidator.fr.resx │ │ │ ├── AddEditProductCommandValidator.id.resx │ │ │ ├── AddEditProductCommandValidator.it.resx │ │ │ ├── AddEditProductCommandValidator.km.resx │ │ │ ├── AddEditProductCommandValidator.nl.resx │ │ │ ├── AddEditProductCommandValidator.ru.resx │ │ │ └── AddEditProductCommandValidator.sv.resx │ │ └── Requests │ │ └── Identity │ │ ├── ChangePasswordRequestValidator.ar.resx │ │ ├── ChangePasswordRequestValidator.de.resx │ │ ├── ChangePasswordRequestValidator.en.resx │ │ ├── ChangePasswordRequestValidator.es.resx │ │ ├── ChangePasswordRequestValidator.fr.resx │ │ ├── ChangePasswordRequestValidator.id.resx │ │ ├── ChangePasswordRequestValidator.it.resx │ │ ├── ChangePasswordRequestValidator.km.resx │ │ ├── ChangePasswordRequestValidator.nl.resx │ │ ├── ChangePasswordRequestValidator.ru.resx │ │ ├── ChangePasswordRequestValidator.sv.resx │ │ ├── ForgotPasswordRequestValidator.ar.resx │ │ ├── ForgotPasswordRequestValidator.de.resx │ │ ├── ForgotPasswordRequestValidator.en.resx │ │ ├── ForgotPasswordRequestValidator.es.resx │ │ ├── ForgotPasswordRequestValidator.fr.resx │ │ ├── ForgotPasswordRequestValidator.id.resx │ │ ├── ForgotPasswordRequestValidator.it.resx │ │ ├── ForgotPasswordRequestValidator.km.resx │ │ ├── ForgotPasswordRequestValidator.nl.resx │ │ ├── ForgotPasswordRequestValidator.ru.resx │ │ ├── ForgotPasswordRequestValidator.sv.resx │ │ ├── RegisterRequestValidator.ar.resx │ │ ├── RegisterRequestValidator.de.resx │ │ ├── RegisterRequestValidator.en.resx │ │ ├── RegisterRequestValidator.es.resx │ │ ├── RegisterRequestValidator.fr.resx │ │ ├── RegisterRequestValidator.id.resx │ │ ├── RegisterRequestValidator.it.resx │ │ ├── RegisterRequestValidator.km.resx │ │ ├── RegisterRequestValidator.nl.resx │ │ ├── RegisterRequestValidator.ru.resx │ │ ├── RegisterRequestValidator.sv.resx │ │ ├── ResetPasswordRequestValidator.ar.resx │ │ ├── ResetPasswordRequestValidator.de.resx │ │ ├── ResetPasswordRequestValidator.en.resx │ │ ├── ResetPasswordRequestValidator.es.resx │ │ ├── ResetPasswordRequestValidator.fr.resx │ │ ├── ResetPasswordRequestValidator.id.resx │ │ ├── ResetPasswordRequestValidator.it.resx │ │ ├── ResetPasswordRequestValidator.km.resx │ │ ├── ResetPasswordRequestValidator.nl.resx │ │ ├── ResetPasswordRequestValidator.ru.resx │ │ ├── ResetPasswordRequestValidator.sv.resx │ │ ├── RoleRequestValidator.ar.resx │ │ ├── RoleRequestValidator.de.resx │ │ ├── RoleRequestValidator.en.resx │ │ ├── RoleRequestValidator.es.resx │ │ ├── RoleRequestValidator.fr.resx │ │ ├── RoleRequestValidator.id.resx │ │ ├── RoleRequestValidator.it.resx │ │ ├── RoleRequestValidator.km.resx │ │ ├── RoleRequestValidator.nl.resx │ │ ├── RoleRequestValidator.ru.resx │ │ ├── RoleRequestValidator.sv.resx │ │ ├── TokenRequestValidator.ar.resx │ │ ├── TokenRequestValidator.de.resx │ │ ├── TokenRequestValidator.en.resx │ │ ├── TokenRequestValidator.es.resx │ │ ├── TokenRequestValidator.fr.resx │ │ ├── TokenRequestValidator.id.resx │ │ ├── TokenRequestValidator.it.resx │ │ ├── TokenRequestValidator.km.resx │ │ ├── TokenRequestValidator.nl.resx │ │ ├── TokenRequestValidator.ru.resx │ │ ├── TokenRequestValidator.sv.resx │ │ ├── UpdateProfileRequestValidator.ar.resx │ │ ├── UpdateProfileRequestValidator.de.resx │ │ ├── UpdateProfileRequestValidator.en.resx │ │ ├── UpdateProfileRequestValidator.es.resx │ │ ├── UpdateProfileRequestValidator.fr.resx │ │ ├── UpdateProfileRequestValidator.id.resx │ │ ├── UpdateProfileRequestValidator.it.resx │ │ ├── UpdateProfileRequestValidator.km.resx │ │ ├── UpdateProfileRequestValidator.nl.resx │ │ ├── UpdateProfileRequestValidator.ru.resx │ │ └── UpdateProfileRequestValidator.sv.resx ├── Responses │ ├── Audit │ │ └── AuditResponse.cs │ └── Identity │ │ ├── ChatHistoryResponse.cs │ │ ├── ChatUserResponse.cs │ │ ├── GetAllRolesResponse.cs │ │ ├── GetAllUsersResponse.cs │ │ ├── PermissionResponse.cs │ │ ├── RoleClaimResponse.cs │ │ ├── RoleResponse.cs │ │ ├── TokenResponse.cs │ │ ├── UserResponse.cs │ │ └── UserRolesResponse.cs ├── Serialization │ ├── JsonConverters │ │ └── TimespanJsonConverter.cs │ ├── Options │ │ └── SystemTextJsonOptions.cs │ ├── Serializers │ │ ├── NewtonSoftJsonSerializer.cs │ │ └── SystemTextJsonSerializer.cs │ └── Settings │ │ └── NewtonsoftJsonSettings.cs ├── Specifications │ ├── Base │ │ ├── HeroSpecification.cs │ │ └── ISpecification.cs │ ├── Catalog │ │ ├── BrandFilterSpecification.cs │ │ └── ProductFilterSpecification.cs │ ├── ExtendedAttribute │ │ └── ExtendedAttributeFilterSpecification.cs │ └── Misc │ │ ├── DocumentFilterSpecification.cs │ │ └── DocumentTypeFilterSpecification.cs └── Validators │ ├── Extensions │ └── ValidatorExtensions.cs │ ├── Features │ ├── Brands │ │ └── Commands │ │ │ └── AddEdit │ │ │ └── AddEditBrandCommandValidator.cs │ ├── DocumentTypes │ │ └── Commands │ │ │ └── AddEdit │ │ │ └── AddEditDocumentTypeCommandValidator.cs │ ├── Documents │ │ └── Commands │ │ │ └── AddEdit │ │ │ └── AddEditDocumentCommandValidator.cs │ ├── ExtendedAttributes │ │ └── Commands │ │ │ └── AddEdit │ │ │ ├── AddEditDocumentExtendedAttributeCommandValidator.cs │ │ │ └── AddEditExtendedAttributeCommandValidator.cs │ └── Products │ │ └── Commands │ │ └── AddEdit │ │ └── AddEditProductCommandValidator.cs │ ├── JsonValidator.cs │ └── Requests │ └── Identity │ ├── ChangePasswordRequestValidator.cs │ ├── ForgotPasswordRequestValidator.cs │ ├── RegisterRequestValidator.cs │ ├── ResetPasswordRequestValidator.cs │ ├── RoleRequestValidator.cs │ ├── TokenRequestValidator.cs │ └── UpdateProfileRequestValidator.cs ├── Client.Infrastructure ├── Authentication │ ├── AuthenticationHeaderHandler.cs │ └── BlazorHeroStateProvider.cs ├── Client.Infrastructure.csproj ├── Extensions │ └── ResultExtensions.cs ├── Managers │ ├── Audit │ │ ├── AuditManager.cs │ │ └── IAuditManager.cs │ ├── Catalog │ │ ├── Brand │ │ │ ├── BrandManager.cs │ │ │ └── IBrandManager.cs │ │ └── Product │ │ │ ├── IProductManager.cs │ │ │ └── ProductManager.cs │ ├── Communication │ │ ├── ChatManager.cs │ │ └── IChatManager.cs │ ├── Dashboard │ │ ├── DashboardManager.cs │ │ └── IDashboardManager.cs │ ├── ExtendedAttribute │ │ ├── ExtendedAttributeManager.cs │ │ └── IExtendedAttributeManager.cs │ ├── IManager.cs │ ├── Identity │ │ ├── Account │ │ │ ├── AccountManager.cs │ │ │ └── IAccountManager.cs │ │ ├── Authentication │ │ │ ├── AuthenticationManager.cs │ │ │ └── IAuthenticationManager.cs │ │ ├── RoleClaims │ │ │ ├── IRoleClaimManager.cs │ │ │ └── RoleClaimManager.cs │ │ ├── Roles │ │ │ ├── IRoleManager.cs │ │ │ └── RoleManager.cs │ │ └── Users │ │ │ ├── IUserManager.cs │ │ │ └── UserManager.cs │ ├── Interceptors │ │ ├── HttpInterceptorManager.cs │ │ └── IHttpInterceptorManager.cs │ ├── Misc │ │ ├── Document │ │ │ ├── DocumentManager.cs │ │ │ └── IDocumentManager.cs │ │ └── DocumentType │ │ │ ├── DocumentTypeManager.cs │ │ │ └── IDocumentTypeManager.cs │ └── Preferences │ │ ├── ClientPreferenceManager.cs │ │ └── IClientPreferenceManager.cs ├── Mappings │ └── RolesProfile.cs ├── Resources │ └── Managers │ │ ├── Identity │ │ └── Authentication │ │ │ ├── AuthenticationManager.ar.resx │ │ │ ├── AuthenticationManager.en.resx │ │ │ ├── AuthenticationManager.es.resx │ │ │ ├── AuthenticationManager.fr.resx │ │ │ ├── AuthenticationManager.km.resx │ │ │ ├── AuthenticationManager.nl.resx │ │ │ └── AuthenticationManager.ru.resx │ │ ├── Interceptors │ │ ├── HttpInterceptorManager.ar.resx │ │ ├── HttpInterceptorManager.en.resx │ │ ├── HttpInterceptorManager.es.resx │ │ ├── HttpInterceptorManager.fr.resx │ │ ├── HttpInterceptorManager.km.resx │ │ ├── HttpInterceptorManager.nl.resx │ │ └── HttpInterceptorManager.ru.resx │ │ └── Preferences │ │ ├── ClientPreferenceManager.ar.resx │ │ ├── ClientPreferenceManager.en.resx │ │ ├── ClientPreferenceManager.es.resx │ │ ├── ClientPreferenceManager.fr.resx │ │ ├── ClientPreferenceManager.km.resx │ │ ├── ClientPreferenceManager.nl.resx │ │ └── ClientPreferenceManager.ru.resx ├── Routes │ ├── AccountEndpoints.cs │ ├── AuditEndpoints.cs │ ├── BrandsEndpoints.cs │ ├── ChatEndpoints.cs │ ├── DashboardEndpoints.cs │ ├── DocumentTypesEndpoints.cs │ ├── DocumentsEndpoints.cs │ ├── ExtendedAttributesEndpoints.cs │ ├── PreferencesEndpoints.cs │ ├── ProductsEndpoints.cs │ ├── RoleClaimsEndpoints.cs │ ├── RolesEndpoints.cs │ ├── TokenEndpoints.cs │ └── UserEndpoints.cs └── Settings │ ├── BlazorHeroTheme.cs │ └── ClientPreference.cs ├── Client ├── App.razor ├── Client.csproj ├── CustomIcons.cs ├── Extensions │ ├── ClaimsPrincipalExtensions.cs │ ├── HubExtensions.cs │ └── WebAssemblyHostBuilderExtensions.cs ├── Models │ ├── ChatMessage.cs │ └── ChatUser.cs ├── Pages │ ├── Authentication │ │ ├── Login.razor │ │ ├── Login.razor.cs │ │ ├── Register.razor │ │ └── Register.razor.cs │ ├── Catalog │ │ ├── AddEditBrandModal.razor │ │ ├── AddEditBrandModal.razor.cs │ │ ├── AddEditProductModal.razor │ │ ├── AddEditProductModal.razor.cs │ │ ├── Brands.razor │ │ ├── Brands.razor.cs │ │ ├── Products.razor │ │ └── Products.razor.cs │ ├── Communication │ │ ├── Chat.razor │ │ └── Chat.razor.cs │ ├── Content │ │ ├── Dashboard.razor │ │ ├── Dashboard.razor.cs │ │ └── Home.razor │ ├── Identity │ │ ├── Account.razor │ │ ├── Forgot.razor │ │ ├── Forgot.razor.cs │ │ ├── Profile.razor │ │ ├── Profile.razor.cs │ │ ├── RegisterUserModal.razor │ │ ├── RegisterUserModal.razor.cs │ │ ├── Reset.razor │ │ ├── Reset.razor.cs │ │ ├── RoleModal.razor │ │ ├── RoleModal.razor.cs │ │ ├── RolePermissions.razor │ │ ├── RolePermissions.razor.cs │ │ ├── Roles.razor │ │ ├── Roles.razor.cs │ │ ├── Security.razor │ │ ├── Security.razor.cs │ │ ├── UserProfile.razor │ │ ├── UserProfile.razor.cs │ │ ├── UserRoles.razor │ │ ├── UserRoles.razor.cs │ │ ├── Users.razor │ │ └── Users.razor.cs │ ├── Misc │ │ ├── AddEditDocumentModal.razor │ │ ├── AddEditDocumentModal.razor.cs │ │ ├── AddEditDocumentTypeModal.razor │ │ ├── AddEditDocumentTypeModal.razor.cs │ │ ├── DocumentStore.razor │ │ ├── DocumentStore.razor.cs │ │ ├── DocumentTypes.razor │ │ ├── DocumentTypes.razor.cs │ │ └── ExtendedAttribute │ │ │ └── DocumentExtendedAttributes.razor │ └── Utilities │ │ ├── AuditTrails.razor │ │ └── AuditTrails.razor.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Resources │ ├── Pages │ │ ├── Account │ │ │ ├── Forgot.ar.resx │ │ │ ├── Forgot.de.resx │ │ │ ├── Forgot.en.resx │ │ │ ├── Forgot.es.resx │ │ │ ├── Forgot.fr.resx │ │ │ ├── Forgot.id.resx │ │ │ ├── Forgot.it.resx │ │ │ ├── Forgot.km.resx │ │ │ ├── Forgot.nl.resx │ │ │ ├── Forgot.ru.resx │ │ │ ├── Forgot.sv.resx │ │ │ ├── Register.ar.resx │ │ │ ├── Register.de.resx │ │ │ ├── Register.en.resx │ │ │ ├── Register.es.resx │ │ │ ├── Register.fr.resx │ │ │ ├── Register.id.resx │ │ │ ├── Register.it.resx │ │ │ ├── Register.km.resx │ │ │ ├── Register.nl.resx │ │ │ ├── Register.ru.resx │ │ │ ├── Register.sv.resx │ │ │ ├── Reset.ar.resx │ │ │ ├── Reset.de.resx │ │ │ ├── Reset.en.resx │ │ │ ├── Reset.es.resx │ │ │ ├── Reset.fr.resx │ │ │ ├── Reset.id.resx │ │ │ ├── Reset.it.resx │ │ │ ├── Reset.km.resx │ │ │ ├── Reset.nl.resx │ │ │ ├── Reset.ru.resx │ │ │ └── Reset.sv.resx │ │ ├── Authentication │ │ │ ├── Login.ar.resx │ │ │ ├── Login.de.resx │ │ │ ├── Login.en.resx │ │ │ ├── Login.es.resx │ │ │ ├── Login.fr.resx │ │ │ ├── Login.id.resx │ │ │ ├── Login.it.resx │ │ │ ├── Login.km.resx │ │ │ ├── Login.nl.resx │ │ │ ├── Login.ru.resx │ │ │ ├── Login.sv.resx │ │ │ ├── Register.ar.resx │ │ │ ├── Register.en.resx │ │ │ ├── Register.es.resx │ │ │ ├── Register.fr.resx │ │ │ ├── Register.id.resx │ │ │ ├── Register.it.resx │ │ │ ├── Register.km.resx │ │ │ ├── Register.nl.resx │ │ │ ├── Register.ru.resx │ │ │ └── Register.sv.resx │ │ ├── Catalog │ │ │ ├── AddEditBrandModal.ar.resx │ │ │ ├── AddEditBrandModal.en.resx │ │ │ ├── AddEditBrandModal.es.resx │ │ │ ├── AddEditBrandModal.fr.resx │ │ │ ├── AddEditBrandModal.id.resx │ │ │ ├── AddEditBrandModal.it.resx │ │ │ ├── AddEditBrandModal.km.resx │ │ │ ├── AddEditBrandModal.nl.resx │ │ │ ├── AddEditBrandModal.ru.resx │ │ │ ├── AddEditProductModal.ar.resx │ │ │ ├── AddEditProductModal.de.resx │ │ │ ├── AddEditProductModal.en.resx │ │ │ ├── AddEditProductModal.es.resx │ │ │ ├── AddEditProductModal.fr.resx │ │ │ ├── AddEditProductModal.id.resx │ │ │ ├── AddEditProductModal.it.resx │ │ │ ├── AddEditProductModal.km.resx │ │ │ ├── AddEditProductModal.nl.resx │ │ │ ├── AddEditProductModal.ru.resx │ │ │ ├── AddEditProductModal.sv.resx │ │ │ ├── Brands.ar.resx │ │ │ ├── Brands.de.resx │ │ │ ├── Brands.en.resx │ │ │ ├── Brands.es.resx │ │ │ ├── Brands.fr.resx │ │ │ ├── Brands.id.resx │ │ │ ├── Brands.it.resx │ │ │ ├── Brands.km.resx │ │ │ ├── Brands.nl.resx │ │ │ ├── Brands.ru.resx │ │ │ ├── Brands.sv.resx │ │ │ ├── Products.ar.resx │ │ │ ├── Products.de.resx │ │ │ ├── Products.en.resx │ │ │ ├── Products.es.resx │ │ │ ├── Products.fr.resx │ │ │ ├── Products.id.resx │ │ │ ├── Products.it.resx │ │ │ ├── Products.km.resx │ │ │ ├── Products.nl.resx │ │ │ ├── Products.ru.resx │ │ │ └── Products.sv.resx │ │ ├── Communication │ │ │ ├── Chat.ar.resx │ │ │ ├── Chat.en.resx │ │ │ ├── Chat.es.resx │ │ │ ├── Chat.fr.resx │ │ │ ├── Chat.id.resx │ │ │ ├── Chat.it.resx │ │ │ ├── Chat.km.resx │ │ │ ├── Chat.nl.resx │ │ │ ├── Chat.ru.resx │ │ │ └── Chat.sv.resx │ │ ├── Content │ │ │ ├── Dashboard.ar.resx │ │ │ ├── Dashboard.en.resx │ │ │ ├── Dashboard.es.resx │ │ │ ├── Dashboard.fr.resx │ │ │ ├── Dashboard.id.resx │ │ │ ├── Dashboard.it.resx │ │ │ ├── Dashboard.km.resx │ │ │ ├── Dashboard.nl.resx │ │ │ ├── Dashboard.ru.resx │ │ │ ├── Dashboard.sv.resx │ │ │ ├── Home.ar.resx │ │ │ ├── Home.de.resx │ │ │ ├── Home.en.resx │ │ │ ├── Home.es.resx │ │ │ ├── Home.fr.resx │ │ │ ├── Home.id.resx │ │ │ ├── Home.it.resx │ │ │ ├── Home.km.resx │ │ │ ├── Home.nl.resx │ │ │ ├── Home.ru.resx │ │ │ ├── Home.sv.resx │ │ │ ├── Resources.ar.resx │ │ │ ├── Resources.de.resx │ │ │ ├── Resources.en.resx │ │ │ ├── Resources.es.resx │ │ │ ├── Resources.fr.resx │ │ │ ├── Resources.id.resx │ │ │ ├── Resources.it.resx │ │ │ ├── Resources.km.resx │ │ │ ├── Resources.nl.resx │ │ │ ├── Resources.ru.resx │ │ │ └── Resources.sv.resx │ │ ├── Identity │ │ │ ├── Account.ar.resx │ │ │ ├── Account.de.resx │ │ │ ├── Account.en.resx │ │ │ ├── Account.es.resx │ │ │ ├── Account.fr.resx │ │ │ ├── Account.id.resx │ │ │ ├── Account.it.resx │ │ │ ├── Account.km.resx │ │ │ ├── Account.nl.resx │ │ │ ├── Account.ru.resx │ │ │ ├── Account.sv.resx │ │ │ ├── Forgot.ar.resx │ │ │ ├── Forgot.en.resx │ │ │ ├── Forgot.es.resx │ │ │ ├── Forgot.fr.resx │ │ │ ├── Forgot.id.resx │ │ │ ├── Forgot.it.resx │ │ │ ├── Forgot.km.resx │ │ │ ├── Forgot.nl.resx │ │ │ ├── Forgot.ru.resx │ │ │ ├── Forgot.sv.resx │ │ │ ├── Profile.ar.resx │ │ │ ├── Profile.de.resx │ │ │ ├── Profile.en.resx │ │ │ ├── Profile.es.resx │ │ │ ├── Profile.fr.resx │ │ │ ├── Profile.id.resx │ │ │ ├── Profile.it.resx │ │ │ ├── Profile.km.resx │ │ │ ├── Profile.nl.resx │ │ │ ├── Profile.ru.resx │ │ │ ├── Profile.sv.resx │ │ │ ├── RegisterUserModal.ar.resx │ │ │ ├── RegisterUserModal.de.resx │ │ │ ├── RegisterUserModal.en.resx │ │ │ ├── RegisterUserModal.es.resx │ │ │ ├── RegisterUserModal.fr.resx │ │ │ ├── RegisterUserModal.id.resx │ │ │ ├── RegisterUserModal.it.resx │ │ │ ├── RegisterUserModal.km.resx │ │ │ ├── RegisterUserModal.nl.resx │ │ │ ├── RegisterUserModal.ru.resx │ │ │ ├── RegisterUserModal.sv.resx │ │ │ ├── Reset.ar.resx │ │ │ ├── Reset.de.resx │ │ │ ├── Reset.en.resx │ │ │ ├── Reset.es.resx │ │ │ ├── Reset.fr.resx │ │ │ ├── Reset.id.resx │ │ │ ├── Reset.it.resx │ │ │ ├── Reset.km.resx │ │ │ ├── Reset.nl.resx │ │ │ ├── Reset.ru.resx │ │ │ ├── Reset.sv.resx │ │ │ ├── RoleModal.ar.resx │ │ │ ├── RoleModal.de.resx │ │ │ ├── RoleModal.en.resx │ │ │ ├── RoleModal.es.resx │ │ │ ├── RoleModal.fr.resx │ │ │ ├── RoleModal.id.resx │ │ │ ├── RoleModal.it.resx │ │ │ ├── RoleModal.km.resx │ │ │ ├── RoleModal.nl.resx │ │ │ ├── RoleModal.ru.resx │ │ │ ├── RoleModal.sv.resx │ │ │ ├── RolePermissions.ar.resx │ │ │ ├── RolePermissions.en.resx │ │ │ ├── RolePermissions.es.resx │ │ │ ├── RolePermissions.fr.resx │ │ │ ├── RolePermissions.id.resx │ │ │ ├── RolePermissions.it.resx │ │ │ ├── RolePermissions.km.resx │ │ │ ├── RolePermissions.nl.resx │ │ │ ├── RolePermissions.ru.resx │ │ │ ├── RolePermissions.sv.resx │ │ │ ├── Roles.ar.resx │ │ │ ├── Roles.de.resx │ │ │ ├── Roles.en.resx │ │ │ ├── Roles.es.resx │ │ │ ├── Roles.fr.resx │ │ │ ├── Roles.id.resx │ │ │ ├── Roles.it.resx │ │ │ ├── Roles.km.resx │ │ │ ├── Roles.nl.resx │ │ │ ├── Roles.ru.resx │ │ │ ├── Roles.sv.resx │ │ │ ├── Security.ar.resx │ │ │ ├── Security.de.resx │ │ │ ├── Security.en.resx │ │ │ ├── Security.es.resx │ │ │ ├── Security.fr.resx │ │ │ ├── Security.id.resx │ │ │ ├── Security.it.resx │ │ │ ├── Security.km.resx │ │ │ ├── Security.nl.resx │ │ │ ├── Security.ru.resx │ │ │ ├── Security.sv.resx │ │ │ ├── UserProfile.ar.resx │ │ │ ├── UserProfile.de.resx │ │ │ ├── UserProfile.en.resx │ │ │ ├── UserProfile.es.resx │ │ │ ├── UserProfile.fr.resx │ │ │ ├── UserProfile.id.resx │ │ │ ├── UserProfile.it.resx │ │ │ ├── UserProfile.km.resx │ │ │ ├── UserProfile.nl.resx │ │ │ ├── UserProfile.ru.resx │ │ │ ├── UserProfile.sv.resx │ │ │ ├── UserRoles.ar.resx │ │ │ ├── UserRoles.de.resx │ │ │ ├── UserRoles.en.resx │ │ │ ├── UserRoles.es.resx │ │ │ ├── UserRoles.fr.resx │ │ │ ├── UserRoles.id.resx │ │ │ ├── UserRoles.it.resx │ │ │ ├── UserRoles.km.resx │ │ │ ├── UserRoles.nl.resx │ │ │ ├── UserRoles.ru.resx │ │ │ ├── UserRoles.sv.resx │ │ │ ├── Users.ar.resx │ │ │ ├── Users.de.resx │ │ │ ├── Users.en.resx │ │ │ ├── Users.es.resx │ │ │ ├── Users.fr.resx │ │ │ ├── Users.id.resx │ │ │ ├── Users.it.resx │ │ │ ├── Users.km.resx │ │ │ ├── Users.nl.resx │ │ │ ├── Users.ru.resx │ │ │ └── Users.sv.resx │ │ ├── Misc │ │ │ ├── AddEditDocumentModal.ar.resx │ │ │ ├── AddEditDocumentModal.en.resx │ │ │ ├── AddEditDocumentModal.es.resx │ │ │ ├── AddEditDocumentModal.fr.resx │ │ │ ├── AddEditDocumentModal.id.resx │ │ │ ├── AddEditDocumentModal.it.resx │ │ │ ├── AddEditDocumentModal.km.resx │ │ │ ├── AddEditDocumentModal.nl.resx │ │ │ ├── AddEditDocumentModal.ru.resx │ │ │ ├── AddEditDocumentModal.sv.resx │ │ │ ├── AddEditDocumentTypeModal.ar.resx │ │ │ ├── AddEditDocumentTypeModal.en.resx │ │ │ ├── AddEditDocumentTypeModal.es.resx │ │ │ ├── AddEditDocumentTypeModal.fr.resx │ │ │ ├── AddEditDocumentTypeModal.km.resx │ │ │ ├── AddEditDocumentTypeModal.nl.resx │ │ │ ├── AddEditDocumentTypeModal.ru.resx │ │ │ ├── DocumentStore.ar.resx │ │ │ ├── DocumentStore.en.resx │ │ │ ├── DocumentStore.es.resx │ │ │ ├── DocumentStore.fr.resx │ │ │ ├── DocumentStore.id.resx │ │ │ ├── DocumentStore.it.resx │ │ │ ├── DocumentStore.km.resx │ │ │ ├── DocumentStore.nl.resx │ │ │ ├── DocumentStore.ru.resx │ │ │ ├── DocumentStore.sv.resx │ │ │ ├── DocumentTypes.ar.resx │ │ │ ├── DocumentTypes.en.resx │ │ │ ├── DocumentTypes.es.resx │ │ │ ├── DocumentTypes.fr.resx │ │ │ ├── DocumentTypes.km.resx │ │ │ ├── DocumentTypes.nl.resx │ │ │ └── DocumentTypes.ru.resx │ │ └── Utilities │ │ │ ├── AuditTrails.ar.resx │ │ │ ├── AuditTrails.en.resx │ │ │ ├── AuditTrails.es.resx │ │ │ ├── AuditTrails.fr.resx │ │ │ ├── AuditTrails.it.resx │ │ │ ├── AuditTrails.km.resx │ │ │ ├── AuditTrails.nl.resx │ │ │ ├── AuditTrails.ru.resx │ │ │ └── AuditTrails.sv.resx │ └── Shared │ │ ├── Components │ │ ├── AddEditExtendedAttributeModalLocalization.ar.resx │ │ ├── AddEditExtendedAttributeModalLocalization.de.resx │ │ ├── AddEditExtendedAttributeModalLocalization.en.resx │ │ ├── AddEditExtendedAttributeModalLocalization.es.resx │ │ ├── AddEditExtendedAttributeModalLocalization.fr.resx │ │ ├── AddEditExtendedAttributeModalLocalization.id.resx │ │ ├── AddEditExtendedAttributeModalLocalization.it.resx │ │ ├── AddEditExtendedAttributeModalLocalization.km.resx │ │ ├── AddEditExtendedAttributeModalLocalization.nl.resx │ │ ├── AddEditExtendedAttributeModalLocalization.ru.resx │ │ ├── AddEditExtendedAttributeModalLocalization.sv.resx │ │ ├── ExtendedAttributesLocalization.ar.resx │ │ ├── ExtendedAttributesLocalization.de.resx │ │ ├── ExtendedAttributesLocalization.en.resx │ │ ├── ExtendedAttributesLocalization.es.resx │ │ ├── ExtendedAttributesLocalization.fr.resx │ │ ├── ExtendedAttributesLocalization.id.resx │ │ ├── ExtendedAttributesLocalization.it.resx │ │ ├── ExtendedAttributesLocalization.km.resx │ │ ├── ExtendedAttributesLocalization.nl.resx │ │ ├── ExtendedAttributesLocalization.ru.resx │ │ ├── ExtendedAttributesLocalization.sv.resx │ │ ├── ImportExcelModal.ar.resx │ │ ├── ImportExcelModal.de.resx │ │ ├── ImportExcelModal.en.resx │ │ ├── ImportExcelModal.es.resx │ │ ├── ImportExcelModal.fr.resx │ │ ├── ImportExcelModal.id.resx │ │ ├── ImportExcelModal.it.resx │ │ ├── ImportExcelModal.km.resx │ │ ├── ImportExcelModal.nl.resx │ │ ├── ImportExcelModal.ru.resx │ │ ├── ImportExcelModal.sv.resx │ │ ├── LanguageSelector.ar.resx │ │ ├── LanguageSelector.de.resx │ │ ├── LanguageSelector.en.resx │ │ ├── LanguageSelector.es.resx │ │ ├── LanguageSelector.fr.resx │ │ ├── LanguageSelector.id.resx │ │ ├── LanguageSelector.it.resx │ │ ├── LanguageSelector.nl.resx │ │ ├── LanguageSelector.ru.resx │ │ ├── TablePager.ar.resx │ │ ├── TablePager.en.resx │ │ ├── TablePager.es.resx │ │ ├── TablePager.fr.resx │ │ ├── TablePager.id.resx │ │ ├── TablePager.it.resx │ │ ├── TablePager.km.resx │ │ ├── TablePager.nl.resx │ │ ├── TablePager.ru.resx │ │ └── TablePager.sv.resx │ │ ├── Dialogs │ │ ├── DeleteConfirmation.en.resx │ │ ├── DeleteConfirmation.es.resx │ │ ├── DeleteConfirmation.fr.resx │ │ ├── DeleteConfirmation.id.resx │ │ ├── DeleteConfirmation.it.resx │ │ ├── DeleteConfirmation.km.resx │ │ ├── DeleteConfirmation.nl.resx │ │ ├── DeleteConfirmation.ru.resx │ │ ├── DeleteConfirmation.sv.resx │ │ ├── Logout.en.resx │ │ ├── Logout.es.resx │ │ ├── Logout.fr.resx │ │ ├── Logout.id.resx │ │ ├── Logout.it.resx │ │ ├── Logout.km.resx │ │ ├── Logout.nl.resx │ │ ├── Logout.ru.resx │ │ ├── Logout.sv.resx │ │ ├── ‏‏DeleteConfirmation.ar.resx │ │ └── ‏‏Logout.ar.resx │ │ ├── MainLayout.en.resx │ │ ├── MainLayout.es.resx │ │ ├── MainLayout.fr.resx │ │ ├── MainLayout.id.resx │ │ ├── MainLayout.it.resx │ │ ├── MainLayout.km.resx │ │ ├── MainLayout.nl.resx │ │ ├── MainLayout.ru.resx │ │ ├── MainLayout.sv.resx │ │ ├── NavMenu.de.resx │ │ ├── NavMenu.en.resx │ │ ├── NavMenu.es.resx │ │ ├── NavMenu.fr.resx │ │ ├── NavMenu.id.resx │ │ ├── NavMenu.it.resx │ │ ├── NavMenu.km.resx │ │ ├── NavMenu.nl.resx │ │ ├── NavMenu.ru.resx │ │ ├── NavMenu.sv.resx │ │ ├── NotFoundLayout.de.resx │ │ ├── NotFoundLayout.en.resx │ │ ├── NotFoundLayout.es.resx │ │ ├── NotFoundLayout.fr.resx │ │ ├── NotFoundLayout.id.resx │ │ ├── NotFoundLayout.it.resx │ │ ├── NotFoundLayout.km.resx │ │ ├── NotFoundLayout.nl.resx │ │ ├── NotFoundLayout.ru.resx │ │ ├── NotFoundLayout.sv.resx │ │ ├── ‏‏MainLayout.ar.resx │ │ ├── ‏‏NavMenu.ar.resx │ │ └── ‏‏NotFoundLayout.ar.resx ├── Shared │ ├── Components │ │ ├── AddEditExtendedAttributeModal.razor │ │ ├── AddEditExtendedAttributeModal.razor.cs │ │ ├── BlazorHeroLogo.razor │ │ ├── ExtendedAttributes.razor │ │ ├── ExtendedAttributes.razor.cs │ │ ├── ExtendedAttributesBase.razor │ │ ├── ExtendedAttributesBase.razor.cs │ │ ├── HeroTitle.razor │ │ ├── ImportExcelModal.razor │ │ ├── ImportExcelModal.razor.cs │ │ ├── LanguageSelector.razor │ │ ├── TablePager.razor │ │ ├── UserCard.razor │ │ └── UserCard.razor.cs │ ├── Dialogs │ │ ├── DeleteConfirmation.razor │ │ └── Logout.razor │ ├── Error.razor │ ├── MainBody.razor │ ├── MainBody.razor.cs │ ├── MainLayout.razor │ ├── MainLayout.razor.cs │ ├── NavMenu.razor │ └── NotFoundLayout.razor ├── _Imports.razor └── wwwroot │ ├── css │ └── loader.css │ ├── favicon.ico │ ├── index.html │ ├── js │ ├── file.js │ ├── scroll.js │ └── sounds.js │ └── media │ └── notification.mp3 ├── Domain ├── Contracts │ ├── AuditableEntity.cs │ ├── AuditableEntityExtendedAttribute.cs │ ├── AuditableEntityWithExtendedAttributes.cs │ ├── IAuditableEntity.cs │ ├── IEntity.cs │ ├── IEntityAuditableExtendedAttribute.cs │ ├── IEntityExtendedAttribute.cs │ └── IEntityWithExtendedAttributes.cs ├── Domain.csproj ├── Entities │ ├── Catalog │ │ ├── Brand.cs │ │ └── Product.cs │ ├── ExtendedAttributes │ │ └── DocumentExtendedAttribute.cs │ └── Misc │ │ ├── Document.cs │ │ └── DocumentType.cs └── Enums │ └── EntityExtendedAttributeType.cs ├── Infrastructure.Shared ├── Infrastructure.Shared.csproj └── Services │ ├── SMTPMailService.cs │ ├── SendGridMailService.cs │ └── SystemDateTimeService.cs ├── Infrastructure ├── Configurations │ └── EntityExtendedAttributeConfiguration.cs ├── Contexts │ ├── AuditableContext.cs │ └── BlazorHeroContext.cs ├── DatabaseSeeder.cs ├── Extensions │ ├── ServiceCollectionExtensions.cs │ └── ValueConversionExtensions.cs ├── Helpers │ └── ClaimExtensions.cs ├── Infrastructure.csproj ├── Mappings │ ├── AuditProfile.cs │ ├── ChatHistoryProfile.cs │ ├── RoleClaimProfile.cs │ ├── RoleProfile.cs │ └── UserProfile.cs ├── Migrations │ ├── 20210413122110_initial.Designer.cs │ ├── 20210413122110_initial.cs │ ├── 20210526094834_AddBlazorHeroRole.Designer.cs │ ├── 20210526094834_AddBlazorHeroRole.cs │ ├── 20210528104232_AddBlazorHeroRoleClaim.Designer.cs │ ├── 20210528104232_AddBlazorHeroRoleClaim.cs │ ├── 20210613102039_AddDocumentTypeAndExtendedAttribute.Designer.cs │ ├── 20210613102039_AddDocumentTypeAndExtendedAttribute.cs │ └── BlazorHeroContextModelSnapshot.cs ├── Models │ ├── Audit │ │ ├── Audit.cs │ │ └── AuditEntry.cs │ └── Identity │ │ ├── BlazorHeroRole.cs │ │ ├── BlazorHeroRoleClaim.cs │ │ └── BlazorHeroUser.cs ├── Repositories │ ├── BrandRepository.cs │ ├── DocumentRepository.cs │ ├── DocumentTypeRepository.cs │ ├── ExtendedAttributeUnitOfWork.cs │ ├── ProductRepository.cs │ ├── RepositoryAsync.cs │ └── UnitOfWork.cs ├── Resources │ ├── DatabaseSeeder.en.resx │ ├── DatabaseSeeder.es.resx │ ├── DatabaseSeeder.fr.resx │ ├── DatabaseSeeder.it.resx │ ├── DatabaseSeeder.km.resx │ ├── DatabaseSeeder.nl.resx │ ├── DatabaseSeeder.ru.resx │ ├── Services │ │ ├── AuditService.en.resx │ │ ├── AuditService.es.resx │ │ ├── AuditService.fr.resx │ │ ├── AuditService.it.resx │ │ ├── AuditService.km.resx │ │ ├── AuditService.nl.resx │ │ ├── AuditService.ru.resx │ │ ├── ChatService.en.resx │ │ ├── ChatService.es.resx │ │ ├── ChatService.fr.resx │ │ ├── ChatService.it.resx │ │ ├── ChatService.km.resx │ │ ├── ChatService.nl.resx │ │ ├── ChatService.ru.resx │ │ ├── ExcelService.en.resx │ │ ├── ExcelService.es.resx │ │ ├── ExcelService.fr.resx │ │ ├── ExcelService.it.resx │ │ ├── ExcelService.km.resx │ │ ├── ExcelService.nl.resx │ │ ├── ExcelService.ru.resx │ │ ├── Identity │ │ │ ├── AccountService.en.resx │ │ │ ├── AccountService.es.resx │ │ │ ├── AccountService.fr.resx │ │ │ ├── AccountService.it.resx │ │ │ ├── AccountService.km.resx │ │ │ ├── AccountService.nl.resx │ │ │ ├── AccountService.ru.resx │ │ │ ├── IdentityService.en.resx │ │ │ ├── IdentityService.es.resx │ │ │ ├── IdentityService.fr.resx │ │ │ ├── IdentityService.it.resx │ │ │ ├── IdentityService.km.resx │ │ │ ├── IdentityService.nl.resx │ │ │ ├── IdentityService.ru.resx │ │ │ ├── RoleClaimService.de.resx │ │ │ ├── RoleClaimService.en.resx │ │ │ ├── RoleClaimService.es.resx │ │ │ ├── RoleClaimService.fr.resx │ │ │ ├── RoleClaimService.id.resx │ │ │ ├── RoleClaimService.it.resx │ │ │ ├── RoleClaimService.km.resx │ │ │ ├── RoleClaimService.nl.resx │ │ │ ├── RoleClaimService.ru.resx │ │ │ ├── RoleClaimService.sv.resx │ │ │ ├── RoleService.en.resx │ │ │ ├── RoleService.es.resx │ │ │ ├── RoleService.fr.resx │ │ │ ├── RoleService.it.resx │ │ │ ├── RoleService.km.resx │ │ │ ├── RoleService.nl.resx │ │ │ ├── RoleService.ru.resx │ │ │ ├── UserService.en.resx │ │ │ ├── UserService.es.resx │ │ │ ├── UserService.fr.resx │ │ │ ├── UserService.it.resx │ │ │ ├── UserService.km.resx │ │ │ ├── UserService.nl.resx │ │ │ ├── UserService.ru.resx │ │ │ ├── ‏‏AccountService.ar.resx │ │ │ ├── ‏‏IdentityService.ar.resx │ │ │ ├── ‏‏RoleClaimService.ar.resx │ │ │ ├── ‏‏RoleService.ar.resx │ │ │ └── ‏‏UserService.ar.resx │ │ ├── ‏‏AuditService.ar.resx │ │ ├── ‏‏ChatService.ar.resx │ │ └── ‏‏ExcelService.ar.resx │ └── ‏‏DatabaseSeeder.ar.resx ├── Services │ ├── AuditService.cs │ ├── ChatService.cs │ ├── ExcelService.cs │ ├── Identity │ │ ├── AccountService.cs │ │ ├── IdentityService.cs │ │ ├── RoleClaimService.cs │ │ ├── RoleService.cs │ │ └── UserService.cs │ ├── Storage │ │ ├── Provider │ │ │ └── ServerStorageProvider.cs │ │ └── ServerStorageService.cs │ └── UploadService.cs └── Specifications │ ├── AuditFilterSpecification.cs │ └── UserFilterSpecification.cs ├── Server ├── Controllers │ ├── BaseApiController.cs │ ├── Communication │ │ └── ChatsController.cs │ ├── Identity │ │ ├── AccountController.cs │ │ ├── RoleClaimController.cs │ │ ├── RoleController.cs │ │ ├── TokenController.cs │ │ └── UserController.cs │ ├── Utilities │ │ ├── AuditsController.cs │ │ ├── ExtendedAttributes │ │ │ ├── Base │ │ │ │ └── ExtendedAttributesController.cs │ │ │ └── Misc │ │ │ │ └── DocumentExtendedAttributesController.cs │ │ ├── Misc │ │ │ ├── DocumentTypesController.cs │ │ │ └── DocumentsController.cs │ │ └── PreferencesController.cs │ └── v1 │ │ ├── Catalog │ │ ├── BrandsController.cs │ │ └── ProductsController.cs │ │ └── DashboardController.cs ├── Dockerfile ├── Extensions │ ├── ApplicationBuilderExtensions.cs │ ├── HostBuilderExtensions.cs │ ├── MvcBuilderExtensions.cs │ └── ServiceCollectionExtensions.cs ├── Files │ └── Images │ │ └── Products │ │ └── demo.jpg ├── Filters │ └── HangfireAuthorizationFilter.cs ├── Hubs │ └── SignalRHub.cs ├── Localization │ ├── ServerCommonResources.cs │ └── ServerLocalizer.cs ├── Managers │ └── Preferences │ │ ├── IServerPreferenceManager.cs │ │ └── ServerPreferenceManager.cs ├── Middlewares │ ├── ErrorHandlerMiddleware.cs │ └── RequestCultureMiddleware.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Resources │ ├── Localization │ │ ├── ServerCommonResources.en.resx │ │ ├── ServerCommonResources.es.resx │ │ ├── ServerCommonResources.fr.resx │ │ ├── ServerCommonResources.it.resx │ │ ├── ServerCommonResources.km.resx │ │ ├── ServerCommonResources.nl.resx │ │ ├── ServerCommonResources.ru.resx │ │ └── ‏‏ServerCommonResources.ar.resx │ ├── Managers │ │ ├── Preferences │ │ │ ├── ServerPreferenceManager.en.resx │ │ │ ├── ServerPreferenceManager.es.resx │ │ │ ├── ServerPreferenceManager.fr.resx │ │ │ ├── ServerPreferenceManager.it.resx │ │ │ ├── ServerPreferenceManager.km.resx │ │ │ ├── ServerPreferenceManager.nl.resx │ │ │ ├── ServerPreferenceManager.ru.resx │ │ │ └── ‏‏ServerPreferenceManager.ar.resx │ │ ├── Startup.en.resx │ │ ├── Startup.es.resx │ │ ├── Startup.fr.resx │ │ ├── Startup.it.resx │ │ ├── Startup.nl.resx │ │ └── ‏‏Startup.ar.resx │ ├── Startup.en.resx │ ├── Startup.es.resx │ ├── Startup.fr.resx │ ├── Startup.it.resx │ ├── Startup.km.resx │ ├── Startup.nl.resx │ ├── Startup.ru.resx │ └── ‏‏Startup.ar.resx ├── Server.csproj ├── Services │ └── CurrentUserService.cs ├── Settings │ └── ServerPreference.cs ├── Startup.cs ├── appsettings.Development.json └── appsettings.json └── Shared ├── Constants ├── Application │ └── ApplicationConstants.cs ├── Localization │ ├── LanguageCode.cs │ └── LocalizationConstants.cs ├── Permission │ ├── ApplicationClaimType.cs │ └── Permissions.cs ├── Role │ └── RoleConstants.cs ├── Storage │ └── StorageConstants.cs └── User │ └── UserConstants.cs ├── Managers └── IPreferenceManager.cs ├── Models └── User.cs ├── Settings └── IPreference.cs ├── Shared.csproj └── Wrapper ├── IResult.cs ├── PaginatedResult.cs └── Result.cs /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: ['https://www.buymeacoffee.com/codewithmukesh'] 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documenation-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Documenation Request 3 | about: Create a report to help us improve. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe your documentation need** 11 | A clear and concise description of what the request is. 12 | 13 | **Requirement** 14 | 15 | 1. Documentation Title 16 | 2. What you need to achieve? 17 | 3. Current Blockers? etc 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expect. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Desktop (please complete the following information):** 26 | - OS: [e.g. iOS] 27 | - Browser [e.g. chrome, safari] 28 | - Version [e.g. 22] 29 | 30 | **Smartphone (please complete the following information):** 31 | - Device: [e.g. iPhone6] 32 | - OS: [e.g. iOS8.1] 33 | - Browser [e.g. stock browser, safari] 34 | - Version [e.g. 22] 35 | 36 | **Additional context** 37 | Add any other context about the problem here. 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: .NET 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Setup .NET 17 | uses: actions/setup-dotnet@v1 18 | with: 19 | dotnet-version: 6.0.x 20 | - name: Restore dependencies 21 | run: dotnet restore 22 | working-directory: src/Server 23 | - name: Build 24 | run: dotnet build --no-restore 25 | working-directory: src/Server 26 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch and Debug Standalone Blazor WebAssembly App", 6 | "type": "blazorwasm", 7 | "request": "launch", 8 | "cwd": "${workspaceFolder}/src/Server" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "build", 8 | "command": "dotnet", 9 | "type": "shell", 10 | "args": [ 11 | "build", 12 | "${workspaceFolder}/BlazorHero.CleanArchitecture.sln", 13 | // Ask dotnet build to generate full paths for file names. 14 | "/property:GenerateFullPaths=true", 15 | // Do not generate summary otherwise it leads to duplicate errors in Problems panel 16 | "/consoleloggerparameters:NoSummary" 17 | ], 18 | "group": "build", 19 | "presentation": { 20 | "reveal": "silent" 21 | }, 22 | "problemMatcher": "$msCompile" 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 blazorhero / codewithmukesh / Mukesh Murugan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docker-compose.dcproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2.1 5 | Linux 6 | 161b234c-6018-4ce5-86b2-0ea95a53982d 7 | LaunchBrowser 8 | {Scheme}://localhost:{ServicePort} 9 | server 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | services: 4 | blazorhero: 5 | image: ${DOCKER_REGISTRY-}blazorhero 6 | build: 7 | context: . 8 | dockerfile: src/Server/Dockerfile 9 | environment: 10 | - "ConnectionStrings__DefaultConnection=Server=db;Database=BlazorHero;User=sa;Password=Your_password123;MultipleActiveResultSets=true" 11 | - "ASPNETCORE_Kestrel__Certificates__Default__Password=securePassword123" 12 | - "ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx" 13 | volumes: 14 | - ~/.aspnet/https:/https:ro 15 | ports: 16 | - "5005:5005" 17 | - "5006:5006" 18 | depends_on: 19 | - db 20 | restart: on-failure 21 | container_name: blazorhero 22 | 23 | db: 24 | image: "mcr.microsoft.com/mssql/server" 25 | environment: 26 | - "SA_PASSWORD=Your_password123" 27 | - "ACCEPT_EULA=Y" 28 | container_name: mssql -------------------------------------------------------------------------------- /src/Application/Application.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | BlazorHero.CleanArchitecture.Application 6 | BlazorHero.CleanArchitecture.Application 7 | latest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/Application/Configurations/AppConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Configurations 2 | { 3 | public class AppConfiguration 4 | { 5 | public string Secret { get; set; } 6 | 7 | public bool BehindSSLProxy { get; set; } 8 | 9 | public string ProxyIP { get; set; } 10 | 11 | public string ApplicationUrl { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Application/Configurations/MailConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Configurations 2 | { 3 | public class MailConfiguration 4 | { 5 | public string From { get; set; } 6 | public string Host { get; set; } 7 | public int Port { get; set; } 8 | public string UserName { get; set; } 9 | public string Password { get; set; } 10 | public string DisplayName { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Application/Enums/AuditType.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Enums 2 | { 3 | public enum AuditType : byte 4 | { 5 | None = 0, 6 | Create = 1, 7 | Update = 2, 8 | Delete = 3 9 | } 10 | } -------------------------------------------------------------------------------- /src/Application/Enums/UploadType.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Enums 4 | { 5 | public enum UploadType : byte 6 | { 7 | [Description(@"Images\Products")] 8 | Product, 9 | 10 | [Description(@"Images\ProfilePictures")] 11 | ProfilePicture, 12 | 13 | [Description(@"Documents")] 14 | Document 15 | } 16 | } -------------------------------------------------------------------------------- /src/Application/Exceptions/ApiException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | namespace BlazorHero.CleanArchitecture.Application.Exceptions 5 | { 6 | public class ApiException : Exception 7 | { 8 | public ApiException() : base() 9 | { 10 | } 11 | 12 | public ApiException(string message) : base(message) 13 | { 14 | } 15 | 16 | public ApiException(string message, params object[] args) 17 | : base(string.Format(CultureInfo.CurrentCulture, message, args)) 18 | { 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Application/Extensions/EnumExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace BlazorHero.CleanArchitecture.Application.Extensions 5 | { 6 | public static class EnumExtensions 7 | { 8 | public static string ToDescriptionString(this Enum val) 9 | { 10 | var attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString())?.GetCustomAttributes(typeof(DescriptionAttribute), false); 11 | 12 | return attributes?.Length > 0 13 | ? attributes[0].Description 14 | : val.ToString(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Application/Extensions/ExpressionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | namespace BlazorHero.CleanArchitecture.Application.Extensions 7 | { 8 | public static class PredicateBuilder 9 | { 10 | public static Expression> And(this Expression> left, Expression> right) 11 | { 12 | ParameterExpression p = left.Parameters.First(); 13 | SubstExpressionVisitor visitor = new SubstExpressionVisitor 14 | { 15 | Subst = {[right.Parameters.First()] = p} 16 | }; 17 | 18 | Expression body = Expression.AndAlso(left.Body, visitor.Visit(right.Body)); 19 | return Expression.Lambda>(body, p); 20 | } 21 | 22 | public static Expression> Or(this Expression> left, Expression> right) 23 | { 24 | 25 | ParameterExpression p = left.Parameters.First(); 26 | SubstExpressionVisitor visitor = new SubstExpressionVisitor 27 | { 28 | Subst = {[right.Parameters.First()] = p} 29 | }; 30 | 31 | Expression body = Expression.OrElse(left.Body, visitor.Visit(right.Body)); 32 | return Expression.Lambda>(body, p); 33 | } 34 | } 35 | 36 | internal class SubstExpressionVisitor : ExpressionVisitor 37 | { 38 | public Dictionary Subst = new(); 39 | 40 | protected override Expression VisitParameter(ParameterExpression node) 41 | { 42 | if (Subst.TryGetValue(node, out var newValue)) 43 | { 44 | return newValue; 45 | } 46 | return node; 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/Application/Extensions/QueryableExtensions.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Exceptions; 2 | using BlazorHero.CleanArchitecture.Application.Specifications.Base; 3 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 4 | using Microsoft.EntityFrameworkCore; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using BlazorHero.CleanArchitecture.Domain.Contracts; 9 | 10 | namespace BlazorHero.CleanArchitecture.Application.Extensions 11 | { 12 | public static class QueryableExtensions 13 | { 14 | public static async Task> ToPaginatedListAsync(this IQueryable source, int pageNumber, int pageSize) where T : class 15 | { 16 | if (source == null) throw new ApiException(); 17 | pageNumber = pageNumber == 0 ? 1 : pageNumber; 18 | pageSize = pageSize == 0 ? 10 : pageSize; 19 | int count = await source.CountAsync(); 20 | pageNumber = pageNumber <= 0 ? 1 : pageNumber; 21 | List items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync(); 22 | return PaginatedResult.Success(items, count, pageNumber, pageSize); 23 | } 24 | 25 | public static IQueryable Specify(this IQueryable query, ISpecification spec) where T : class, IEntity 26 | { 27 | var queryableResultWithIncludes = spec.Includes 28 | .Aggregate(query, 29 | (current, include) => current.Include(include)); 30 | var secondaryResult = spec.IncludeStrings 31 | .Aggregate(queryableResultWithIncludes, 32 | (current, include) => current.Include(include)); 33 | return secondaryResult.Where(spec.Criteria); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/Application/Features/Brands/Queries/GetAll/GetAllBrandsQuery.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BlazorHero.CleanArchitecture.Application.Interfaces.Repositories; 3 | using BlazorHero.CleanArchitecture.Domain.Entities.Catalog; 4 | using BlazorHero.CleanArchitecture.Shared.Constants.Application; 5 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 6 | using LazyCache; 7 | using MediatR; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | 13 | namespace BlazorHero.CleanArchitecture.Application.Features.Brands.Queries.GetAll 14 | { 15 | public class GetAllBrandsQuery : IRequest>> 16 | { 17 | public GetAllBrandsQuery() 18 | { 19 | } 20 | } 21 | 22 | internal class GetAllBrandsCachedQueryHandler : IRequestHandler>> 23 | { 24 | private readonly IUnitOfWork _unitOfWork; 25 | private readonly IMapper _mapper; 26 | private readonly IAppCache _cache; 27 | 28 | public GetAllBrandsCachedQueryHandler(IUnitOfWork unitOfWork, IMapper mapper, IAppCache cache) 29 | { 30 | _unitOfWork = unitOfWork; 31 | _mapper = mapper; 32 | _cache = cache; 33 | } 34 | 35 | public async Task>> Handle(GetAllBrandsQuery request, CancellationToken cancellationToken) 36 | { 37 | Func>> getAllBrands = () => _unitOfWork.Repository().GetAllAsync(); 38 | var brandList = await _cache.GetOrAddAsync(ApplicationConstants.Cache.GetAllBrandsCacheKey, getAllBrands); 39 | var mappedBrands = _mapper.Map>(brandList); 40 | return await Result>.SuccessAsync(mappedBrands); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/Application/Features/Brands/Queries/GetAll/GetAllBrandsResponse.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Features.Brands.Queries.GetAll 2 | { 3 | public class GetAllBrandsResponse 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } 7 | public string Description { get; set; } 8 | public decimal Tax { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Application/Features/Brands/Queries/GetById/GetBrandByIdQuery.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BlazorHero.CleanArchitecture.Application.Interfaces.Repositories; 3 | using BlazorHero.CleanArchitecture.Domain.Entities.Catalog; 4 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 5 | using MediatR; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace BlazorHero.CleanArchitecture.Application.Features.Brands.Queries.GetById 10 | { 11 | public class GetBrandByIdQuery : IRequest> 12 | { 13 | public int Id { get; set; } 14 | } 15 | 16 | internal class GetProductByIdQueryHandler : IRequestHandler> 17 | { 18 | private readonly IUnitOfWork _unitOfWork; 19 | private readonly IMapper _mapper; 20 | 21 | public GetProductByIdQueryHandler(IUnitOfWork unitOfWork, IMapper mapper) 22 | { 23 | _unitOfWork = unitOfWork; 24 | _mapper = mapper; 25 | } 26 | 27 | public async Task> Handle(GetBrandByIdQuery query, CancellationToken cancellationToken) 28 | { 29 | var brand = await _unitOfWork.Repository().GetByIdAsync(query.Id); 30 | var mappedBrand = _mapper.Map(brand); 31 | return await Result.SuccessAsync(mappedBrand); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Application/Features/Brands/Queries/GetById/GetBrandByIdResponse.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Features.Brands.Queries.GetById 2 | { 3 | public class GetBrandByIdResponse 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } 7 | public decimal Tax { get; set; } 8 | public string Description { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Application/Features/Dashboards/Queries/GetData/DashboardDataResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Features.Dashboards.Queries.GetData 4 | { 5 | public class DashboardDataResponse 6 | { 7 | public int ProductCount { get; set; } 8 | public int BrandCount { get; set; } 9 | public int DocumentCount { get; set; } 10 | public int DocumentTypeCount { get; set; } 11 | public int DocumentExtendedAttributeCount { get; set; } 12 | public int UserCount { get; set; } 13 | public int RoleCount { get; set; } 14 | public List DataEnterBarChart { get; set; } = new(); 15 | public Dictionary ProductByBrandTypePieChart { get; set; } 16 | } 17 | 18 | public class ChartSeries 19 | { 20 | public ChartSeries() { } 21 | 22 | public string Name { get; set; } 23 | public double[] Data { get; set; } 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /src/Application/Features/DocumentTypes/Queries/GetAll/GetAllDocumentTypesResponse.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Features.DocumentTypes.Queries.GetAll 2 | { 3 | public class GetAllDocumentTypesResponse 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } 7 | public string Description { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Application/Features/DocumentTypes/Queries/GetById/GetDocumentTypeByIdQuery.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using AutoMapper; 4 | using BlazorHero.CleanArchitecture.Application.Interfaces.Repositories; 5 | using BlazorHero.CleanArchitecture.Domain.Entities.Misc; 6 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 7 | using MediatR; 8 | 9 | namespace BlazorHero.CleanArchitecture.Application.Features.DocumentTypes.Queries.GetById 10 | { 11 | public class GetDocumentTypeByIdQuery : IRequest> 12 | { 13 | public int Id { get; set; } 14 | } 15 | 16 | internal class GetDocumentTypeByIdQueryHandler : IRequestHandler> 17 | { 18 | private readonly IUnitOfWork _unitOfWork; 19 | private readonly IMapper _mapper; 20 | 21 | public GetDocumentTypeByIdQueryHandler(IUnitOfWork unitOfWork, IMapper mapper) 22 | { 23 | _unitOfWork = unitOfWork; 24 | _mapper = mapper; 25 | } 26 | 27 | public async Task> Handle(GetDocumentTypeByIdQuery query, CancellationToken cancellationToken) 28 | { 29 | var documentType = await _unitOfWork.Repository().GetByIdAsync(query.Id); 30 | var mappedDocumentType = _mapper.Map(documentType); 31 | return await Result.SuccessAsync(mappedDocumentType); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Application/Features/DocumentTypes/Queries/GetById/GetDocumentTypeByIdResponse.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Features.DocumentTypes.Queries.GetById 2 | { 3 | public class GetDocumentTypeByIdResponse 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } 7 | public string Description { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Application/Features/Documents/Queries/GetAll/GetAllDocumentsResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Features.Documents.Queries.GetAll 4 | { 5 | public class GetAllDocumentsResponse 6 | { 7 | public int Id { get; set; } 8 | public string Title { get; set; } 9 | public string Description { get; set; } 10 | public bool IsPublic { get; set; } 11 | public string CreatedBy { get; set; } 12 | public DateTime CreatedOn { get; set; } 13 | public string URL { get; set; } 14 | public string DocumentType { get; set; } 15 | public int DocumentTypeId { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Application/Features/Documents/Queries/GetById/GetDocumentByIdQuery.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using AutoMapper; 4 | using BlazorHero.CleanArchitecture.Application.Interfaces.Repositories; 5 | using BlazorHero.CleanArchitecture.Domain.Entities.Misc; 6 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 7 | using MediatR; 8 | 9 | namespace BlazorHero.CleanArchitecture.Application.Features.Documents.Queries.GetById 10 | { 11 | public class GetDocumentByIdQuery : IRequest> 12 | { 13 | public int Id { get; set; } 14 | } 15 | 16 | internal class GetDocumentByIdQueryHandler : IRequestHandler> 17 | { 18 | private readonly IUnitOfWork _unitOfWork; 19 | private readonly IMapper _mapper; 20 | 21 | public GetDocumentByIdQueryHandler(IUnitOfWork unitOfWork, IMapper mapper) 22 | { 23 | _unitOfWork = unitOfWork; 24 | _mapper = mapper; 25 | } 26 | 27 | public async Task> Handle(GetDocumentByIdQuery query, CancellationToken cancellationToken) 28 | { 29 | var document = await _unitOfWork.Repository().GetByIdAsync(query.Id); 30 | var mappedDocument = _mapper.Map(document); 31 | return await Result.SuccessAsync(mappedDocument); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Application/Features/Documents/Queries/GetById/GetDocumentByIdResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Features.Documents.Queries.GetById 4 | { 5 | public class GetDocumentByIdResponse 6 | { 7 | public int Id { get; set; } 8 | public string Title { get; set; } 9 | public string Description { get; set; } 10 | public bool IsPublic { get; set; } 11 | public string CreatedBy { get; set; } 12 | public DateTime CreatedOn { get; set; } 13 | public string URL { get; set; } 14 | public string DocumentType { get; set; } 15 | public int DocumentTypeId { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Application/Features/Products/Commands/Delete/DeleteProductCommand.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Interfaces.Repositories; 2 | using BlazorHero.CleanArchitecture.Domain.Entities.Catalog; 3 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 4 | using MediatR; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using Microsoft.Extensions.Localization; 8 | 9 | namespace BlazorHero.CleanArchitecture.Application.Features.Products.Commands.Delete 10 | { 11 | public class DeleteProductCommand : IRequest> 12 | { 13 | public int Id { get; set; } 14 | } 15 | 16 | internal class DeleteProductCommandHandler : IRequestHandler> 17 | { 18 | private readonly IUnitOfWork _unitOfWork; 19 | private readonly IStringLocalizer _localizer; 20 | 21 | public DeleteProductCommandHandler(IUnitOfWork unitOfWork, IStringLocalizer localizer) 22 | { 23 | _unitOfWork = unitOfWork; 24 | _localizer = localizer; 25 | } 26 | 27 | public async Task> Handle(DeleteProductCommand command, CancellationToken cancellationToken) 28 | { 29 | var product = await _unitOfWork.Repository().GetByIdAsync(command.Id); 30 | if (product != null) 31 | { 32 | await _unitOfWork.Repository().DeleteAsync(product); 33 | await _unitOfWork.Commit(cancellationToken); 34 | return await Result.SuccessAsync(product.Id, _localizer["Product Deleted"]); 35 | } 36 | else 37 | { 38 | return await Result.FailAsync(_localizer["Product Not Found!"]); 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/Application/Features/Products/Queries/GetAllPaged/GetAllPagedProductsResponse.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Features.Products.Queries.GetAllPaged 2 | { 3 | public class GetAllPagedProductsResponse 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } 7 | public string Barcode { get; set; } 8 | public string Description { get; set; } 9 | public decimal Rate { get; set; } 10 | public string Brand { get; set; } 11 | public int BrandId { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Application/Features/Products/Queries/GetProductImage/GetProductImageQuery.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Interfaces.Repositories; 2 | using BlazorHero.CleanArchitecture.Domain.Entities.Catalog; 3 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 4 | using MediatR; 5 | using Microsoft.EntityFrameworkCore; 6 | using System.Linq; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace BlazorHero.CleanArchitecture.Application.Features.Products.Queries.GetProductImage 11 | { 12 | public class GetProductImageQuery : IRequest> 13 | { 14 | public int Id { get; set; } 15 | 16 | public GetProductImageQuery(int productId) 17 | { 18 | Id = productId; 19 | } 20 | } 21 | 22 | internal class GetProductImageQueryHandler : IRequestHandler> 23 | { 24 | private readonly IUnitOfWork _unitOfWork; 25 | 26 | public GetProductImageQueryHandler(IUnitOfWork unitOfWork) 27 | { 28 | _unitOfWork = unitOfWork; 29 | } 30 | 31 | public async Task> Handle(GetProductImageQuery request, CancellationToken cancellationToken) 32 | { 33 | var data = await _unitOfWork.Repository().Entities.Where(p => p.Id == request.Id).Select(a => a.ImageDataURL).FirstOrDefaultAsync(cancellationToken); 34 | return await Result.SuccessAsync(data: data); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Application/Features/Products/Queries/GetProductImage/GetProductImageResponse.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Features.Products.Queries.GetProductImage 2 | { 3 | public class GetProductImageResponse 4 | { 5 | public string ImageDataURL { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Chat/IChatHistory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Chat 4 | { 5 | public interface IChatHistory where TUser : IChatUser 6 | { 7 | public long Id { get; set; } 8 | public string FromUserId { get; set; } 9 | public string ToUserId { get; set; } 10 | public string Message { get; set; } 11 | public DateTime CreatedDate { get; set; } 12 | public TUser FromUser { get; set; } 13 | public TUser ToUser { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Chat/IChatUser.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations.Schema; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Chat 4 | { 5 | public interface IChatUser 6 | { 7 | public string FirstName { get; set; } 8 | 9 | public string LastName { get; set; } 10 | 11 | [Column(TypeName = "text")] 12 | public string ProfilePictureDataUrl { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Common/IScopedService.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Common 2 | { 3 | public interface IScopedService 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Common/IService.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Common 2 | { 3 | public interface IService 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Common/ISingletonService.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Common 2 | { 3 | public interface ISingletonService 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Repositories/IBrandRepository.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Repositories 2 | { 3 | public interface IBrandRepository 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Repositories/IDocumentRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Repositories 4 | { 5 | public interface IDocumentRepository 6 | { 7 | Task IsDocumentTypeUsed(int documentTypeId); 8 | 9 | Task IsDocumentExtendedAttributeUsed(int documentExtendedAttributeId); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Repositories/IDocumentTypeRepository.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Repositories 2 | { 3 | public interface IDocumentTypeRepository 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Repositories/IExtendedAttributeUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using BlazorHero.CleanArchitecture.Domain.Contracts; 5 | 6 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Repositories 7 | { 8 | public interface IExtendedAttributeUnitOfWork : IDisposable where TEntity : AuditableEntity 9 | { 10 | IRepositoryAsync Repository() where T : AuditableEntityExtendedAttribute; 11 | 12 | Task Commit(CancellationToken cancellationToken); 13 | 14 | Task CommitAndRemoveCache(CancellationToken cancellationToken, params string[] cacheKeys); 15 | 16 | Task Rollback(); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Repositories/IProductRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Repositories 4 | { 5 | public interface IProductRepository 6 | { 7 | Task IsBrandUsed(int brandId); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Repositories/IRepositoryAsync.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using BlazorHero.CleanArchitecture.Domain.Contracts; 5 | 6 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Repositories 7 | { 8 | public interface IRepositoryAsync where T : class, IEntity 9 | { 10 | IQueryable Entities { get; } 11 | 12 | Task GetByIdAsync(TId id); 13 | 14 | Task> GetAllAsync(); 15 | 16 | Task> GetPagedResponseAsync(int pageNumber, int pageSize); 17 | 18 | Task AddAsync(T entity); 19 | 20 | Task UpdateAsync(T entity); 21 | 22 | Task DeleteAsync(T entity); 23 | } 24 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Repositories/IUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Domain.Contracts; 2 | using System; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Repositories 7 | { 8 | public interface IUnitOfWork : IDisposable 9 | { 10 | IRepositoryAsync Repository() where T : AuditableEntity; 11 | 12 | Task Commit(CancellationToken cancellationToken); 13 | 14 | Task CommitAndRemoveCache(CancellationToken cancellationToken, params string[] cacheKeys); 15 | 16 | Task Rollback(); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Serialization/Options/IJsonSerializerOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Serialization.Options 4 | { 5 | public interface IJsonSerializerOptions 6 | { 7 | /// 8 | /// Options for . 9 | /// 10 | public JsonSerializerOptions JsonSerializerOptions { get; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Serialization/Serializers/IJsonSerializer.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Serialization.Serializers 2 | { 3 | public interface IJsonSerializer 4 | { 5 | string Serialize(T obj); 6 | T Deserialize(string text); 7 | } 8 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Serialization/Settings/IJsonSerializerSettings.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Serialization.Settings 4 | { 5 | public interface IJsonSerializerSettings 6 | { 7 | /// 8 | /// Settings for . 9 | /// 10 | public JsonSerializerSettings JsonSerializerSettings { get; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Services/Account/IAccountService.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Interfaces.Common; 2 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 3 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 4 | using System.Threading.Tasks; 5 | 6 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Services.Account 7 | { 8 | public interface IAccountService : IService 9 | { 10 | Task UpdateProfileAsync(UpdateProfileRequest model, string userId); 11 | 12 | Task ChangePasswordAsync(ChangePasswordRequest model, string userId); 13 | 14 | Task> GetProfilePictureAsync(string userId); 15 | 16 | Task> UpdateProfilePictureAsync(UpdateProfilePictureRequest request, string userId); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Services/IAuditService.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Responses.Audit; 2 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Services 7 | { 8 | public interface IAuditService 9 | { 10 | Task>> GetCurrentUserTrailsAsync(string userId); 11 | 12 | Task> ExportToExcelAsync(string userId, string searchString = "", bool searchInOldValues = false, bool searchInNewValues = false); 13 | } 14 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Services/IChatService.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Responses.Identity; 2 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using BlazorHero.CleanArchitecture.Application.Interfaces.Chat; 6 | using BlazorHero.CleanArchitecture.Application.Models.Chat; 7 | 8 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Services 9 | { 10 | public interface IChatService 11 | { 12 | Task>> GetChatUsersAsync(string userId); 13 | 14 | Task SaveMessageAsync(ChatHistory message); 15 | 16 | Task>> GetChatHistoryAsync(string userId, string contactId); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Services/ICurrentUserService.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Interfaces.Common; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Services 4 | { 5 | public interface ICurrentUserService : IService 6 | { 7 | string UserId { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Services/IDatabaseSeeder.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Services 2 | { 3 | public interface IDatabaseSeeder 4 | { 5 | void Initialize(); 6 | } 7 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Services/IDateTimeService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Services 4 | { 5 | public interface IDateTimeService 6 | { 7 | DateTime NowUtc { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Services/IExcelService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.IO; 5 | using System.Threading.Tasks; 6 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 7 | 8 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Services 9 | { 10 | public interface IExcelService 11 | { 12 | Task ExportAsync(IEnumerable data 13 | , Dictionary> mappers 14 | , string sheetName = "Sheet1"); 15 | 16 | Task>> ImportAsync(Stream data 17 | , Dictionary> mappers 18 | , string sheetName = "Sheet1"); 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Services/IMailService.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Requests.Mail; 2 | using System.Threading.Tasks; 3 | 4 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Services 5 | { 6 | public interface IMailService 7 | { 8 | Task SendAsync(MailRequest request); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Services/IUploadService.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Requests; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Services 4 | { 5 | public interface IUploadService 6 | { 7 | string UploadAsync(UploadRequest request); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Services/Identity/IRoleClaimService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using BlazorHero.CleanArchitecture.Application.Interfaces.Common; 4 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 5 | using BlazorHero.CleanArchitecture.Application.Responses.Identity; 6 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 7 | 8 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Services.Identity 9 | { 10 | public interface IRoleClaimService : IService 11 | { 12 | Task>> GetAllAsync(); 13 | 14 | Task GetCountAsync(); 15 | 16 | Task> GetByIdAsync(int id); 17 | 18 | Task>> GetAllByRoleIdAsync(string roleId); 19 | 20 | Task> SaveAsync(RoleClaimRequest request); 21 | 22 | Task> DeleteAsync(int id); 23 | } 24 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Services/Identity/IRoleService.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Interfaces.Common; 2 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 3 | using BlazorHero.CleanArchitecture.Application.Responses.Identity; 4 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | 8 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Services.Identity 9 | { 10 | public interface IRoleService : IService 11 | { 12 | Task>> GetAllAsync(); 13 | 14 | Task GetCountAsync(); 15 | 16 | Task> GetByIdAsync(string id); 17 | 18 | Task> SaveAsync(RoleRequest request); 19 | 20 | Task> DeleteAsync(string id); 21 | 22 | Task> GetAllPermissionsAsync(string roleId); 23 | 24 | Task> UpdatePermissionsAsync(PermissionRequest request); 25 | } 26 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Services/Identity/ITokenService.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Interfaces.Common; 2 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 3 | using BlazorHero.CleanArchitecture.Application.Responses.Identity; 4 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 5 | using System.Threading.Tasks; 6 | 7 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Services.Identity 8 | { 9 | public interface ITokenService : IService 10 | { 11 | Task> LoginAsync(TokenRequest model); 12 | 13 | Task> GetRefreshTokenAsync(RefreshTokenRequest model); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Services/Identity/IUserService.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Interfaces.Common; 2 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 3 | using BlazorHero.CleanArchitecture.Application.Responses.Identity; 4 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | 8 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Services.Identity 9 | { 10 | public interface IUserService : IService 11 | { 12 | Task>> GetAllAsync(); 13 | 14 | Task GetCountAsync(); 15 | 16 | Task> GetAsync(string userId); 17 | 18 | Task RegisterAsync(RegisterRequest request, string origin); 19 | 20 | Task ToggleUserStatusAsync(ToggleUserStatusRequest request); 21 | 22 | Task> GetRolesAsync(string id); 23 | 24 | Task UpdateRolesAsync(UpdateUserRolesRequest request); 25 | 26 | Task> ConfirmEmailAsync(string userId, string code); 27 | 28 | Task ForgotPasswordAsync(ForgotPasswordRequest request, string origin); 29 | 30 | Task ResetPasswordAsync(ResetPasswordRequest request); 31 | 32 | Task ExportToExcelAsync(string searchString = ""); 33 | } 34 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Services/Storage/ChangedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Services.Storage 4 | { 5 | [ExcludeFromCodeCoverage] 6 | public class ChangedEventArgs 7 | { 8 | public string Key { get; set; } 9 | public object OldValue { get; set; } 10 | public object NewValue { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Services/Storage/ChangingEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Services.Storage 4 | { 5 | [ExcludeFromCodeCoverage] 6 | public class ChangingEventArgs : ChangedEventArgs 7 | { 8 | public bool Cancel { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Application/Interfaces/Services/Storage/Provider/IServerStorageProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Interfaces.Services.Storage.Provider 4 | { 5 | public interface IStorageProvider 6 | { 7 | void Clear(); 8 | ValueTask ClearAsync(); 9 | bool ContainKey(string key); 10 | ValueTask ContainKeyAsync(string key); 11 | string GetItem(string key); 12 | ValueTask GetItemAsync(string key); 13 | string Key(int index); 14 | ValueTask KeyAsync(int index); 15 | int Length(); 16 | ValueTask LengthAsync(); 17 | void RemoveItem(string key); 18 | ValueTask RemoveItemAsync(string key); 19 | void SetItem(string key, string data); 20 | ValueTask SetItemAsync(string key, string data); 21 | } 22 | } -------------------------------------------------------------------------------- /src/Application/Mappings/BrandProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BlazorHero.CleanArchitecture.Application.Features.Brands.Commands.AddEdit; 3 | using BlazorHero.CleanArchitecture.Application.Features.Brands.Queries.GetAll; 4 | using BlazorHero.CleanArchitecture.Application.Features.Brands.Queries.GetById; 5 | using BlazorHero.CleanArchitecture.Domain.Entities.Catalog; 6 | 7 | namespace BlazorHero.CleanArchitecture.Application.Mappings 8 | { 9 | public class BrandProfile : Profile 10 | { 11 | public BrandProfile() 12 | { 13 | CreateMap().ReverseMap(); 14 | CreateMap().ReverseMap(); 15 | CreateMap().ReverseMap(); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Application/Mappings/DocumentProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BlazorHero.CleanArchitecture.Application.Features.Documents.Commands.AddEdit; 3 | using BlazorHero.CleanArchitecture.Application.Features.Documents.Queries.GetById; 4 | using BlazorHero.CleanArchitecture.Domain.Entities.Misc; 5 | 6 | namespace BlazorHero.CleanArchitecture.Application.Mappings 7 | { 8 | public class DocumentProfile : Profile 9 | { 10 | public DocumentProfile() 11 | { 12 | CreateMap().ReverseMap(); 13 | CreateMap().ReverseMap(); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Application/Mappings/DocumentTypeProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BlazorHero.CleanArchitecture.Application.Features.DocumentTypes.Commands.AddEdit; 3 | using BlazorHero.CleanArchitecture.Application.Features.DocumentTypes.Queries.GetAll; 4 | using BlazorHero.CleanArchitecture.Application.Features.DocumentTypes.Queries.GetById; 5 | using BlazorHero.CleanArchitecture.Domain.Entities.Misc; 6 | 7 | namespace BlazorHero.CleanArchitecture.Application.Mappings 8 | { 9 | public class DocumentTypeProfile : Profile 10 | { 11 | public DocumentTypeProfile() 12 | { 13 | CreateMap().ReverseMap(); 14 | CreateMap().ReverseMap(); 15 | CreateMap().ReverseMap(); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Application/Mappings/ExtendedAttributeProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BlazorHero.CleanArchitecture.Application.Features.ExtendedAttributes.Commands.AddEdit; 3 | using BlazorHero.CleanArchitecture.Application.Features.ExtendedAttributes.Queries.GetAll; 4 | using BlazorHero.CleanArchitecture.Application.Features.ExtendedAttributes.Queries.GetAllByEntityId; 5 | using BlazorHero.CleanArchitecture.Application.Features.ExtendedAttributes.Queries.GetById; 6 | using BlazorHero.CleanArchitecture.Domain.Entities.ExtendedAttributes; 7 | 8 | namespace BlazorHero.CleanArchitecture.Application.Mappings 9 | { 10 | public class ExtendedAttributeProfile : Profile 11 | { 12 | public ExtendedAttributeProfile() 13 | { 14 | CreateMap(typeof(AddEditExtendedAttributeCommand<,,,>), typeof(DocumentExtendedAttribute)) 15 | .ForMember(nameof(DocumentExtendedAttribute.Entity), opt => opt.Ignore()) 16 | .ForMember(nameof(DocumentExtendedAttribute.CreatedBy), opt => opt.Ignore()) 17 | .ForMember(nameof(DocumentExtendedAttribute.CreatedOn), opt => opt.Ignore()) 18 | .ForMember(nameof(DocumentExtendedAttribute.LastModifiedBy), opt => opt.Ignore()) 19 | .ForMember(nameof(DocumentExtendedAttribute.LastModifiedOn), opt => opt.Ignore()); 20 | 21 | CreateMap(typeof(GetExtendedAttributeByIdResponse<,>), typeof(DocumentExtendedAttribute)).ReverseMap(); 22 | CreateMap(typeof(GetAllExtendedAttributesResponse<,>), typeof(DocumentExtendedAttribute)).ReverseMap(); 23 | CreateMap(typeof(GetAllExtendedAttributesByEntityIdResponse<,>), typeof(DocumentExtendedAttribute)).ReverseMap(); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Application/Mappings/ProductProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BlazorHero.CleanArchitecture.Application.Features.Products.Commands.AddEdit; 3 | using BlazorHero.CleanArchitecture.Domain.Entities.Catalog; 4 | 5 | namespace BlazorHero.CleanArchitecture.Application.Mappings 6 | { 7 | public class ProductProfile : Profile 8 | { 9 | public ProductProfile() 10 | { 11 | CreateMap().ReverseMap(); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Application/Models/Chat/ChatHistory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BlazorHero.CleanArchitecture.Application.Interfaces.Chat; 3 | 4 | namespace BlazorHero.CleanArchitecture.Application.Models.Chat 5 | { 6 | public partial class ChatHistory : IChatHistory where TUser : IChatUser 7 | { 8 | public long Id { get; set; } 9 | public string FromUserId { get; set; } 10 | public string ToUserId { get; set; } 11 | public string Message { get; set; } 12 | public DateTime CreatedDate { get; set; } 13 | public virtual TUser FromUser { get; set; } 14 | public virtual TUser ToUser { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Application/Models/Chat/Message.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Models.Chat 2 | { 3 | public class Message 4 | { 5 | public string ToUserId { get; set; } 6 | public string FromUserId { get; set; } 7 | public string MessageText { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Application/Requests/Catalog/GetAllPagedProductsRequest.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Requests.Catalog 2 | { 3 | public class GetAllPagedProductsRequest : PagedRequest 4 | { 5 | public string SearchString { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/Application/Requests/Documents/GetAllPagedDocumentsRequest.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Requests.Documents 2 | { 3 | public class GetAllPagedDocumentsRequest : PagedRequest 4 | { 5 | public string SearchString { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/Application/Requests/Identity/ChangePasswordRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Requests.Identity 4 | { 5 | public class ChangePasswordRequest 6 | { 7 | [Required] 8 | public string Password { get; set; } 9 | 10 | [Required] 11 | public string NewPassword { get; set; } 12 | 13 | [Required] 14 | public string ConfirmNewPassword { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Application/Requests/Identity/ForgotPasswordRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Requests.Identity 4 | { 5 | public class ForgotPasswordRequest 6 | { 7 | [Required] 8 | [EmailAddress] 9 | public string Email { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Application/Requests/Identity/PermissionRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Requests.Identity 4 | { 5 | public class PermissionRequest 6 | { 7 | public string RoleId { get; set; } 8 | public IList RoleClaims { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Application/Requests/Identity/RefreshTokenRequest.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Requests.Identity 2 | { 3 | public class RefreshTokenRequest 4 | { 5 | public string Token { get; set; } 6 | public string RefreshToken { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /src/Application/Requests/Identity/RegisterRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Requests.Identity 4 | { 5 | public class RegisterRequest 6 | { 7 | [Required] 8 | public string FirstName { get; set; } 9 | 10 | [Required] 11 | public string LastName { get; set; } 12 | 13 | [Required] 14 | [EmailAddress] 15 | public string Email { get; set; } 16 | 17 | [Required] 18 | [MinLength(6)] 19 | public string UserName { get; set; } 20 | 21 | [Required] 22 | [MinLength(6)] 23 | public string Password { get; set; } 24 | 25 | [Required] 26 | [Compare(nameof(Password))] 27 | public string ConfirmPassword { get; set; } 28 | 29 | public string PhoneNumber { get; set; } 30 | 31 | public bool ActivateUser { get; set; } = false; 32 | public bool AutoConfirmEmail { get; set; } = false; 33 | } 34 | } -------------------------------------------------------------------------------- /src/Application/Requests/Identity/ResetPasswordRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Requests.Identity 4 | { 5 | public class ResetPasswordRequest 6 | { 7 | [Required] 8 | [EmailAddress] 9 | public string Email { get; set; } 10 | [Required] 11 | public string Password { get; set; } 12 | [Required] 13 | [Compare(nameof(Password))] 14 | public string ConfirmPassword { get; set; } 15 | [Required] 16 | public string Token { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Application/Requests/Identity/RoleClaimRequest.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Requests.Identity 2 | { 3 | public class RoleClaimRequest 4 | { 5 | public int Id { get; set; } 6 | public string RoleId { get; set; } 7 | public string Type { get; set; } 8 | public string Value { get; set; } 9 | public string Description { get; set; } 10 | public string Group { get; set; } 11 | public bool Selected { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Application/Requests/Identity/RoleRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Requests.Identity 4 | { 5 | public class RoleRequest 6 | { 7 | public string Id { get; set; } 8 | 9 | [Required] 10 | public string Name { get; set; } 11 | public string Description { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Application/Requests/Identity/ToggleUserStatusRequest.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Requests.Identity 2 | { 3 | public class ToggleUserStatusRequest 4 | { 5 | public bool ActivateUser { get; set; } 6 | public string UserId { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /src/Application/Requests/Identity/TokenRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Requests.Identity 4 | { 5 | public class TokenRequest 6 | { 7 | [Required] 8 | public string Email { get; set; } 9 | 10 | [Required] 11 | public string Password { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Application/Requests/Identity/UpdateProfilePictureRequest.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Requests.Identity 2 | { 3 | public class UpdateProfilePictureRequest : UploadRequest 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/Application/Requests/Identity/UpdateProfileRequest.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Requests.Identity 4 | { 5 | public class UpdateProfileRequest 6 | { 7 | [Required] 8 | public string FirstName { get; set; } 9 | 10 | [Required] 11 | public string LastName { get; set; } 12 | 13 | public string PhoneNumber { get; set; } 14 | public string Email { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Application/Requests/Identity/UpdateUserRolesRequest.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Responses.Identity; 2 | using System.Collections.Generic; 3 | 4 | namespace BlazorHero.CleanArchitecture.Application.Requests.Identity 5 | { 6 | public class UpdateUserRolesRequest 7 | { 8 | public string UserId { get; set; } 9 | public IList UserRoles { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Application/Requests/Mail/MailRequest.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Requests.Mail 2 | { 3 | public class MailRequest 4 | { 5 | public string To { get; set; } 6 | public string Subject { get; set; } 7 | public string Body { get; set; } 8 | public string From { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Application/Requests/PagedRequest.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Requests 2 | { 3 | public abstract class PagedRequest 4 | { 5 | public int PageSize { get; set; } 6 | public int PageNumber { get; set; } 7 | 8 | public string[] Orderby { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Application/Requests/UploadRequest.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Enums; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Requests 4 | { 5 | public class UploadRequest 6 | { 7 | public string FileName { get; set; } 8 | public string Extension { get; set; } 9 | public UploadType UploadType { get; set; } 10 | public byte[] Data { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Application/Responses/Audit/AuditResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Responses.Audit 4 | { 5 | public class AuditResponse 6 | { 7 | public int Id { get; set; } 8 | public string UserId { get; set; } 9 | public string Type { get; set; } 10 | public string TableName { get; set; } 11 | public DateTime DateTime { get; set; } 12 | public string OldValues { get; set; } 13 | public string NewValues { get; set; } 14 | public string AffectedColumns { get; set; } 15 | public string PrimaryKey { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Application/Responses/Identity/ChatHistoryResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Responses.Identity 4 | { 5 | public partial class ChatHistoryResponse 6 | { 7 | public long Id { get; set; } 8 | public string FromUserId { get; set; } 9 | public string FromUserImageURL { get; set; } 10 | public string FromUserFullName { get; set; } 11 | public string ToUserId { get; set; } 12 | public string ToUserImageURL { get; set; } 13 | public string ToUserFullName { get; set; } 14 | public string Message { get; set; } 15 | public DateTime CreatedDate { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Application/Responses/Identity/ChatUserResponse.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Models.Chat; 2 | using System.Collections.Generic; 3 | using BlazorHero.CleanArchitecture.Application.Interfaces.Chat; 4 | 5 | namespace BlazorHero.CleanArchitecture.Application.Responses.Identity 6 | { 7 | public class ChatUserResponse 8 | { 9 | public string Id { get; set; } 10 | public string UserName { get; set; } 11 | public string ProfilePictureDataUrl { get; set; } 12 | public string FirstName { get; set; } 13 | public string LastName { get; set; } 14 | public string EmailAddress { get; set; } 15 | public bool IsOnline { get; set; } 16 | public virtual ICollection> ChatHistoryFromUsers { get; set; } 17 | public virtual ICollection> ChatHistoryToUsers { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Application/Responses/Identity/GetAllRolesResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Responses.Identity 4 | { 5 | public class GetAllRolesResponse 6 | { 7 | public IEnumerable Roles { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Application/Responses/Identity/GetAllUsersResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Responses.Identity 4 | { 5 | public class GetAllUsersResponse 6 | { 7 | public IEnumerable Users { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Application/Responses/Identity/PermissionResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Responses.Identity 4 | { 5 | public class PermissionResponse 6 | { 7 | public string RoleId { get; set; } 8 | public string RoleName { get; set; } 9 | public List RoleClaims { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Application/Responses/Identity/RoleClaimResponse.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Responses.Identity 2 | { 3 | public class RoleClaimResponse 4 | { 5 | public int Id { get; set; } 6 | public string RoleId { get; set; } 7 | public string Type { get; set; } 8 | public string Value { get; set; } 9 | public string Description { get; set; } 10 | public string Group { get; set; } 11 | public bool Selected { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Application/Responses/Identity/RoleResponse.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Responses.Identity 4 | { 5 | public class RoleResponse 6 | { 7 | public string Id { get; set; } 8 | 9 | [Required] 10 | public string Name { get; set; } 11 | public string Description { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Application/Responses/Identity/TokenResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Responses.Identity 4 | { 5 | public class TokenResponse 6 | { 7 | public string Token { get; set; } 8 | public string RefreshToken { get; set; } 9 | public string UserImageURL { get; set; } 10 | public DateTime RefreshTokenExpiryTime { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/Application/Responses/Identity/UserResponse.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Application.Responses.Identity 2 | { 3 | public class UserResponse 4 | { 5 | public string Id { get; set; } 6 | public string UserName { get; set; } 7 | public string FirstName { get; set; } 8 | public string LastName { get; set; } 9 | public string Email { get; set; } 10 | public bool IsActive { get; set; } = true; 11 | public bool EmailConfirmed { get; set; } 12 | public string PhoneNumber { get; set; } 13 | public string ProfilePictureDataUrl { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Application/Responses/Identity/UserRolesResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace BlazorHero.CleanArchitecture.Application.Responses.Identity 4 | { 5 | public class UserRolesResponse 6 | { 7 | public List UserRoles { get; set; } = new(); 8 | } 9 | 10 | public class UserRoleModel 11 | { 12 | public string RoleName { get; set; } 13 | public string RoleDescription { get; set; } 14 | public bool Selected { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Application/Serialization/JsonConverters/TimespanJsonConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | using System.Text.Json.Serialization; 4 | using System.Text.RegularExpressions; 5 | 6 | namespace BlazorHero.CleanArchitecture.Application.Serialization.JsonConverters 7 | { 8 | /// 9 | /// The new Json.NET doesn't support Timespan at this time 10 | /// https://github.com/dotnet/corefx/issues/38641 11 | /// 12 | public class TimespanJsonConverter : JsonConverter 13 | { 14 | /// 15 | /// Format: Days.Hours:Minutes:Seconds:Milliseconds 16 | /// 17 | public const string TimeSpanFormatString = @"d\.hh\:mm\:ss\:FFF"; 18 | 19 | public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 20 | { 21 | var s = reader.GetString(); 22 | if (string.IsNullOrWhiteSpace(s)) 23 | { 24 | return TimeSpan.Zero; 25 | } 26 | 27 | if (!TimeSpan.TryParseExact(s, TimeSpanFormatString, null, out var parsedTimeSpan)) 28 | { 29 | throw new FormatException($"Input timespan is not in an expected format : expected {Regex.Unescape(TimeSpanFormatString)}. Please retrieve this key as a string and parse manually."); 30 | } 31 | 32 | return parsedTimeSpan; 33 | } 34 | 35 | public override void Write(Utf8JsonWriter writer, TimeSpan value, JsonSerializerOptions options) 36 | { 37 | var timespanFormatted = $"{value.ToString(TimeSpanFormatString)}"; 38 | writer.WriteStringValue(timespanFormatted); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/Application/Serialization/Options/SystemTextJsonOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using BlazorHero.CleanArchitecture.Application.Interfaces.Serialization.Options; 3 | 4 | namespace BlazorHero.CleanArchitecture.Application.Serialization.Options 5 | { 6 | public class SystemTextJsonOptions : IJsonSerializerOptions 7 | { 8 | public JsonSerializerOptions JsonSerializerOptions { get; } = new(); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Application/Serialization/Serializers/NewtonSoftJsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Interfaces.Serialization.Serializers; 2 | using BlazorHero.CleanArchitecture.Application.Serialization.Settings; 3 | using Microsoft.Extensions.Options; 4 | using Newtonsoft.Json; 5 | 6 | namespace BlazorHero.CleanArchitecture.Application.Serialization.Serializers 7 | { 8 | public class NewtonSoftJsonSerializer : IJsonSerializer 9 | { 10 | private readonly JsonSerializerSettings _settings; 11 | 12 | public NewtonSoftJsonSerializer(IOptions settings) 13 | { 14 | _settings = settings.Value.JsonSerializerSettings; 15 | } 16 | 17 | public T Deserialize(string text) 18 | => JsonConvert.DeserializeObject(text, _settings); 19 | 20 | public string Serialize(T obj) 21 | => JsonConvert.SerializeObject(obj, _settings); 22 | } 23 | } -------------------------------------------------------------------------------- /src/Application/Serialization/Serializers/SystemTextJsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using BlazorHero.CleanArchitecture.Application.Interfaces.Serialization.Serializers; 3 | using BlazorHero.CleanArchitecture.Application.Serialization.Options; 4 | using Microsoft.Extensions.Options; 5 | 6 | namespace BlazorHero.CleanArchitecture.Application.Serialization.Serializers 7 | { 8 | public class SystemTextJsonSerializer : IJsonSerializer 9 | { 10 | private readonly JsonSerializerOptions _options; 11 | 12 | public SystemTextJsonSerializer(IOptions options) 13 | { 14 | _options = options.Value.JsonSerializerOptions; 15 | } 16 | 17 | public T Deserialize(string data) 18 | => JsonSerializer.Deserialize(data, _options); 19 | 20 | public string Serialize(T data) 21 | => JsonSerializer.Serialize(data, _options); 22 | } 23 | } -------------------------------------------------------------------------------- /src/Application/Serialization/Settings/NewtonsoftJsonSettings.cs: -------------------------------------------------------------------------------- 1 |  2 | using BlazorHero.CleanArchitecture.Application.Interfaces.Serialization.Settings; 3 | using Newtonsoft.Json; 4 | 5 | namespace BlazorHero.CleanArchitecture.Application.Serialization.Settings 6 | { 7 | public class NewtonsoftJsonSettings : IJsonSerializerSettings 8 | { 9 | public JsonSerializerSettings JsonSerializerSettings { get; } = new(); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Application/Specifications/Base/HeroSpecification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | using BlazorHero.CleanArchitecture.Application.Extensions; 5 | using BlazorHero.CleanArchitecture.Domain.Contracts; 6 | 7 | namespace BlazorHero.CleanArchitecture.Application.Specifications.Base 8 | { 9 | public abstract class HeroSpecification : ISpecification where T : class, IEntity 10 | { 11 | public Expression> Criteria { get; set; } 12 | public List>> Includes { get; } = new(); 13 | public List IncludeStrings { get; } = new(); 14 | 15 | protected virtual void AddInclude(Expression> includeExpression) 16 | { 17 | Includes.Add(includeExpression); 18 | } 19 | 20 | protected virtual void AddInclude(string includeString) 21 | { 22 | IncludeStrings.Add(includeString); 23 | } 24 | 25 | public Expression> And(Expression> query) 26 | { 27 | return Criteria = Criteria == null ? query : Criteria.And(query); 28 | } 29 | 30 | public Expression> Or(Expression> query) 31 | { 32 | return Criteria = Criteria == null ? query : Criteria.Or(query); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/Application/Specifications/Base/ISpecification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | using BlazorHero.CleanArchitecture.Domain.Contracts; 5 | 6 | namespace BlazorHero.CleanArchitecture.Application.Specifications.Base 7 | { 8 | public interface ISpecification where T : class, IEntity 9 | { 10 | Expression> Criteria { get; } 11 | List>> Includes { get; } 12 | List IncludeStrings { get; } 13 | Expression> And(Expression> query); 14 | Expression> Or(Expression> query); 15 | } 16 | } -------------------------------------------------------------------------------- /src/Application/Specifications/Catalog/BrandFilterSpecification.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Specifications.Base; 2 | using BlazorHero.CleanArchitecture.Domain.Entities.Catalog; 3 | 4 | namespace BlazorHero.CleanArchitecture.Application.Specifications.Catalog 5 | { 6 | public class BrandFilterSpecification : HeroSpecification 7 | { 8 | public BrandFilterSpecification(string searchString) 9 | { 10 | if (!string.IsNullOrEmpty(searchString)) 11 | { 12 | Criteria = p => p.Name.Contains(searchString) || p.Description.Contains(searchString); 13 | } 14 | else 15 | { 16 | Criteria = p => true; 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Application/Specifications/Catalog/ProductFilterSpecification.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Specifications.Base; 2 | using BlazorHero.CleanArchitecture.Domain.Entities.Catalog; 3 | 4 | namespace BlazorHero.CleanArchitecture.Application.Specifications.Catalog 5 | { 6 | public class ProductFilterSpecification : HeroSpecification 7 | { 8 | public ProductFilterSpecification(string searchString) 9 | { 10 | Includes.Add(a => a.Brand); 11 | if (!string.IsNullOrEmpty(searchString)) 12 | { 13 | Criteria = p => p.Barcode != null && (p.Name.Contains(searchString) || p.Description.Contains(searchString) || p.Barcode.Contains(searchString) || p.Brand.Name.Contains(searchString)); 14 | } 15 | else 16 | { 17 | Criteria = p => p.Barcode != null; 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Application/Specifications/Misc/DocumentFilterSpecification.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Specifications.Base; 2 | using BlazorHero.CleanArchitecture.Domain.Entities.Misc; 3 | 4 | namespace BlazorHero.CleanArchitecture.Application.Specifications.Misc 5 | { 6 | public class DocumentFilterSpecification : HeroSpecification 7 | { 8 | public DocumentFilterSpecification(string searchString, string userId) 9 | { 10 | if (!string.IsNullOrEmpty(searchString)) 11 | { 12 | Criteria = p => (p.Title.Contains(searchString) || p.Description.Contains(searchString)) && (p.IsPublic == true || (p.IsPublic == false && p.CreatedBy == userId)); 13 | } 14 | else 15 | { 16 | Criteria = p => (p.IsPublic == true || (p.IsPublic == false && p.CreatedBy == userId)); 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Application/Specifications/Misc/DocumentTypeFilterSpecification.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Specifications.Base; 2 | using BlazorHero.CleanArchitecture.Domain.Entities.Misc; 3 | 4 | namespace BlazorHero.CleanArchitecture.Application.Specifications.Misc 5 | { 6 | public class DocumentTypeFilterSpecification : HeroSpecification 7 | { 8 | public DocumentTypeFilterSpecification(string searchString) 9 | { 10 | if (!string.IsNullOrEmpty(searchString)) 11 | { 12 | Criteria = p => p.Name.Contains(searchString) || p.Description.Contains(searchString); 13 | } 14 | else 15 | { 16 | Criteria = p => true; 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Application/Validators/Extensions/ValidatorExtensions.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Interfaces.Serialization.Serializers; 2 | using FluentValidation; 3 | using FluentValidation.Validators; 4 | 5 | namespace BlazorHero.CleanArchitecture.Application.Validators.Extensions 6 | { 7 | public static class ValidatorExtensions 8 | { 9 | public static IRuleBuilderOptions MustBeJson(this IRuleBuilder ruleBuilder, IPropertyValidator validator) where T : class 10 | { 11 | return ruleBuilder.SetValidator(validator); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Application/Validators/Features/Brands/Commands/AddEdit/AddEditBrandCommandValidator.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Features.Brands.Commands.AddEdit; 2 | using FluentValidation; 3 | using Microsoft.Extensions.Localization; 4 | 5 | namespace BlazorHero.CleanArchitecture.Application.Validators.Features.Brands.Commands.AddEdit 6 | { 7 | public class AddEditBrandCommandValidator : AbstractValidator 8 | { 9 | public AddEditBrandCommandValidator(IStringLocalizer localizer) 10 | { 11 | RuleFor(request => request.Name) 12 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Name is required!"]); 13 | RuleFor(request => request.Description) 14 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Description is required!"]); 15 | RuleFor(request => request.Tax) 16 | .GreaterThan(0).WithMessage(x => localizer["Tax must be greater than 0"]); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Application/Validators/Features/DocumentTypes/Commands/AddEdit/AddEditDocumentTypeCommandValidator.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Features.DocumentTypes.Commands.AddEdit; 2 | using FluentValidation; 3 | using Microsoft.Extensions.Localization; 4 | 5 | namespace BlazorHero.CleanArchitecture.Application.Validators.Features.DocumentTypes.Commands.AddEdit 6 | { 7 | public class AddEditDocumentTypeCommandValidator : AbstractValidator 8 | { 9 | public AddEditDocumentTypeCommandValidator(IStringLocalizer localizer) 10 | { 11 | RuleFor(request => request.Name) 12 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Name is required!"]); 13 | RuleFor(request => request.Description) 14 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Description is required!"]); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Application/Validators/Features/Documents/Commands/AddEdit/AddEditDocumentCommandValidator.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Features.Documents.Commands.AddEdit; 2 | using FluentValidation; 3 | using Microsoft.Extensions.Localization; 4 | 5 | namespace BlazorHero.CleanArchitecture.Application.Validators.Features.Documents.Commands.AddEdit 6 | { 7 | public class AddEditDocumentCommandValidator : AbstractValidator 8 | { 9 | public AddEditDocumentCommandValidator(IStringLocalizer localizer) 10 | { 11 | RuleFor(request => request.Title) 12 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Title is required!"]); 13 | RuleFor(request => request.Description) 14 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Description is required!"]); 15 | RuleFor(request => request.DocumentTypeId) 16 | .GreaterThan(0).WithMessage(x => localizer["Document Type is required!"]); 17 | RuleFor(request => request.URL) 18 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["File is required!"]); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Application/Validators/Features/ExtendedAttributes/Commands/AddEdit/AddEditDocumentExtendedAttributeCommandValidator.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Domain.Entities.ExtendedAttributes; 2 | using BlazorHero.CleanArchitecture.Domain.Entities.Misc; 3 | using Microsoft.Extensions.Localization; 4 | 5 | namespace BlazorHero.CleanArchitecture.Application.Validators.Features.ExtendedAttributes.Commands.AddEdit 6 | { 7 | public class AddEditDocumentExtendedAttributeCommandValidator : AddEditExtendedAttributeCommandValidator 8 | { 9 | public AddEditDocumentExtendedAttributeCommandValidator(IStringLocalizer localizer) : base(localizer) 10 | { 11 | // you can override the validation rules here 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Application/Validators/Features/Products/Commands/AddEdit/AddEditProductCommandValidator.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Features.Products.Commands.AddEdit; 2 | using FluentValidation; 3 | using Microsoft.Extensions.Localization; 4 | 5 | namespace BlazorHero.CleanArchitecture.Application.Validators.Features.Products.Commands.AddEdit 6 | { 7 | public class AddEditProductCommandValidator : AbstractValidator 8 | { 9 | public AddEditProductCommandValidator(IStringLocalizer localizer) 10 | { 11 | RuleFor(request => request.Name) 12 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Name is required!"]); 13 | RuleFor(request => request.Barcode) 14 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Barcode is required!"]); 15 | RuleFor(request => request.Description) 16 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Description is required!"]); 17 | RuleFor(request => request.BrandId) 18 | .GreaterThan(0).WithMessage(x => localizer["Brand is required!"]); 19 | RuleFor(request => request.Rate) 20 | .GreaterThan(0).WithMessage(x => localizer["Rate must be greater than 0"]); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Application/Validators/JsonValidator.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Interfaces.Serialization.Serializers; 2 | using FluentValidation; 3 | using FluentValidation.Validators; 4 | 5 | namespace BlazorHero.CleanArchitecture.Application.Validators 6 | { 7 | public class JsonValidator : PropertyValidator 8 | { 9 | private readonly IJsonSerializer _jsonSerializer; 10 | 11 | public JsonValidator(IJsonSerializer jsonSerializer) 12 | { 13 | _jsonSerializer = jsonSerializer; 14 | } 15 | 16 | public override string Name => "JsonValidator"; 17 | 18 | public override bool IsValid(ValidationContext context, string value) 19 | { 20 | var isJson = true; 21 | value = value.Trim(); 22 | try 23 | { 24 | _jsonSerializer.Deserialize(value); 25 | } 26 | catch 27 | { 28 | isJson = false; 29 | } 30 | isJson = isJson && value.StartsWith("{") && value.EndsWith("}") 31 | || value.StartsWith("[") && value.EndsWith("]"); 32 | 33 | return isJson; 34 | } 35 | 36 | protected override string GetDefaultMessageTemplate(string errorCode) 37 | => "{PropertyName} must be json string."; 38 | } 39 | } -------------------------------------------------------------------------------- /src/Application/Validators/Requests/Identity/ChangePasswordRequestValidator.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 2 | using FluentValidation; 3 | using Microsoft.Extensions.Localization; 4 | 5 | namespace BlazorHero.CleanArchitecture.Application.Validators.Requests.Identity 6 | { 7 | public class ChangePasswordRequestValidator : AbstractValidator 8 | { 9 | public ChangePasswordRequestValidator(IStringLocalizer localizer) 10 | { 11 | RuleFor(request => request.Password) 12 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Current Password is required!"]); 13 | RuleFor(request => request.NewPassword) 14 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Password is required!"]) 15 | .MinimumLength(8).WithMessage(localizer["Password must be at least of length 8"]) 16 | .Matches(@"[A-Z]").WithMessage(localizer["Password must contain at least one capital letter"]) 17 | .Matches(@"[a-z]").WithMessage(localizer["Password must contain at least one lowercase letter"]) 18 | .Matches(@"[0-9]").WithMessage(localizer["Password must contain at least one digit"]); 19 | RuleFor(request => request.ConfirmNewPassword) 20 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Password Confirmation is required!"]) 21 | .Equal(request => request.NewPassword).WithMessage(x => localizer["Passwords don't match"]); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Application/Validators/Requests/Identity/ForgotPasswordRequestValidator.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 2 | using FluentValidation; 3 | using Microsoft.Extensions.Localization; 4 | 5 | namespace BlazorHero.CleanArchitecture.Application.Validators.Requests.Identity 6 | { 7 | public class ForgotPasswordRequestValidator : AbstractValidator 8 | { 9 | public ForgotPasswordRequestValidator(IStringLocalizer localizer) 10 | { 11 | RuleFor(request => request.Email) 12 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Email is required"]) 13 | .EmailAddress().WithMessage(x => localizer["Email is not correct"]); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Application/Validators/Requests/Identity/ResetPasswordRequestValidator.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 2 | using FluentValidation; 3 | using Microsoft.Extensions.Localization; 4 | 5 | namespace BlazorHero.CleanArchitecture.Application.Validators.Requests.Identity 6 | { 7 | public class ResetPasswordRequestValidator : AbstractValidator 8 | { 9 | public ResetPasswordRequestValidator(IStringLocalizer localizer) 10 | { 11 | RuleFor(request => request.Email) 12 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Email is required"]) 13 | .EmailAddress().WithMessage(x => localizer["Email is not correct"]); 14 | RuleFor(request => request.Password) 15 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Password is required!"]) 16 | .MinimumLength(8).WithMessage(localizer["Password must be at least of length 8"]) 17 | .Matches(@"[A-Z]").WithMessage(localizer["Password must contain at least one capital letter"]) 18 | .Matches(@"[a-z]").WithMessage(localizer["Password must contain at least one lowercase letter"]) 19 | .Matches(@"[0-9]").WithMessage(localizer["Password must contain at least one digit"]); 20 | RuleFor(request => request.ConfirmPassword) 21 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Password Confirmation is required!"]) 22 | .Equal(request => request.Password).WithMessage(x => localizer["Passwords don't match"]); 23 | RuleFor(request => request.Token) 24 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Token is required"]); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Application/Validators/Requests/Identity/RoleRequestValidator.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 2 | using FluentValidation; 3 | using Microsoft.Extensions.Localization; 4 | 5 | namespace BlazorHero.CleanArchitecture.Application.Validators.Requests.Identity 6 | { 7 | public class RoleRequestValidator : AbstractValidator 8 | { 9 | public RoleRequestValidator(IStringLocalizer localizer) 10 | { 11 | RuleFor(request => request.Name) 12 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Name is required"]); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Application/Validators/Requests/Identity/TokenRequestValidator.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 2 | using FluentValidation; 3 | using Microsoft.Extensions.Localization; 4 | 5 | namespace BlazorHero.CleanArchitecture.Application.Validators.Requests.Identity 6 | { 7 | public class TokenRequestValidator : AbstractValidator 8 | { 9 | public TokenRequestValidator(IStringLocalizer localizer) 10 | { 11 | RuleFor(request => request.Email) 12 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Email is required"]) 13 | .EmailAddress().WithMessage(x => localizer["Email is not correct"]); 14 | RuleFor(request => request.Password) 15 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Password is required!"]); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Application/Validators/Requests/Identity/UpdateProfileRequestValidator.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 2 | using FluentValidation; 3 | using Microsoft.Extensions.Localization; 4 | 5 | namespace BlazorHero.CleanArchitecture.Application.Validators.Requests.Identity 6 | { 7 | public class UpdateProfileRequestValidator : AbstractValidator 8 | { 9 | public UpdateProfileRequestValidator(IStringLocalizer localizer) 10 | { 11 | RuleFor(request => request.FirstName) 12 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["First Name is required"]); 13 | RuleFor(request => request.LastName) 14 | .Must(x => !string.IsNullOrWhiteSpace(x)).WithMessage(x => localizer["Last Name is required"]); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Authentication/AuthenticationHeaderHandler.cs: -------------------------------------------------------------------------------- 1 | using Blazored.LocalStorage; 2 | using System.Net.Http; 3 | using System.Net.Http.Headers; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using BlazorHero.CleanArchitecture.Shared.Constants.Storage; 7 | 8 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Authentication 9 | { 10 | public class AuthenticationHeaderHandler : DelegatingHandler 11 | { 12 | private readonly ILocalStorageService localStorage; 13 | 14 | public AuthenticationHeaderHandler(ILocalStorageService localStorage) 15 | => this.localStorage = localStorage; 16 | 17 | protected override async Task SendAsync( 18 | HttpRequestMessage request, 19 | CancellationToken cancellationToken) 20 | { 21 | if (request.Headers.Authorization?.Scheme != "Bearer") 22 | { 23 | var savedToken = await this.localStorage.GetItemAsync(StorageConstants.Local.AuthToken); 24 | 25 | if (!string.IsNullOrWhiteSpace(savedToken)) 26 | { 27 | request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", savedToken); 28 | } 29 | } 30 | 31 | return await base.SendAsync(request, cancellationToken); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Client.Infrastructure.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | BlazorHero.CleanArchitecture.Client.Infrastructure 6 | BlazorHero.CleanArchitecture.Client.Infrastructure 7 | latest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Audit/AuditManager.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Responses.Audit; 2 | using BlazorHero.CleanArchitecture.Client.Infrastructure.Extensions; 3 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 4 | using System.Collections.Generic; 5 | using System.Net.Http; 6 | using System.Threading.Tasks; 7 | 8 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Audit 9 | { 10 | public class AuditManager : IAuditManager 11 | { 12 | private readonly HttpClient _httpClient; 13 | 14 | public AuditManager(HttpClient httpClient) 15 | { 16 | _httpClient = httpClient; 17 | } 18 | 19 | public async Task>> GetCurrentUserTrailsAsync() 20 | { 21 | var response = await _httpClient.GetAsync(Routes.AuditEndpoints.GetCurrentUserTrails); 22 | var data = await response.ToResult>(); 23 | return data; 24 | } 25 | 26 | public async Task> DownloadFileAsync(string searchString = "", bool searchInOldValues = false, bool searchInNewValues = false) 27 | { 28 | var response = await _httpClient.GetAsync(string.IsNullOrWhiteSpace(searchString) 29 | ? Routes.AuditEndpoints.DownloadFile 30 | : Routes.AuditEndpoints.DownloadFileFiltered(searchString, searchInOldValues, searchInNewValues)); 31 | return await response.ToResult(); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Audit/IAuditManager.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Responses.Audit; 2 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | 6 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Audit 7 | { 8 | public interface IAuditManager : IManager 9 | { 10 | Task>> GetCurrentUserTrailsAsync(); 11 | 12 | Task> DownloadFileAsync(string searchString = "", bool searchInOldValues = false, bool searchInNewValues = false); 13 | } 14 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Catalog/Brand/IBrandManager.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Features.Brands.Queries.GetAll; 2 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using BlazorHero.CleanArchitecture.Application.Features.Brands.Commands.AddEdit; 6 | using BlazorHero.CleanArchitecture.Application.Features.Brands.Commands.Import; 7 | 8 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Catalog.Brand 9 | { 10 | public interface IBrandManager : IManager 11 | { 12 | Task>> GetAllAsync(); 13 | 14 | Task> SaveAsync(AddEditBrandCommand request); 15 | 16 | Task> DeleteAsync(int id); 17 | 18 | Task> ExportToExcelAsync(string searchString = ""); 19 | 20 | Task> ImportAsync(ImportBrandsCommand request); 21 | } 22 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Catalog/Product/IProductManager.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Features.Products.Commands.AddEdit; 2 | using BlazorHero.CleanArchitecture.Application.Features.Products.Queries.GetAllPaged; 3 | using BlazorHero.CleanArchitecture.Application.Requests.Catalog; 4 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 5 | using System.Threading.Tasks; 6 | 7 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Catalog.Product 8 | { 9 | public interface IProductManager : IManager 10 | { 11 | Task> GetProductsAsync(GetAllPagedProductsRequest request); 12 | 13 | Task> GetProductImageAsync(int id); 14 | 15 | Task> SaveAsync(AddEditProductCommand request); 16 | 17 | Task> DeleteAsync(int id); 18 | 19 | Task> ExportToExcelAsync(string searchString = ""); 20 | } 21 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Communication/ChatManager.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Models.Chat; 2 | using BlazorHero.CleanArchitecture.Application.Responses.Identity; 3 | using BlazorHero.CleanArchitecture.Client.Infrastructure.Extensions; 4 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 5 | using System.Collections.Generic; 6 | using System.Net.Http; 7 | using System.Net.Http.Json; 8 | using System.Threading.Tasks; 9 | using BlazorHero.CleanArchitecture.Application.Interfaces.Chat; 10 | 11 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Communication 12 | { 13 | public class ChatManager : IChatManager 14 | { 15 | private readonly HttpClient _httpClient; 16 | 17 | public ChatManager(HttpClient httpClient) 18 | { 19 | _httpClient = httpClient; 20 | } 21 | 22 | public async Task>> GetChatHistoryAsync(string cId) 23 | { 24 | var response = await _httpClient.GetAsync(Routes.ChatEndpoint.GetChatHistory(cId)); 25 | var data = await response.ToResult>(); 26 | return data; 27 | } 28 | 29 | public async Task>> GetChatUsersAsync() 30 | { 31 | var response = await _httpClient.GetAsync(Routes.ChatEndpoint.GetAvailableUsers); 32 | var data = await response.ToResult>(); 33 | return data; 34 | } 35 | 36 | public async Task SaveMessageAsync(ChatHistory chatHistory) 37 | { 38 | var response = await _httpClient.PostAsJsonAsync(Routes.ChatEndpoint.SaveMessage, chatHistory); 39 | var data = await response.ToResult(); 40 | return data; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Communication/IChatManager.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Models.Chat; 2 | using BlazorHero.CleanArchitecture.Application.Responses.Identity; 3 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | using BlazorHero.CleanArchitecture.Application.Interfaces.Chat; 7 | 8 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Communication 9 | { 10 | public interface IChatManager : IManager 11 | { 12 | Task>> GetChatUsersAsync(); 13 | 14 | Task SaveMessageAsync(ChatHistory chatHistory); 15 | 16 | Task>> GetChatHistoryAsync(string cId); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Dashboard/DashboardManager.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Client.Infrastructure.Extensions; 2 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | using BlazorHero.CleanArchitecture.Application.Features.Dashboards.Queries.GetData; 6 | 7 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Dashboard 8 | { 9 | public class DashboardManager : IDashboardManager 10 | { 11 | private readonly HttpClient _httpClient; 12 | 13 | public DashboardManager(HttpClient httpClient) 14 | { 15 | _httpClient = httpClient; 16 | } 17 | 18 | public async Task> GetDataAsync() 19 | { 20 | var response = await _httpClient.GetAsync(Routes.DashboardEndpoints.GetData); 21 | var data = await response.ToResult(); 22 | return data; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Dashboard/IDashboardManager.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 2 | using System.Threading.Tasks; 3 | using BlazorHero.CleanArchitecture.Application.Features.Dashboards.Queries.GetData; 4 | 5 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Dashboard 6 | { 7 | public interface IDashboardManager : IManager 8 | { 9 | Task> GetDataAsync(); 10 | } 11 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/ExtendedAttribute/IExtendedAttributeManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using BlazorHero.CleanArchitecture.Application.Features.ExtendedAttributes.Commands.AddEdit; 5 | using BlazorHero.CleanArchitecture.Application.Features.ExtendedAttributes.Queries.Export; 6 | using BlazorHero.CleanArchitecture.Application.Features.ExtendedAttributes.Queries.GetAll; 7 | using BlazorHero.CleanArchitecture.Application.Features.ExtendedAttributes.Queries.GetAllByEntityId; 8 | using BlazorHero.CleanArchitecture.Domain.Contracts; 9 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 10 | 11 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.ExtendedAttribute 12 | { 13 | public interface IExtendedAttributeManager 14 | where TEntity : AuditableEntity, IEntityWithExtendedAttributes, IEntity 15 | where TExtendedAttribute : AuditableEntityExtendedAttribute, IEntity 16 | where TId : IEquatable 17 | { 18 | Task>>> GetAllAsync(); 19 | 20 | Task>>> GetAllByEntityIdAsync(TEntityId entityId); 21 | 22 | Task> SaveAsync(AddEditExtendedAttributeCommand request); 23 | 24 | Task> DeleteAsync(TId id); 25 | 26 | Task> ExportToExcelAsync(ExportExtendedAttributesQuery request); 27 | } 28 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/IManager.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers 2 | { 3 | public interface IManager 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Identity/Account/AccountManager.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 2 | using BlazorHero.CleanArchitecture.Client.Infrastructure.Extensions; 3 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 4 | using System.Net.Http; 5 | using System.Net.Http.Json; 6 | using System.Threading.Tasks; 7 | 8 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Identity.Account 9 | { 10 | public class AccountManager : IAccountManager 11 | { 12 | private readonly HttpClient _httpClient; 13 | 14 | public AccountManager(HttpClient httpClient) 15 | { 16 | _httpClient = httpClient; 17 | } 18 | 19 | public async Task ChangePasswordAsync(ChangePasswordRequest model) 20 | { 21 | var response = await _httpClient.PutAsJsonAsync(Routes.AccountEndpoints.ChangePassword, model); 22 | return await response.ToResult(); 23 | } 24 | 25 | public async Task UpdateProfileAsync(UpdateProfileRequest model) 26 | { 27 | var response = await _httpClient.PutAsJsonAsync(Routes.AccountEndpoints.UpdateProfile, model); 28 | return await response.ToResult(); 29 | } 30 | 31 | public async Task> GetProfilePictureAsync(string userId) 32 | { 33 | var response = await _httpClient.GetAsync(Routes.AccountEndpoints.GetProfilePicture(userId)); 34 | return await response.ToResult(); 35 | } 36 | 37 | public async Task> UpdateProfilePictureAsync(UpdateProfilePictureRequest request, string userId) 38 | { 39 | var response = await _httpClient.PostAsJsonAsync(Routes.AccountEndpoints.UpdateProfilePicture(userId), request); 40 | return await response.ToResult(); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Identity/Account/IAccountManager.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 2 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 3 | using System.Threading.Tasks; 4 | 5 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Identity.Account 6 | { 7 | public interface IAccountManager : IManager 8 | { 9 | Task ChangePasswordAsync(ChangePasswordRequest model); 10 | 11 | Task UpdateProfileAsync(UpdateProfileRequest model); 12 | 13 | Task> GetProfilePictureAsync(string userId); 14 | 15 | Task> UpdateProfilePictureAsync(UpdateProfilePictureRequest request, string userId); 16 | } 17 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Identity/Authentication/IAuthenticationManager.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 2 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 3 | using System.Security.Claims; 4 | using System.Threading.Tasks; 5 | 6 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Identity.Authentication 7 | { 8 | public interface IAuthenticationManager : IManager 9 | { 10 | Task Login(TokenRequest model); 11 | 12 | Task Logout(); 13 | 14 | Task RefreshToken(); 15 | 16 | Task TryRefreshToken(); 17 | 18 | Task TryForceRefreshToken(); 19 | 20 | Task CurrentUser(); 21 | } 22 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Identity/RoleClaims/IRoleClaimManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 4 | using BlazorHero.CleanArchitecture.Application.Responses.Identity; 5 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 6 | 7 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Identity.RoleClaims 8 | { 9 | public interface IRoleClaimManager : IManager 10 | { 11 | Task>> GetRoleClaimsAsync(); 12 | 13 | Task>> GetRoleClaimsByRoleIdAsync(string roleId); 14 | 15 | Task> SaveAsync(RoleClaimRequest role); 16 | 17 | Task> DeleteAsync(string id); 18 | } 19 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Identity/Roles/IRoleManager.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 2 | using BlazorHero.CleanArchitecture.Application.Responses.Identity; 3 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Identity.Roles 8 | { 9 | public interface IRoleManager : IManager 10 | { 11 | Task>> GetRolesAsync(); 12 | 13 | Task> SaveAsync(RoleRequest role); 14 | 15 | Task> DeleteAsync(string id); 16 | 17 | Task> GetPermissionsAsync(string roleId); 18 | 19 | Task> UpdatePermissionsAsync(PermissionRequest request); 20 | } 21 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Identity/Users/IUserManager.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 2 | using BlazorHero.CleanArchitecture.Application.Responses.Identity; 3 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Identity.Users 8 | { 9 | public interface IUserManager : IManager 10 | { 11 | Task>> GetAllAsync(); 12 | 13 | Task ForgotPasswordAsync(ForgotPasswordRequest request); 14 | 15 | Task ResetPasswordAsync(ResetPasswordRequest request); 16 | 17 | Task> GetAsync(string userId); 18 | 19 | Task> GetRolesAsync(string userId); 20 | 21 | Task RegisterUserAsync(RegisterRequest request); 22 | 23 | Task ToggleUserStatusAsync(ToggleUserStatusRequest request); 24 | 25 | Task UpdateRolesAsync(UpdateUserRolesRequest request); 26 | 27 | Task ExportToExcelAsync(string searchString = ""); 28 | } 29 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Interceptors/IHttpInterceptorManager.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Toolbelt.Blazor; 3 | 4 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Interceptors 5 | { 6 | public interface IHttpInterceptorManager : IManager 7 | { 8 | void RegisterEvent(); 9 | 10 | Task InterceptBeforeHttpAsync(object sender, HttpClientInterceptorEventArgs e); 11 | 12 | void DisposeEvent(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Misc/Document/IDocumentManager.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Features.Documents.Commands.AddEdit; 2 | using BlazorHero.CleanArchitecture.Application.Features.Documents.Queries.GetAll; 3 | using BlazorHero.CleanArchitecture.Application.Requests.Documents; 4 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 5 | using System.Threading.Tasks; 6 | using BlazorHero.CleanArchitecture.Application.Features.Documents.Queries.GetById; 7 | 8 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Misc.Document 9 | { 10 | public interface IDocumentManager : IManager 11 | { 12 | Task> GetAllAsync(GetAllPagedDocumentsRequest request); 13 | 14 | Task> GetByIdAsync(GetDocumentByIdQuery request); 15 | 16 | Task> SaveAsync(AddEditDocumentCommand request); 17 | 18 | Task> DeleteAsync(int id); 19 | } 20 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Misc/DocumentType/IDocumentTypeManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using BlazorHero.CleanArchitecture.Application.Features.DocumentTypes.Commands.AddEdit; 4 | using BlazorHero.CleanArchitecture.Application.Features.DocumentTypes.Queries.GetAll; 5 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 6 | 7 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Misc.DocumentType 8 | { 9 | public interface IDocumentTypeManager : IManager 10 | { 11 | Task>> GetAllAsync(); 12 | 13 | Task> SaveAsync(AddEditDocumentTypeCommand request); 14 | 15 | Task> DeleteAsync(int id); 16 | 17 | Task> ExportToExcelAsync(string searchString = ""); 18 | } 19 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Managers/Preferences/IClientPreferenceManager.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Shared.Managers; 2 | using MudBlazor; 3 | using System.Threading.Tasks; 4 | 5 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Preferences 6 | { 7 | public interface IClientPreferenceManager : IPreferenceManager 8 | { 9 | Task GetCurrentThemeAsync(); 10 | 11 | Task ToggleDarkModeAsync(); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Mappings/RolesProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 3 | using BlazorHero.CleanArchitecture.Application.Responses.Identity; 4 | 5 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Mappings 6 | { 7 | public class RoleProfile : Profile 8 | { 9 | public RoleProfile() 10 | { 11 | CreateMap().ReverseMap(); 12 | CreateMap().ReverseMap(); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Routes/AccountEndpoints.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Routes 2 | { 3 | public static class AccountEndpoints 4 | { 5 | public static string Register = "api/identity/account/register"; 6 | public static string ChangePassword = "api/identity/account/changepassword"; 7 | public static string UpdateProfile = "api/identity/account/updateprofile"; 8 | 9 | public static string GetProfilePicture(string userId) 10 | { 11 | return $"api/identity/account/profile-picture/{userId}"; 12 | } 13 | 14 | public static string UpdateProfilePicture(string userId) 15 | { 16 | return $"api/identity/account/profile-picture/{userId}"; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Routes/AuditEndpoints.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Routes 2 | { 3 | public static class AuditEndpoints 4 | { 5 | public static string DownloadFileFiltered(string searchString, bool searchInOldValues = false, bool searchInNewValues = false) 6 | { 7 | return $"{DownloadFile}?searchString={searchString}&searchInOldValues={searchInOldValues}&searchInNewValues={searchInNewValues}"; 8 | } 9 | 10 | public static string GetCurrentUserTrails = "api/audits"; 11 | public static string DownloadFile = "api/audits/export"; 12 | } 13 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Routes/BrandsEndpoints.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Routes 2 | { 3 | public static class BrandsEndpoints 4 | { 5 | public static string ExportFiltered(string searchString) 6 | { 7 | return $"{Export}?searchString={searchString}"; 8 | } 9 | 10 | public static string Export = "api/v1/brands/export"; 11 | 12 | public static string GetAll = "api/v1/brands"; 13 | public static string Delete = "api/v1/brands"; 14 | public static string Save = "api/v1/brands"; 15 | public static string GetCount = "api/v1/brands/count"; 16 | public static string Import = "api/v1/brands/import"; 17 | } 18 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Routes/ChatEndpoints.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Routes 2 | { 3 | public static class ChatEndpoint 4 | { 5 | public static string GetAvailableUsers = "api/chats/users"; 6 | public static string SaveMessage = "api/chats"; 7 | 8 | public static string GetChatHistory(string userId) 9 | { 10 | return $"api/chats/{userId}"; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Routes/DashboardEndpoints.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Routes 2 | { 3 | public class DashboardEndpoints 4 | { 5 | public static string GetData = "api/v1/dashboard"; 6 | } 7 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Routes/DocumentTypesEndpoints.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Routes 2 | { 3 | public static class DocumentTypesEndpoints 4 | { 5 | public static string ExportFiltered(string searchString) 6 | { 7 | return $"{Export}?searchString={searchString}"; 8 | } 9 | 10 | public static string Export = "api/documentTypes/export"; 11 | 12 | public static string GetAll = "api/documentTypes"; 13 | public static string Delete = "api/documentTypes"; 14 | public static string Save = "api/documentTypes"; 15 | public static string GetCount = "api/documentTypes/count"; 16 | } 17 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Routes/DocumentsEndpoints.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Routes 2 | { 3 | public static class DocumentsEndpoints 4 | { 5 | public static string GetAllPaged(int pageNumber, int pageSize, string searchString) 6 | { 7 | return $"api/documents?pageNumber={pageNumber}&pageSize={pageSize}&searchString={searchString}"; 8 | } 9 | 10 | public static string GetById(int documentId) 11 | { 12 | return $"api/documents/{documentId}"; 13 | } 14 | 15 | public static string Save = "api/documents"; 16 | public static string Delete = "api/documents"; 17 | } 18 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Routes/ExtendedAttributesEndpoints.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Routes 2 | { 3 | public static class ExtendedAttributesEndpoints 4 | { 5 | public static string GetAll(string entityName) => $"api/{entityName}ExtendedAttributes"; 6 | public static string GetAllByEntityId(string entityName, TEntityId entityId) => $"{GetAll(entityName)}/by-entity/{entityId}"; 7 | public static string Export(string entityName, TEntityId entityId, bool includeEntity, bool onlyCurrentGroup = false, string currentGroup = "") => $"api/{entityName}ExtendedAttributes/export?{nameof(entityId)}={entityId}&{nameof(includeEntity)}={includeEntity}&{nameof(onlyCurrentGroup)}={onlyCurrentGroup}&{nameof(currentGroup)}={currentGroup}"; 8 | public static string ExportFiltered(string entityName, string searchString, TEntityId entityId, bool includeEntity, bool onlyCurrentGroup = false, string currentGroup = "") => $"api/{entityName}ExtendedAttributes/export?{nameof(searchString)}={searchString}&{nameof(entityId)}={entityId}&{nameof(includeEntity)}={includeEntity}&{nameof(onlyCurrentGroup)}={onlyCurrentGroup}&{nameof(currentGroup)}={currentGroup}"; 9 | public static string Delete(string entityName) => $"api/{entityName}ExtendedAttributes"; 10 | public static string Save(string entityName) => $"api/{entityName}ExtendedAttributes"; 11 | public static string GetCount(string entityName) => $"api/{entityName}ExtendedAttributes/count"; 12 | } 13 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Routes/PreferencesEndpoints.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Routes 2 | { 3 | public static class PreferencesEndpoints 4 | { 5 | public static string ChangeLanguage = "changeLanguage"; 6 | 7 | //TODO - add endpoints 8 | } 9 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Routes/ProductsEndpoints.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Routes 4 | { 5 | public static class ProductsEndpoints 6 | { 7 | public static string GetAllPaged(int pageNumber, int pageSize, string searchString, string[] orderBy) 8 | { 9 | var url = $"api/v1/products?pageNumber={pageNumber}&pageSize={pageSize}&searchString={searchString}&orderBy="; 10 | if (orderBy?.Any() == true) 11 | { 12 | foreach (var orderByPart in orderBy) 13 | { 14 | url += $"{orderByPart},"; 15 | } 16 | url = url[..^1]; // loose training , 17 | } 18 | return url; 19 | } 20 | 21 | public static string GetCount = "api/v1/products/count"; 22 | 23 | public static string GetProductImage(int productId) 24 | { 25 | return $"api/v1/products/image/{productId}"; 26 | } 27 | 28 | public static string ExportFiltered(string searchString) 29 | { 30 | return $"{Export}?searchString={searchString}"; 31 | } 32 | 33 | public static string Save = "api/v1/products"; 34 | public static string Delete = "api/v1/products"; 35 | public static string Export = "api/v1/products/export"; 36 | public static string ChangePassword = "api/identity/account/changepassword"; 37 | public static string UpdateProfile = "api/identity/account/updateprofile"; 38 | } 39 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Routes/RoleClaimsEndpoints.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Routes 2 | { 3 | class RoleClaimsEndpoints 4 | { 5 | public static string Delete = "api/identity/roleClaim"; 6 | public static string GetAll = "api/identity/roleClaim"; 7 | public static string Save = "api/identity/roleClaim"; 8 | } 9 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Routes/RolesEndpoints.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Routes 2 | { 3 | public static class RolesEndpoints 4 | { 5 | public static string Delete = "api/identity/role"; 6 | public static string GetAll = "api/identity/role"; 7 | public static string Save = "api/identity/role"; 8 | public static string GetPermissions = "api/identity/role/permissions/"; 9 | public static string UpdatePermissions = "api/identity/role/permissions/update"; 10 | } 11 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Routes/TokenEndpoints.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Routes 2 | { 3 | public static class TokenEndpoints 4 | { 5 | public static string Get = "api/identity/token"; 6 | public static string Refresh = "api/identity/token/refresh"; 7 | } 8 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Routes/UserEndpoints.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Routes 2 | { 3 | public static class UserEndpoints 4 | { 5 | public static string GetAll = "api/identity/user"; 6 | 7 | public static string Get(string userId) 8 | { 9 | return $"api/identity/user/{userId}"; 10 | } 11 | 12 | public static string GetUserRoles(string userId) 13 | { 14 | return $"api/identity/user/roles/{userId}"; 15 | } 16 | 17 | public static string ExportFiltered(string searchString) 18 | { 19 | return $"{Export}?searchString={searchString}"; 20 | } 21 | 22 | public static string Export = "api/identity/user/export"; 23 | public static string Register = "api/identity/user"; 24 | public static string ToggleUserStatus = "api/identity/user/toggle-status"; 25 | public static string ForgotPassword = "api/identity/user/forgot-password"; 26 | public static string ResetPassword = "api/identity/user/reset-password"; 27 | } 28 | } -------------------------------------------------------------------------------- /src/Client.Infrastructure/Settings/ClientPreference.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using BlazorHero.CleanArchitecture.Shared.Constants.Localization; 3 | using BlazorHero.CleanArchitecture.Shared.Settings; 4 | 5 | namespace BlazorHero.CleanArchitecture.Client.Infrastructure.Settings 6 | { 7 | public record ClientPreference : IPreference 8 | { 9 | public bool IsDarkMode { get; set; } 10 | public bool IsRTL { get; set; } 11 | public bool IsDrawerOpen { get; set; } 12 | public string PrimaryColor { get; set; } 13 | public string LanguageCode { get; set; } = LocalizationConstants.SupportedLanguages.FirstOrDefault()?.Code ?? "en-US"; 14 | } 15 | } -------------------------------------------------------------------------------- /src/Client/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

Sorry, there's nothing at this address.

14 |
15 |
16 |
17 |
18 |
-------------------------------------------------------------------------------- /src/Client/CustomIcons.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | namespace BlazorHero.CleanArchitecture.Client 4 | { 5 | [ExcludeFromCodeCoverage] 6 | public class CustomIcons 7 | { 8 | public static string BlazorHero { get; } = ""; 9 | } 10 | } -------------------------------------------------------------------------------- /src/Client/Extensions/ClaimsPrincipalExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | 3 | namespace BlazorHero.CleanArchitecture.Client.Extensions 4 | { 5 | internal static class ClaimsPrincipalExtensions 6 | { 7 | internal static string GetEmail(this ClaimsPrincipal claimsPrincipal) 8 | => claimsPrincipal.FindFirstValue(ClaimTypes.Email); 9 | 10 | internal static string GetFirstName(this ClaimsPrincipal claimsPrincipal) 11 | => claimsPrincipal.FindFirstValue(ClaimTypes.Name); 12 | 13 | internal static string GetLastName(this ClaimsPrincipal claimsPrincipal) 14 | => claimsPrincipal.FindFirstValue(ClaimTypes.Surname); 15 | 16 | internal static string GetPhoneNumber(this ClaimsPrincipal claimsPrincipal) 17 | => claimsPrincipal.FindFirstValue(ClaimTypes.MobilePhone); 18 | 19 | internal static string GetUserId(this ClaimsPrincipal claimsPrincipal) 20 | => claimsPrincipal.FindFirstValue(ClaimTypes.NameIdentifier); 21 | } 22 | } -------------------------------------------------------------------------------- /src/Client/Extensions/HubExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | using Microsoft.AspNetCore.SignalR.Client; 3 | using BlazorHero.CleanArchitecture.Shared.Constants.Application; 4 | using Blazored.LocalStorage; 5 | 6 | namespace BlazorHero.CleanArchitecture.Client.Extensions 7 | { 8 | public static class HubExtensions 9 | { 10 | public static HubConnection TryInitialize(this HubConnection hubConnection, NavigationManager navigationManager, ILocalStorageService _localStorage) 11 | { 12 | if (hubConnection == null) 13 | { 14 | hubConnection = new HubConnectionBuilder() 15 | .WithUrl(navigationManager.ToAbsoluteUri(ApplicationConstants.SignalR.HubUrl), options => { 16 | options.AccessTokenProvider = async () => (await _localStorage.GetItemAsync("authToken")); 17 | }) 18 | .WithAutomaticReconnect() 19 | .Build(); 20 | } 21 | return hubConnection; 22 | } 23 | public static HubConnection TryInitialize(this HubConnection hubConnection, NavigationManager navigationManager) 24 | { 25 | if (hubConnection == null) 26 | { 27 | hubConnection = new HubConnectionBuilder() 28 | .WithUrl(navigationManager.ToAbsoluteUri(ApplicationConstants.SignalR.HubUrl)) 29 | .Build(); 30 | } 31 | return hubConnection; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Client/Models/ChatMessage.cs: -------------------------------------------------------------------------------- 1 | namespace AdminDashboard.Wasm.Models 2 | { 3 | public class ChatMessage 4 | { 5 | public string UserName { get; set; } 6 | public string Message { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /src/Client/Models/ChatUser.cs: -------------------------------------------------------------------------------- 1 | using MudBlazor; 2 | 3 | namespace AdminDashboard.Wasm.Models 4 | { 5 | public class ChatUser 6 | { 7 | public string UserName { get; set; } 8 | public string UserRoleColor { get; set; } 9 | public Color OnlineStatus { get; set; } 10 | public bool Spotify { get; set; } 11 | public string AvatarUrl { get; set; } 12 | public Color AvatarColor { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Client/Pages/Identity/Account.razor: -------------------------------------------------------------------------------- 1 | @page "/account" 2 | @inject Microsoft.Extensions.Localization.IStringLocalizer localizer 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/Client/Pages/Identity/Forgot.razor: -------------------------------------------------------------------------------- 1 | @page "/account/forgot-password" 2 | @layout MainLayout 3 | @attribute [AllowAnonymous] 4 | @inject Microsoft.Extensions.Localization.IStringLocalizer _localizer 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |
14 | 15 |
16 | @_localizer["Forgot password?"] 17 |
18 |
19 | 20 |
21 | @_localizer["Enter email for password reset"] 22 |
23 |
24 | 25 | 26 | 27 | 28 | @_localizer["Reset Password"] 29 | 30 |
31 |
-------------------------------------------------------------------------------- /src/Client/Pages/Identity/Forgot.razor.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 2 | using MudBlazor; 3 | using System.Threading.Tasks; 4 | using Blazored.FluentValidation; 5 | 6 | namespace BlazorHero.CleanArchitecture.Client.Pages.Identity 7 | { 8 | public partial class Forgot 9 | { 10 | private FluentValidationValidator _fluentValidationValidator; 11 | private bool Validated => _fluentValidationValidator.Validate(options => { options.IncludeAllRuleSets(); }); 12 | private readonly ForgotPasswordRequest _emailModel = new(); 13 | 14 | private async Task SubmitAsync() 15 | { 16 | var result = await _userManager.ForgotPasswordAsync(_emailModel); 17 | if (result.Succeeded) 18 | { 19 | _snackBar.Add(_localizer["Done!"], Severity.Success); 20 | _navigationManager.NavigateTo("/"); 21 | } 22 | else 23 | { 24 | foreach (var message in result.Messages) 25 | { 26 | _snackBar.Add(message, Severity.Error); 27 | } 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/Client/Pages/Misc/ExtendedAttribute/DocumentExtendedAttributes.razor: -------------------------------------------------------------------------------- 1 | @page "/extended-attributes/{EntityName}/{EntityIdString}" 2 | @attribute [Authorize(Policy = Permissions.DocumentExtendedAttributes.View)] 3 | 4 | @using BlazorHero.CleanArchitecture.Domain.Entities.Misc 5 | @using BlazorHero.CleanArchitecture.Domain.Entities.ExtendedAttributes 6 | @namespace BlazorHero.CleanArchitecture.Client.Pages.Misc.ExtendedAttribute 7 | 8 | @inherits ExtendedAttributes 9 | 10 | @Inherited() 11 | 12 | @code 13 | { 14 | protected override Func FromStringToEntityIdTypeConverter => s => int.Parse(s); 15 | 16 | protected override string ExtendedAttributesViewPolicyName => Permissions.DocumentExtendedAttributes.View; 17 | protected override string ExtendedAttributesEditPolicyName => Permissions.DocumentExtendedAttributes.Edit; 18 | protected override string ExtendedAttributesCreatePolicyName => Permissions.DocumentExtendedAttributes.Create; 19 | protected override string ExtendedAttributesDeletePolicyName => Permissions.DocumentExtendedAttributes.Delete; 20 | protected override string ExtendedAttributesExportPolicyName => Permissions.DocumentExtendedAttributes.Export; 21 | protected override string ExtendedAttributesSearchPolicyName => Permissions.DocumentExtendedAttributes.Search; 22 | } -------------------------------------------------------------------------------- /src/Client/Program.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Client.Extensions; 2 | using BlazorHero.CleanArchitecture.Client.Infrastructure.Managers.Preferences; 3 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using System.Globalization; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using BlazorHero.CleanArchitecture.Client.Infrastructure.Settings; 9 | using BlazorHero.CleanArchitecture.Shared.Constants.Localization; 10 | 11 | namespace BlazorHero.CleanArchitecture.Client 12 | { 13 | public static class Program 14 | { 15 | public static async Task Main(string[] args) 16 | { 17 | var builder = WebAssemblyHostBuilder 18 | .CreateDefault(args) 19 | .AddRootComponents() 20 | .AddClientServices(); 21 | var host = builder.Build(); 22 | var storageService = host.Services.GetRequiredService(); 23 | if (storageService != null) 24 | { 25 | CultureInfo culture; 26 | var preference = await storageService.GetPreference() as ClientPreference; 27 | if (preference != null) 28 | culture = new CultureInfo(preference.LanguageCode); 29 | else 30 | culture = new CultureInfo(LocalizationConstants.SupportedLanguages.FirstOrDefault()?.Code ?? "en-US"); 31 | CultureInfo.DefaultThreadCurrentCulture = culture; 32 | CultureInfo.DefaultThreadCurrentUICulture = culture; 33 | } 34 | await builder.Build().RunAsync(); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Client/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:65410", 7 | "sslPort": 44398 8 | } 9 | }, 10 | "profiles": { 11 | "BlazorHero.CleanArchitecture": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": "true", 14 | "launchBrowser": true, 15 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 16 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Client/Shared/Components/BlazorHeroLogo.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorHero.CleanArchitecture.Client.Shared.Components 2 | 3 | 4 | 5 | @code 6 | { 7 | [Parameter] 8 | public string Style { get; set; } = "width:100px; height:100px;"; 9 | } -------------------------------------------------------------------------------- /src/Client/Shared/Components/ExtendedAttributes.razor: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /src/Client/Shared/Components/ExtendedAttributes.razor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BlazorHero.CleanArchitecture.Domain.Contracts; 3 | using Microsoft.AspNetCore.Components; 4 | 5 | namespace BlazorHero.CleanArchitecture.Client.Shared.Components 6 | { 7 | public abstract partial class ExtendedAttributes 8 | : ExtendedAttributesBase 9 | where TEntity : AuditableEntity, IEntityWithExtendedAttributes, IEntity 10 | where TExtendedAttribute : AuditableEntityExtendedAttribute, IEntity 11 | where TId : IEquatable 12 | { 13 | protected override RenderFragment Inherited() => builder => base.BuildRenderTree(builder); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Client/Shared/Components/HeroTitle.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorHero.CleanArchitecture.Client.Shared.Components 2 | @Title 3 | @Description 4 | 5 | @code 6 | { 7 | [Parameter] public string Title { get; set; } 8 | [Parameter] public string Description { get; set; } 9 | } -------------------------------------------------------------------------------- /src/Client/Shared/Components/LanguageSelector.razor: -------------------------------------------------------------------------------- 1 | @using BlazorHero.CleanArchitecture.Shared.Constants.Localization 2 | @inject Microsoft.Extensions.Localization.IStringLocalizer _localizer 3 | 4 | 5 | @foreach (var language in LocalizationConstants.SupportedLanguages) 6 | { 7 | @_localizer[language.DisplayName] 8 | } 9 | 10 | 11 | @code 12 | { 13 | private async Task ChangeLanguageAsync(string languageCode) 14 | { 15 | var result = await _clientPreferenceManager.ChangeLanguageAsync(languageCode); 16 | if (result.Succeeded) 17 | { 18 | _snackBar.Add(result.Messages[0], Severity.Success); 19 | _navigationManager.NavigateTo(_navigationManager.Uri, forceLoad: true); 20 | } 21 | else 22 | { 23 | foreach (var error in result.Messages) 24 | { 25 | _snackBar.Add(error, Severity.Error); 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Client/Shared/Components/TablePager.razor: -------------------------------------------------------------------------------- 1 | @inject Microsoft.Extensions.Localization.IStringLocalizer localizer 2 | 3 | -------------------------------------------------------------------------------- /src/Client/Shared/Components/UserCard.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorHero.CleanArchitecture.Client.Shared.Components 2 | 3 | 4 | 5 | @if (string.IsNullOrEmpty(ImageDataUrl)) 6 | { 7 | @FirstLetterOfName 8 | } 9 | else 10 | { 11 | 12 | } 13 | 14 | 15 | @FirstName @SecondName 16 | @Email 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Client/Shared/Components/UserCard.razor.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Client.Extensions; 2 | using Microsoft.AspNetCore.Components; 3 | using System.Threading.Tasks; 4 | using BlazorHero.CleanArchitecture.Shared.Constants.Storage; 5 | 6 | namespace BlazorHero.CleanArchitecture.Client.Shared.Components 7 | { 8 | public partial class UserCard 9 | { 10 | [Parameter] public string Class { get; set; } 11 | private string FirstName { get; set; } 12 | private string SecondName { get; set; } 13 | private string Email { get; set; } 14 | private char FirstLetterOfName { get; set; } 15 | 16 | [Parameter] 17 | public string ImageDataUrl { get; set; } 18 | 19 | protected override async Task OnAfterRenderAsync(bool firstRender) 20 | { 21 | if (firstRender) 22 | { 23 | await LoadDataAsync(); 24 | } 25 | } 26 | 27 | private async Task LoadDataAsync() 28 | { 29 | var state = await _stateProvider.GetAuthenticationStateAsync(); 30 | var user = state.User; 31 | 32 | this.Email = user.GetEmail().Replace(".com", string.Empty); 33 | this.FirstName = user.GetFirstName(); 34 | this.SecondName = user.GetLastName(); 35 | if (this.FirstName.Length > 0) 36 | { 37 | FirstLetterOfName = FirstName[0]; 38 | } 39 | var UserId = user.GetUserId(); 40 | var imageResponse = await _localStorage.GetItemAsync(StorageConstants.Local.UserImageURL); 41 | if (!string.IsNullOrEmpty(imageResponse)) 42 | { 43 | ImageDataUrl = imageResponse; 44 | } 45 | StateHasChanged(); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/Client/Shared/Dialogs/DeleteConfirmation.razor: -------------------------------------------------------------------------------- 1 | @namespace BlazorHero.CleanArchitecture.Client.Shared.Dialogs 2 | @inject Microsoft.Extensions.Localization.IStringLocalizer localizer 3 | 4 | 5 | 6 | 7 | @localizer["Delete Confirmation"] 8 | 9 | 10 | 11 | @ContentText 12 | 13 | 14 | @localizer["Cancel"] 15 | @localizer["Confirm"] 16 | 17 | 18 | @code { 19 | [CascadingParameter] MudDialogInstance MudDialog { get; set; } 20 | 21 | [Parameter] public string ContentText { get; set; } 22 | 23 | void Submit() 24 | { 25 | MudDialog.Close(DialogResult.Ok(true)); 26 | } 27 | void Cancel() => MudDialog.Cancel(); 28 | } -------------------------------------------------------------------------------- /src/Client/Shared/Dialogs/Logout.razor: -------------------------------------------------------------------------------- 1 | @using BlazorHero.CleanArchitecture.Shared.Constants.Application 2 | @using Microsoft.AspNetCore.SignalR.Client 3 | @namespace BlazorHero.CleanArchitecture.Client.Shared.Dialogs 4 | @inject Microsoft.Extensions.Localization.IStringLocalizer localizer 5 | 6 | 7 | 8 | 9 | @localizer["Logout Confirmation"] 10 | 11 | 12 | 13 | @ContentText 14 | 15 | 16 | @localizer["Cancel"] 17 | @ButtonText 18 | 19 | 20 | @code { 21 | [CascadingParameter] MudDialogInstance MudDialog { get; set; } 22 | 23 | [Parameter] public HubConnection HubConnection { get; set; } 24 | 25 | [Parameter] public string ContentText { get; set; } 26 | 27 | [Parameter] public string ButtonText { get; set; } 28 | 29 | [Parameter] public Color Color { get; set; } 30 | 31 | [Parameter] public string CurrentUserId { get; set; } 32 | 33 | async Task Submit() 34 | { 35 | await HubConnection.SendAsync(ApplicationConstants.SignalR.OnDisconnect, CurrentUserId); 36 | await _authenticationManager.Logout(); 37 | _navigationManager.NavigateTo("/login"); 38 | MudDialog.Close(DialogResult.Ok(true)); 39 | } 40 | void Cancel() => MudDialog.Cancel(); 41 | } -------------------------------------------------------------------------------- /src/Client/Shared/Error.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.Extensions.Logging 2 | @inject ILogger Logger 3 | 4 | 5 | @ChildContent 6 | 7 | 8 | @code { 9 | [Parameter] 10 | public RenderFragment ChildContent { get; set; } 11 | 12 | public void ProcessError(Exception ex) 13 | { 14 | Logger.LogError("Error:ProcessError - Type: {Type} Message: {Message}", 15 | ex.GetType(), ex.Message); 16 | //StateHasChanged(); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Client/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | @inject Microsoft.Extensions.Localization.IStringLocalizer _localizer 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/Client/Shared/MainLayout.razor.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Client.Infrastructure.Settings; 2 | using MudBlazor; 3 | using System; 4 | using System.Threading.Tasks; 5 | 6 | namespace BlazorHero.CleanArchitecture.Client.Shared 7 | { 8 | public partial class MainLayout : IDisposable 9 | { 10 | private MudTheme _currentTheme; 11 | private bool _rightToLeft = false; 12 | private async Task RightToLeftToggle(bool value) 13 | { 14 | _rightToLeft = value; 15 | await Task.CompletedTask; 16 | } 17 | 18 | protected override async Task OnInitializedAsync() 19 | { 20 | _currentTheme = BlazorHeroTheme.DefaultTheme; 21 | _currentTheme = await _clientPreferenceManager.GetCurrentThemeAsync(); 22 | _rightToLeft = await _clientPreferenceManager.IsRTL(); 23 | _interceptor.RegisterEvent(); 24 | } 25 | 26 | private async Task DarkMode() 27 | { 28 | bool isDarkMode = await _clientPreferenceManager.ToggleDarkModeAsync(); 29 | _currentTheme = isDarkMode 30 | ? BlazorHeroTheme.DefaultTheme 31 | : BlazorHeroTheme.DarkTheme; 32 | } 33 | 34 | public void Dispose() 35 | { 36 | _interceptor.DisposeEvent(); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/Client/wwwroot/css/loader.css: -------------------------------------------------------------------------------- 1 | .loader-mask { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | right: 0; 6 | bottom: 0; 7 | background-color: #fff; 8 | z-index: 99999; 9 | } 10 | 11 | .loader { 12 | position: absolute; 13 | left: 50%; 14 | top: 50%; 15 | width: 50px; 16 | height: 50px; 17 | font-size: 0; 18 | color: #00c9d0; 19 | display: inline-block; 20 | margin: -25px 0 0 -25px; 21 | text-indent: -9999em; 22 | -webkit-transform: translateZ(0); 23 | -ms-transform: translateZ(0); 24 | transform: translateZ(0); 25 | } 26 | 27 | .loader div { 28 | background-color: #00c9d0; 29 | display: inline-block; 30 | float: none; 31 | position: absolute; 32 | top: 0; 33 | left: 0; 34 | width: 50px; 35 | height: 50px; 36 | opacity: 0.5; 37 | border-radius: 50%; 38 | -webkit-animation: ballPulseDouble 2s ease-in-out infinite; 39 | animation: ballPulseDouble 2s ease-in-out infinite; 40 | } 41 | 42 | .loader div:last-child { 43 | -webkit-animation-delay: -1s; 44 | animation-delay: -1s; 45 | } 46 | 47 | @-webkit-keyframes ballPulseDouble { 48 | 0%, 100% { 49 | -webkit-transform: scale(0); 50 | transform: scale(0); 51 | } 52 | 53 | 50% { 54 | -webkit-transform: scale(1); 55 | transform: scale(1); 56 | } 57 | } 58 | 59 | @keyframes ballPulseDouble { 60 | 0%, 100% { 61 | -webkit-transform: scale(0); 62 | transform: scale(0); 63 | } 64 | 65 | 50% { 66 | -webkit-transform: scale(1); 67 | transform: scale(1); 68 | } 69 | } 70 | 71 | ::-webkit-scrollbar { 72 | width: 2px!important; 73 | } -------------------------------------------------------------------------------- /src/Client/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackhero/blazor-starter-kit/11f810d97cb66a251dfbee335b581fa9f1d1beab/src/Client/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/Client/wwwroot/js/file.js: -------------------------------------------------------------------------------- 1 | window.Download = (options) => { 2 | var fileUrl = "data:" + options.mimeType + ";base64," + options.byteArray; 3 | fetch(fileUrl) 4 | .then(response => response.blob()) 5 | .then(blob => { 6 | var link = window.document.createElement("a"); 7 | link.href = window.URL.createObjectURL(blob, { type: options.mimeType }); 8 | link.download = options.fileName; 9 | document.body.appendChild(link); 10 | link.click(); 11 | document.body.removeChild(link); 12 | }); 13 | } -------------------------------------------------------------------------------- /src/Client/wwwroot/js/scroll.js: -------------------------------------------------------------------------------- 1 | window.ScrollToBottom = (elementName) => { 2 | element = document.getElementById(elementName); 3 | element.scrollTop = element.scrollHeight - element.clientHeight; 4 | } -------------------------------------------------------------------------------- /src/Client/wwwroot/js/sounds.js: -------------------------------------------------------------------------------- 1 | window.PlayAudio = (elementName) => { 2 | document.getElementById(elementName).play(); 3 | } -------------------------------------------------------------------------------- /src/Client/wwwroot/media/notification.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackhero/blazor-starter-kit/11f810d97cb66a251dfbee335b581fa9f1d1beab/src/Client/wwwroot/media/notification.mp3 -------------------------------------------------------------------------------- /src/Domain/Contracts/AuditableEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorHero.CleanArchitecture.Domain.Contracts 4 | { 5 | public abstract class AuditableEntity : IAuditableEntity 6 | { 7 | public TId Id { get; set; } 8 | public string CreatedBy { get; set; } 9 | public DateTime CreatedOn { get; set; } 10 | public string LastModifiedBy { get; set; } 11 | public DateTime? LastModifiedOn { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Domain/Contracts/AuditableEntityWithExtendedAttributes.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace BlazorHero.CleanArchitecture.Domain.Contracts 4 | { 5 | public abstract class AuditableEntityWithExtendedAttributes 6 | : AuditableEntity, IEntityWithExtendedAttributes 7 | where TEntity : IEntity 8 | { 9 | public virtual ICollection ExtendedAttributes { get; set; } 10 | 11 | public AuditableEntityWithExtendedAttributes() 12 | { 13 | ExtendedAttributes = new HashSet(); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Domain/Contracts/IAuditableEntity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorHero.CleanArchitecture.Domain.Contracts 4 | { 5 | public interface IAuditableEntity : IAuditableEntity, IEntity 6 | { 7 | } 8 | 9 | public interface IAuditableEntity : IEntity 10 | { 11 | string CreatedBy { get; set; } 12 | 13 | DateTime CreatedOn { get; set; } 14 | 15 | string LastModifiedBy { get; set; } 16 | 17 | DateTime? LastModifiedOn { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Domain/Contracts/IEntity.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Domain.Contracts 2 | { 3 | public interface IEntity : IEntity 4 | { 5 | public TId Id { get; set; } 6 | } 7 | 8 | public interface IEntity 9 | { 10 | } 11 | } -------------------------------------------------------------------------------- /src/Domain/Contracts/IEntityAuditableExtendedAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Domain.Contracts 2 | { 3 | public interface IEntityAuditableExtendedAttribute 4 | : IEntityExtendedAttribute, IAuditableEntity 5 | where TEntity : IEntity 6 | { 7 | } 8 | 9 | public interface IEntityAuditableExtendedAttribute 10 | : IEntityExtendedAttribute, IAuditableEntity 11 | where TEntity : IEntity 12 | { 13 | } 14 | 15 | public interface IEntityAuditableExtendedAttribute : IEntityExtendedAttribute, IAuditableEntity 16 | { 17 | } 18 | } -------------------------------------------------------------------------------- /src/Domain/Contracts/IEntityWithExtendedAttributes.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace BlazorHero.CleanArchitecture.Domain.Contracts 4 | { 5 | public interface IEntityWithExtendedAttributes 6 | { 7 | public ICollection ExtendedAttributes { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Domain/Domain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | BlazorHero.CleanArchitecture.Domain 6 | BlazorHero.CleanArchitecture.Domain 7 | latest 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Domain/Entities/Catalog/Brand.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Domain.Contracts; 2 | 3 | namespace BlazorHero.CleanArchitecture.Domain.Entities.Catalog 4 | { 5 | public class Brand : AuditableEntity 6 | { 7 | public string Name { get; set; } 8 | public string Description { get; set; } 9 | public decimal Tax { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Domain/Entities/Catalog/Product.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Domain.Contracts; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace BlazorHero.CleanArchitecture.Domain.Entities.Catalog 5 | { 6 | public class Product : AuditableEntity 7 | { 8 | public string Name { get; set; } 9 | public string Barcode { get; set; } 10 | 11 | [Column(TypeName = "text")] 12 | public string ImageDataURL { get; set; } 13 | 14 | public string Description { get; set; } 15 | public decimal Rate { get; set; } 16 | public int BrandId { get; set; } 17 | public virtual Brand Brand { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /src/Domain/Entities/ExtendedAttributes/DocumentExtendedAttribute.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Domain.Contracts; 2 | using BlazorHero.CleanArchitecture.Domain.Entities.Misc; 3 | 4 | namespace BlazorHero.CleanArchitecture.Domain.Entities.ExtendedAttributes 5 | { 6 | public class DocumentExtendedAttribute : AuditableEntityExtendedAttribute 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /src/Domain/Entities/Misc/Document.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Domain.Contracts; 2 | using BlazorHero.CleanArchitecture.Domain.Entities.ExtendedAttributes; 3 | 4 | namespace BlazorHero.CleanArchitecture.Domain.Entities.Misc 5 | { 6 | public class Document : AuditableEntityWithExtendedAttributes 7 | { 8 | public string Title { get; set; } 9 | public string Description { get; set; } 10 | public bool IsPublic { get; set; } = false; 11 | public string URL { get; set; } 12 | public int DocumentTypeId { get; set; } 13 | public virtual DocumentType DocumentType { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Domain/Entities/Misc/DocumentType.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Domain.Contracts; 2 | 3 | namespace BlazorHero.CleanArchitecture.Domain.Entities.Misc 4 | { 5 | public class DocumentType : AuditableEntity 6 | { 7 | public string Name { get; set; } 8 | public string Description { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Domain/Enums/EntityExtendedAttributeType.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Domain.Enums 2 | { 3 | public enum EntityExtendedAttributeType : byte 4 | { 5 | Decimal = 1, 6 | 7 | Text = 2, 8 | 9 | DateTime = 3, 10 | 11 | Json = 4 12 | } 13 | } -------------------------------------------------------------------------------- /src/Infrastructure.Shared/Infrastructure.Shared.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | BlazorHero.CleanArchitecture.Infrastructure.Shared 6 | BlazorHero.CleanArchitecture.Infrastructure.Shared 7 | latest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Infrastructure.Shared/Services/SendGridMailService.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Infrastructure.Shared.Services 2 | { 3 | internal class SendGridMailService 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/Infrastructure.Shared/Services/SystemDateTimeService.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Interfaces.Services; 2 | using System; 3 | 4 | namespace BlazorHero.CleanArchitecture.Infrastructure.Shared.Services 5 | { 6 | public class SystemDateTimeService : IDateTimeService 7 | { 8 | public DateTime NowUtc => DateTime.UtcNow; 9 | } 10 | } -------------------------------------------------------------------------------- /src/Infrastructure/Configurations/EntityExtendedAttributeConfiguration.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Serialization.Options; 2 | using BlazorHero.CleanArchitecture.Application.Serialization.Serializers; 3 | using BlazorHero.CleanArchitecture.Domain.Contracts; 4 | using BlazorHero.CleanArchitecture.Infrastructure.Extensions; 5 | using Microsoft.EntityFrameworkCore; 6 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 7 | using Microsoft.Extensions.Options; 8 | 9 | namespace BlazorHero.CleanArchitecture.Infrastructure.Configurations 10 | { 11 | public class EntityExtendedAttributeConfiguration : IEntityTypeConfiguration 12 | { 13 | public void Configure(EntityTypeBuilder builder) 14 | { 15 | // This Converter will perform the conversion to and from Json to the desired type 16 | builder 17 | .Property(e => e.Json) 18 | .HasJsonConversion( 19 | new SystemTextJsonSerializer( 20 | new OptionsWrapper(new SystemTextJsonOptions()))); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Infrastructure/Extensions/ValueConversionExtensions.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Interfaces.Serialization.Serializers; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.ChangeTracking; 4 | using Microsoft.EntityFrameworkCore.Metadata.Builders; 5 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 6 | 7 | namespace BlazorHero.CleanArchitecture.Infrastructure.Extensions 8 | { 9 | public static class ValueConversionExtensions 10 | { 11 | public static PropertyBuilder HasJsonConversion(this PropertyBuilder propertyBuilder, IJsonSerializer serializer) where T : class//, new() 12 | { 13 | var converter = new ValueConverter 14 | ( 15 | v => serializer.Serialize(v), 16 | v => serializer.Deserialize(v) // ?? new T() 17 | ); 18 | 19 | var comparer = new ValueComparer 20 | ( 21 | (l, r) => serializer.Serialize(l) == serializer.Serialize(r), 22 | v => v == null ? 0 : serializer.Serialize(v).GetHashCode(), 23 | v => serializer.Deserialize(serializer.Serialize(v)) 24 | ); 25 | 26 | propertyBuilder.HasConversion(converter); 27 | propertyBuilder.Metadata.SetValueConverter(converter); 28 | propertyBuilder.Metadata.SetValueComparer(comparer); 29 | propertyBuilder.HasColumnType("nvarchar(max)"); 30 | 31 | return propertyBuilder; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Infrastructure/Mappings/AuditProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BlazorHero.CleanArchitecture.Infrastructure.Models.Audit; 3 | using BlazorHero.CleanArchitecture.Application.Responses.Audit; 4 | 5 | namespace BlazorHero.CleanArchitecture.Infrastructure.Mappings 6 | { 7 | public class AuditProfile : Profile 8 | { 9 | public AuditProfile() 10 | { 11 | CreateMap().ReverseMap(); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Infrastructure/Mappings/ChatHistoryProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BlazorHero.CleanArchitecture.Application.Interfaces.Chat; 3 | using BlazorHero.CleanArchitecture.Application.Models.Chat; 4 | using BlazorHero.CleanArchitecture.Infrastructure.Models.Identity; 5 | 6 | namespace BlazorHero.CleanArchitecture.Infrastructure.Mappings 7 | { 8 | public class ChatHistoryProfile : Profile 9 | { 10 | public ChatHistoryProfile() 11 | { 12 | CreateMap, ChatHistory>().ReverseMap(); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Infrastructure/Mappings/RoleClaimProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 3 | using BlazorHero.CleanArchitecture.Application.Responses.Identity; 4 | using BlazorHero.CleanArchitecture.Infrastructure.Models.Identity; 5 | 6 | namespace BlazorHero.CleanArchitecture.Infrastructure.Mappings 7 | { 8 | public class RoleClaimProfile : Profile 9 | { 10 | public RoleClaimProfile() 11 | { 12 | CreateMap() 13 | .ForMember(nameof(BlazorHeroRoleClaim.ClaimType), opt => opt.MapFrom(c => c.Type)) 14 | .ForMember(nameof(BlazorHeroRoleClaim.ClaimValue), opt => opt.MapFrom(c => c.Value)) 15 | .ReverseMap(); 16 | 17 | CreateMap() 18 | .ForMember(nameof(BlazorHeroRoleClaim.ClaimType), opt => opt.MapFrom(c => c.Type)) 19 | .ForMember(nameof(BlazorHeroRoleClaim.ClaimValue), opt => opt.MapFrom(c => c.Value)) 20 | .ReverseMap(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Infrastructure/Mappings/RoleProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BlazorHero.CleanArchitecture.Infrastructure.Models.Identity; 3 | using BlazorHero.CleanArchitecture.Application.Responses.Identity; 4 | 5 | namespace BlazorHero.CleanArchitecture.Infrastructure.Mappings 6 | { 7 | public class RoleProfile : Profile 8 | { 9 | public RoleProfile() 10 | { 11 | CreateMap().ReverseMap(); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/Infrastructure/Mappings/UserProfile.cs: -------------------------------------------------------------------------------- 1 | using AutoMapper; 2 | using BlazorHero.CleanArchitecture.Infrastructure.Models.Identity; 3 | using BlazorHero.CleanArchitecture.Application.Responses.Identity; 4 | 5 | namespace BlazorHero.CleanArchitecture.Infrastructure.Mappings 6 | { 7 | public class UserProfile : Profile 8 | { 9 | public UserProfile() 10 | { 11 | CreateMap().ReverseMap(); 12 | CreateMap().ReverseMap() 13 | .ForMember(dest => dest.EmailAddress, source => source.MapFrom(source => source.Email)); //Specific Mapping 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Infrastructure/Models/Audit/Audit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BlazorHero.CleanArchitecture.Domain.Contracts; 3 | 4 | namespace BlazorHero.CleanArchitecture.Infrastructure.Models.Audit 5 | { 6 | public class Audit : IEntity 7 | { 8 | public int Id { get; set; } 9 | public string UserId { get; set; } 10 | public string Type { get; set; } 11 | public string TableName { get; set; } 12 | public DateTime DateTime { get; set; } 13 | public string OldValues { get; set; } 14 | public string NewValues { get; set; } 15 | public string AffectedColumns { get; set; } 16 | public string PrimaryKey { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /src/Infrastructure/Models/Audit/AuditEntry.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Enums; 2 | using Microsoft.EntityFrameworkCore.ChangeTracking; 3 | using Newtonsoft.Json; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace BlazorHero.CleanArchitecture.Infrastructure.Models.Audit 9 | { 10 | public class AuditEntry 11 | { 12 | public AuditEntry(EntityEntry entry) 13 | { 14 | Entry = entry; 15 | } 16 | 17 | public EntityEntry Entry { get; } 18 | public string UserId { get; set; } 19 | public string TableName { get; set; } 20 | public Dictionary KeyValues { get; } = new(); 21 | public Dictionary OldValues { get; } = new(); 22 | public Dictionary NewValues { get; } = new(); 23 | public List TemporaryProperties { get; } = new(); 24 | public AuditType AuditType { get; set; } 25 | public List ChangedColumns { get; } = new(); 26 | public bool HasTemporaryProperties => TemporaryProperties.Any(); 27 | 28 | public Audit ToAudit() 29 | { 30 | var audit = new Audit 31 | { 32 | UserId = UserId, 33 | Type = AuditType.ToString(), 34 | TableName = TableName, 35 | DateTime = DateTime.UtcNow, 36 | PrimaryKey = JsonConvert.SerializeObject(KeyValues), 37 | OldValues = OldValues.Count == 0 ? null : JsonConvert.SerializeObject(OldValues), 38 | NewValues = NewValues.Count == 0 ? null : JsonConvert.SerializeObject(NewValues), 39 | AffectedColumns = ChangedColumns.Count == 0 ? null : JsonConvert.SerializeObject(ChangedColumns) 40 | }; 41 | return audit; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/Infrastructure/Models/Identity/BlazorHeroRole.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using BlazorHero.CleanArchitecture.Domain.Contracts; 4 | using Microsoft.AspNetCore.Identity; 5 | 6 | namespace BlazorHero.CleanArchitecture.Infrastructure.Models.Identity 7 | { 8 | public class BlazorHeroRole : IdentityRole, IAuditableEntity 9 | { 10 | public string Description { get; set; } 11 | public string CreatedBy { get; set; } 12 | public DateTime CreatedOn { get; set; } 13 | public string LastModifiedBy { get; set; } 14 | public DateTime? LastModifiedOn { get; set; } 15 | public virtual ICollection RoleClaims { get; set; } 16 | 17 | public BlazorHeroRole() : base() 18 | { 19 | RoleClaims = new HashSet(); 20 | } 21 | 22 | public BlazorHeroRole(string roleName, string roleDescription = null) : base(roleName) 23 | { 24 | RoleClaims = new HashSet(); 25 | Description = roleDescription; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Infrastructure/Models/Identity/BlazorHeroRoleClaim.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BlazorHero.CleanArchitecture.Domain.Contracts; 3 | using Microsoft.AspNetCore.Identity; 4 | 5 | namespace BlazorHero.CleanArchitecture.Infrastructure.Models.Identity 6 | { 7 | public class BlazorHeroRoleClaim : IdentityRoleClaim, IAuditableEntity 8 | { 9 | public string Description { get; set; } 10 | public string Group { get; set; } 11 | public string CreatedBy { get; set; } 12 | public DateTime CreatedOn { get; set; } 13 | public string LastModifiedBy { get; set; } 14 | public DateTime? LastModifiedOn { get; set; } 15 | public virtual BlazorHeroRole Role { get; set; } 16 | 17 | public BlazorHeroRoleClaim() : base() 18 | { 19 | } 20 | 21 | public BlazorHeroRoleClaim(string roleClaimDescription = null, string roleClaimGroup = null) : base() 22 | { 23 | Description = roleClaimDescription; 24 | Group = roleClaimGroup; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Infrastructure/Models/Identity/BlazorHeroUser.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Domain.Contracts; 2 | using Microsoft.AspNetCore.Identity; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations.Schema; 6 | using BlazorHero.CleanArchitecture.Application.Interfaces.Chat; 7 | using BlazorHero.CleanArchitecture.Application.Models.Chat; 8 | 9 | namespace BlazorHero.CleanArchitecture.Infrastructure.Models.Identity 10 | { 11 | public class BlazorHeroUser : IdentityUser, IChatUser, IAuditableEntity 12 | { 13 | public string FirstName { get; set; } 14 | 15 | public string LastName { get; set; } 16 | public string CreatedBy { get; set; } 17 | 18 | [Column(TypeName = "text")] 19 | public string ProfilePictureDataUrl { get; set; } 20 | 21 | public DateTime CreatedOn { get; set; } 22 | 23 | public string LastModifiedBy { get; set; } 24 | 25 | public DateTime? LastModifiedOn { get; set; } 26 | 27 | public bool IsDeleted { get; set; } 28 | 29 | public DateTime? DeletedOn { get; set; } 30 | public bool IsActive { get; set; } 31 | public string RefreshToken { get; set; } 32 | public DateTime RefreshTokenExpiryTime { get; set; } 33 | public virtual ICollection> ChatHistoryFromUsers { get; set; } 34 | public virtual ICollection> ChatHistoryToUsers { get; set; } 35 | 36 | public BlazorHeroUser() 37 | { 38 | ChatHistoryFromUsers = new HashSet>(); 39 | ChatHistoryToUsers = new HashSet>(); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/Infrastructure/Repositories/BrandRepository.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Interfaces.Repositories; 2 | using BlazorHero.CleanArchitecture.Domain.Entities.Catalog; 3 | 4 | namespace BlazorHero.CleanArchitecture.Infrastructure.Repositories 5 | { 6 | public class BrandRepository : IBrandRepository 7 | { 8 | private readonly IRepositoryAsync _repository; 9 | 10 | public BrandRepository(IRepositoryAsync repository) 11 | { 12 | _repository = repository; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Infrastructure/Repositories/DocumentRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Threading.Tasks; 3 | using BlazorHero.CleanArchitecture.Application.Interfaces.Repositories; 4 | using BlazorHero.CleanArchitecture.Domain.Entities.Misc; 5 | using Microsoft.EntityFrameworkCore; 6 | 7 | namespace BlazorHero.CleanArchitecture.Infrastructure.Repositories 8 | { 9 | public class DocumentRepository : IDocumentRepository 10 | { 11 | private readonly IRepositoryAsync _repository; 12 | 13 | public DocumentRepository(IRepositoryAsync repository) 14 | { 15 | _repository = repository; 16 | } 17 | 18 | public async Task IsDocumentTypeUsed(int documentTypeId) 19 | { 20 | return await _repository.Entities.AnyAsync(b => b.DocumentTypeId == documentTypeId); 21 | } 22 | 23 | public async Task IsDocumentExtendedAttributeUsed(int documentExtendedAttributeId) 24 | { 25 | return await _repository.Entities.AnyAsync(b => b.ExtendedAttributes.Any(x => x.Id == documentExtendedAttributeId)); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Infrastructure/Repositories/DocumentTypeRepository.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Interfaces.Repositories; 2 | using BlazorHero.CleanArchitecture.Domain.Entities.Misc; 3 | 4 | namespace BlazorHero.CleanArchitecture.Infrastructure.Repositories 5 | { 6 | public class DocumentTypeRepository : IDocumentTypeRepository 7 | { 8 | private readonly IRepositoryAsync _repository; 9 | 10 | public DocumentTypeRepository(IRepositoryAsync repository) 11 | { 12 | _repository = repository; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Infrastructure/Repositories/ProductRepository.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Interfaces.Repositories; 2 | using BlazorHero.CleanArchitecture.Domain.Entities.Catalog; 3 | using Microsoft.EntityFrameworkCore; 4 | using System.Threading.Tasks; 5 | 6 | namespace BlazorHero.CleanArchitecture.Infrastructure.Repositories 7 | { 8 | public class ProductRepository : IProductRepository 9 | { 10 | private readonly IRepositoryAsync _repository; 11 | 12 | public ProductRepository(IRepositoryAsync repository) 13 | { 14 | _repository = repository; 15 | } 16 | 17 | public async Task IsBrandUsed(int brandId) 18 | { 19 | return await _repository.Entities.AnyAsync(b => b.BrandId == brandId); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Infrastructure/Specifications/AuditFilterSpecification.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Infrastructure.Models.Audit; 2 | using BlazorHero.CleanArchitecture.Application.Specifications.Base; 3 | 4 | namespace BlazorHero.CleanArchitecture.Infrastructure.Specifications 5 | { 6 | public class AuditFilterSpecification : HeroSpecification 7 | { 8 | public AuditFilterSpecification(string userId, string searchString, bool searchInOldValues, bool searchInNewValues) 9 | { 10 | if (!string.IsNullOrEmpty(searchString)) 11 | { 12 | Criteria = p => (p.TableName.Contains(searchString) || searchInOldValues && p.OldValues.Contains(searchString) || searchInNewValues && p.NewValues.Contains(searchString)) && p.UserId == userId; 13 | } 14 | else 15 | { 16 | Criteria = p => p.UserId == userId; 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Infrastructure/Specifications/UserFilterSpecification.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Infrastructure.Models.Identity; 2 | using BlazorHero.CleanArchitecture.Application.Specifications.Base; 3 | 4 | namespace BlazorHero.CleanArchitecture.Infrastructure.Specifications 5 | { 6 | public class UserFilterSpecification : HeroSpecification 7 | { 8 | public UserFilterSpecification(string searchString) 9 | { 10 | if (!string.IsNullOrEmpty(searchString)) 11 | { 12 | Criteria = p => p.FirstName.Contains(searchString) || p.LastName.Contains(searchString) || p.Email.Contains(searchString) || p.PhoneNumber.Contains(searchString) || p.UserName.Contains(searchString); 13 | } 14 | else 15 | { 16 | Criteria = p => true; 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Server/Controllers/BaseApiController.cs: -------------------------------------------------------------------------------- 1 | using MediatR; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Logging; 5 | 6 | namespace BlazorHero.CleanArchitecture.Server.Controllers 7 | { 8 | /// 9 | /// Abstract BaseApi Controller Class 10 | /// 11 | [ApiController] 12 | [Route("api/v{version:apiVersion}/[controller]")] 13 | public abstract class BaseApiController : ControllerBase 14 | { 15 | private IMediator _mediatorInstance; 16 | private ILogger _loggerInstance; 17 | protected IMediator _mediator => _mediatorInstance ??= HttpContext.RequestServices.GetService(); 18 | protected ILogger _logger => _loggerInstance ??= HttpContext.RequestServices.GetService>(); 19 | } 20 | } -------------------------------------------------------------------------------- /src/Server/Controllers/Identity/TokenController.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Interfaces.Services; 2 | using BlazorHero.CleanArchitecture.Application.Interfaces.Services.Identity; 3 | using BlazorHero.CleanArchitecture.Application.Requests.Identity; 4 | using Microsoft.AspNetCore.Mvc; 5 | using System.Threading.Tasks; 6 | 7 | namespace BlazorHero.CleanArchitecture.Server.Controllers.Identity 8 | { 9 | [Route("api/identity/token")] 10 | [ApiController] 11 | public class TokenController : ControllerBase 12 | { 13 | private readonly ITokenService _identityService; 14 | 15 | public TokenController(ITokenService identityService, ICurrentUserService currentUserService) 16 | { 17 | _identityService = identityService; 18 | } 19 | 20 | /// 21 | /// Get Token (Email, Password) 22 | /// 23 | /// 24 | /// Status 200 OK 25 | [HttpPost] 26 | public async Task Get(TokenRequest model) 27 | { 28 | var response = await _identityService.LoginAsync(model); 29 | return Ok(response); 30 | } 31 | 32 | /// 33 | /// Refresh Token 34 | /// 35 | /// 36 | /// Status 200 OK 37 | [HttpPost("refresh")] 38 | public async Task Refresh([FromBody] RefreshTokenRequest model) 39 | { 40 | var response = await _identityService.GetRefreshTokenAsync(model); 41 | return Ok(response); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/Server/Controllers/Utilities/PreferencesController.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using BlazorHero.CleanArchitecture.Server.Managers.Preferences; 3 | using BlazorHero.CleanArchitecture.Shared.Constants.Permission; 4 | using Microsoft.AspNetCore.Authorization; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace BlazorHero.CleanArchitecture.Server.Controllers.Utilities 8 | { 9 | [Route("api/[controller]")] 10 | [ApiController] 11 | public class PreferencesController : ControllerBase 12 | { 13 | private readonly ServerPreferenceManager _serverPreferenceManager; 14 | 15 | public PreferencesController(ServerPreferenceManager serverPreferenceManager) 16 | { 17 | _serverPreferenceManager = serverPreferenceManager; 18 | } 19 | 20 | /// 21 | /// Change Language Preference 22 | /// 23 | /// 24 | /// Status 200 OK 25 | [Authorize(Policy = Permissions.Preferences.ChangeLanguage)] 26 | [HttpPost("changeLanguage")] 27 | public async Task ChangeLanguageAsync(string languageCode) 28 | { 29 | var result = await _serverPreferenceManager.ChangeLanguageAsync(languageCode); 30 | return Ok(result); 31 | } 32 | 33 | //TODO - add actions 34 | } 35 | } -------------------------------------------------------------------------------- /src/Server/Controllers/v1/DashboardController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Mvc; 3 | using System.Threading.Tasks; 4 | using BlazorHero.CleanArchitecture.Application.Features.Dashboards.Queries.GetData; 5 | using BlazorHero.CleanArchitecture.Shared.Constants.Permission; 6 | 7 | namespace BlazorHero.CleanArchitecture.Server.Controllers.v1 8 | { 9 | [ApiController] 10 | public class DashboardController : BaseApiController 11 | { 12 | /// 13 | /// Get Dashboard Data 14 | /// 15 | /// Status 200 OK 16 | [Authorize(Policy = Permissions.Dashboards.View)] 17 | [HttpGet] 18 | public async Task GetDataAsync() 19 | { 20 | var result = await _mediator.Send(new GetDashboardDataQuery()); 21 | return Ok(result); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Server/Dockerfile: -------------------------------------------------------------------------------- 1 | #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. 2 | 3 | FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base 4 | ENV ASPNETCORE_URLS=https://+:5005;http://+:5006 5 | WORKDIR /app 6 | EXPOSE 5005 7 | EXPOSE 5006 8 | 9 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 10 | WORKDIR / 11 | COPY ["src/Server/Server.csproj", "src/Server/"] 12 | COPY ["src/Application/Application.csproj", "src/Application/"] 13 | COPY ["src/Domain/Domain.csproj", "src/Domain/"] 14 | COPY ["src/Shared/Shared.csproj", "src/Shared/"] 15 | COPY ["src/Infrastructure.Shared/Infrastructure.Shared.csproj", "src/Infrastructure.Shared/"] 16 | COPY ["src/Infrastructure/Infrastructure.csproj", "src/Infrastructure/"] 17 | COPY ["src/Client/Client.csproj", "src/Client/"] 18 | COPY ["src/Client.Infrastructure/Client.Infrastructure.csproj", "src/Client.Infrastructure/"] 19 | RUN dotnet restore "src/Server/Server.csproj" --disable-parallel 20 | COPY . . 21 | WORKDIR "src/Server" 22 | RUN dotnet build "Server.csproj" -c Release -o /app/build 23 | 24 | FROM build AS publish 25 | RUN dotnet publish "Server.csproj" -c Release -o /app/publish 26 | 27 | FROM base AS final 28 | WORKDIR /app 29 | COPY --from=publish /app/publish . 30 | WORKDIR /app/Files 31 | WORKDIR /app 32 | ENTRYPOINT ["dotnet", "BlazorHero.CleanArchitecture.Server.dll"] 33 | -------------------------------------------------------------------------------- /src/Server/Extensions/HostBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.Hosting; 3 | using Serilog; 4 | 5 | namespace BlazorHero.CleanArchitecture.Server.Extensions 6 | { 7 | internal static class HostBuilderExtensions 8 | { 9 | internal static IHostBuilder UseSerilog(this IHostBuilder builder) 10 | { 11 | var configuration = new ConfigurationBuilder() 12 | .AddJsonFile("appsettings.Development.json") 13 | .AddJsonFile("appsettings.json") 14 | .AddEnvironmentVariables() 15 | .Build(); 16 | Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(configuration).CreateLogger(); 17 | SerilogHostBuilderExtensions.UseSerilog(builder); 18 | return builder; 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Server/Files/Images/Products/demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fullstackhero/blazor-starter-kit/11f810d97cb66a251dfbee335b581fa9f1d1beab/src/Server/Files/Images/Products/demo.jpg -------------------------------------------------------------------------------- /src/Server/Filters/HangfireAuthorizationFilter.cs: -------------------------------------------------------------------------------- 1 | using Hangfire.Dashboard; 2 | 3 | namespace BlazorHero.CleanArchitecture.Server.Filters 4 | { 5 | public class HangfireAuthorizationFilter : IDashboardAuthorizationFilter 6 | { 7 | public bool Authorize(DashboardContext context) 8 | { 9 | //TODO implement authorization logic 10 | 11 | //var httpContext = context.GetHttpContext(); 12 | 13 | // Allow all authenticated users to see the Dashboard (potentially dangerous). 14 | //return httpContext.User.Identity.IsAuthenticated; 15 | //return httpContext.User.IsInRole(Permissions.Hangfire.View); 16 | 17 | return true; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Server/Localization/ServerCommonResources.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Server.Localization 2 | { 3 | internal class ServerCommonResources 4 | { 5 | // Used to localize strings in static classes 6 | } 7 | } -------------------------------------------------------------------------------- /src/Server/Localization/ServerLocalizer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Localization; 2 | 3 | namespace BlazorHero.CleanArchitecture.Server.Localization 4 | { 5 | internal class ServerLocalizer where T : class 6 | { 7 | public IStringLocalizer Localizer { get; } 8 | 9 | public ServerLocalizer(IStringLocalizer localizer) 10 | { 11 | Localizer = localizer; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Server/Managers/Preferences/IServerPreferenceManager.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Shared.Managers; 2 | 3 | namespace BlazorHero.CleanArchitecture.Server.Managers.Preferences 4 | { 5 | public interface IServerPreferenceManager : IPreferenceManager 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Server/Middlewares/RequestCultureMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Http; 5 | 6 | namespace BlazorHero.CleanArchitecture.Server.Middlewares 7 | { 8 | public class RequestCultureMiddleware 9 | { 10 | private readonly RequestDelegate _next; 11 | 12 | public RequestCultureMiddleware(RequestDelegate next) 13 | { 14 | _next = next; 15 | } 16 | 17 | public async Task InvokeAsync(HttpContext context) 18 | { 19 | var cultureQuery = context.Request.Query["culture"]; 20 | if (!string.IsNullOrWhiteSpace(cultureQuery)) 21 | { 22 | var culture = new CultureInfo(cultureQuery); 23 | 24 | CultureInfo.CurrentCulture = culture; 25 | CultureInfo.CurrentUICulture = culture; 26 | } 27 | else if (context.Request.Headers.ContainsKey("Accept-Language")) 28 | { 29 | var cultureHeader = context.Request.Headers["Accept-Language"]; 30 | if (cultureHeader.Any()) 31 | { 32 | var culture = new CultureInfo(cultureHeader.First().Split(',').First().Trim()); 33 | 34 | CultureInfo.CurrentCulture = culture; 35 | CultureInfo.CurrentUICulture = culture; 36 | } 37 | } 38 | 39 | await _next(context); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/Server/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using BlazorHero.CleanArchitecture.Infrastructure.Contexts; 4 | using BlazorHero.CleanArchitecture.Server.Extensions; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.EntityFrameworkCore; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Hosting; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace BlazorHero.CleanArchitecture.Server 12 | { 13 | public class Program 14 | { 15 | public async static Task Main(string[] args) 16 | { 17 | var host = CreateHostBuilder(args).Build(); 18 | 19 | using (var scope = host.Services.CreateScope()) 20 | { 21 | var services = scope.ServiceProvider; 22 | 23 | try 24 | { 25 | var context = services.GetRequiredService(); 26 | 27 | if (context.Database.IsSqlServer()) 28 | { 29 | context.Database.Migrate(); 30 | } 31 | } 32 | catch (Exception ex) 33 | { 34 | var logger = scope.ServiceProvider.GetRequiredService>(); 35 | 36 | logger.LogError(ex, "An error occurred while migrating or seeding the database."); 37 | 38 | throw; 39 | } 40 | } 41 | 42 | await host.RunAsync(); 43 | } 44 | 45 | public static IHostBuilder CreateHostBuilder(string[] args) => 46 | Host.CreateDefaultBuilder(args) 47 | .UseSerilog() 48 | .ConfigureWebHostDefaults(webBuilder => 49 | { 50 | webBuilder.UseStaticWebAssets(); 51 | webBuilder.UseStartup(); 52 | }); 53 | } 54 | } -------------------------------------------------------------------------------- /src/Server/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:65410", 7 | "sslPort": 44398 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | }, 17 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}" 18 | }, 19 | "BlazorHero.CleanArchitecture.Server": { 20 | "commandName": "Project", 21 | "launchBrowser": true, 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | }, 25 | "dotnetRunMessages": "true", 26 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 27 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}" 28 | }, 29 | "Docker": { 30 | "commandName": "Docker", 31 | "launchBrowser": true, 32 | "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", 33 | "publishAllPorts": true, 34 | "useSSL": true 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/Server/Services/CurrentUserService.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Application.Interfaces.Services; 2 | using Microsoft.AspNetCore.Http; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Security.Claims; 6 | 7 | namespace BlazorHero.CleanArchitecture.Server.Services 8 | { 9 | public class CurrentUserService : ICurrentUserService 10 | { 11 | public CurrentUserService(IHttpContextAccessor httpContextAccessor) 12 | { 13 | UserId = httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier); 14 | Claims = httpContextAccessor.HttpContext?.User?.Claims.AsEnumerable().Select(item => new KeyValuePair(item.Type, item.Value)).ToList(); 15 | } 16 | 17 | public string UserId { get; } 18 | public List> Claims { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /src/Server/Settings/ServerPreference.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using BlazorHero.CleanArchitecture.Shared.Constants.Localization; 3 | using BlazorHero.CleanArchitecture.Shared.Settings; 4 | 5 | namespace BlazorHero.CleanArchitecture.Server.Settings 6 | { 7 | public record ServerPreference : IPreference 8 | { 9 | public string LanguageCode { get; set; } = LocalizationConstants.SupportedLanguages.FirstOrDefault()?.Code ?? "en-US"; 10 | 11 | //TODO - add server preferences 12 | } 13 | } -------------------------------------------------------------------------------- /src/Server/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Data Source=(localdb)\\mssqllocaldb;Initial Catalog=BlazorHero.CleanArchitecture;Integrated Security=True;MultipleActiveResultSets=True" 4 | }, 5 | "Logging": { 6 | "LogLevel": { 7 | "Default": "Information", 8 | "Microsoft": "Warning", 9 | "Microsoft.Hosting.Lifetime": "Information" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Shared/Constants/Localization/LanguageCode.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Shared.Constants.Localization 2 | { 3 | public class LanguageCode 4 | { 5 | public string DisplayName { get; set; } 6 | public string Code { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Shared/Constants/Permission/ApplicationClaimType.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Shared.Constants.Permission 2 | { 3 | public static class ApplicationClaimTypes 4 | { 5 | public const string Permission = "Permission"; 6 | } 7 | } -------------------------------------------------------------------------------- /src/Shared/Constants/Role/RoleConstants.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Shared.Constants.Role 2 | { 3 | public static class RoleConstants 4 | { 5 | public const string AdministratorRole = "Administrator"; 6 | public const string BasicRole = "Basic"; 7 | public const string DefaultPassword = "123Pa$$word!"; 8 | } 9 | } -------------------------------------------------------------------------------- /src/Shared/Constants/Storage/StorageConstants.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Shared.Constants.Storage 2 | { 3 | public static class StorageConstants 4 | { 5 | public static class Local 6 | { 7 | public static string Preference = "clientPreference"; 8 | 9 | public static string AuthToken = "authToken"; 10 | public static string RefreshToken = "refreshToken"; 11 | public static string UserImageURL = "userImageURL"; 12 | } 13 | 14 | public static class Server 15 | { 16 | public static string Preference = "serverPreference"; 17 | 18 | //TODO - add 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/Shared/Constants/User/UserConstants.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Shared.Constants.User 2 | { 3 | public static class UserConstants 4 | { 5 | public const string DefaultPassword = "123Pa$$word!"; 6 | } 7 | } -------------------------------------------------------------------------------- /src/Shared/Managers/IPreferenceManager.cs: -------------------------------------------------------------------------------- 1 | using BlazorHero.CleanArchitecture.Shared.Settings; 2 | using System.Threading.Tasks; 3 | using BlazorHero.CleanArchitecture.Shared.Wrapper; 4 | 5 | namespace BlazorHero.CleanArchitecture.Shared.Managers 6 | { 7 | public interface IPreferenceManager 8 | { 9 | Task SetPreference(IPreference preference); 10 | 11 | Task GetPreference(); 12 | 13 | Task ChangeLanguageAsync(string languageCode); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Shared/Models/User.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BlazorHero.CleanArchitecture.Shared.Models 4 | { 5 | public class User 6 | { 7 | public string FirstName { get; set; } 8 | 9 | public string LastName { get; set; } 10 | 11 | public string CreatedBy { get; set; } 12 | 13 | public DateTime CreatedOn { get; set; } 14 | 15 | public string LastModifiedBy { get; set; } 16 | 17 | public DateTime? LastModifiedOn { get; set; } 18 | 19 | public bool IsDeleted { get; set; } 20 | 21 | public DateTime? DeletedOn { get; set; } 22 | 23 | public bool IsActive { get; set; } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Shared/Settings/IPreference.cs: -------------------------------------------------------------------------------- 1 | namespace BlazorHero.CleanArchitecture.Shared.Settings 2 | { 3 | public interface IPreference 4 | { 5 | public string LanguageCode { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/Shared/Shared.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | BlazorHero.CleanArchitecture.Shared 6 | BlazorHero.CleanArchitecture.Shared 7 | latest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Shared/Wrapper/IResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace BlazorHero.CleanArchitecture.Shared.Wrapper 4 | { 5 | public interface IResult 6 | { 7 | List Messages { get; set; } 8 | 9 | bool Succeeded { get; set; } 10 | } 11 | 12 | public interface IResult : IResult 13 | { 14 | T Data { get; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Shared/Wrapper/PaginatedResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace BlazorHero.CleanArchitecture.Shared.Wrapper 5 | { 6 | public class PaginatedResult : Result 7 | { 8 | public PaginatedResult(List data) 9 | { 10 | Data = data; 11 | } 12 | 13 | public List Data { get; set; } 14 | 15 | internal PaginatedResult(bool succeeded, List data = default, List messages = null, int count = 0, int page = 1, int pageSize = 10) 16 | { 17 | Data = data; 18 | CurrentPage = page; 19 | Succeeded = succeeded; 20 | PageSize = pageSize; 21 | TotalPages = (int)Math.Ceiling(count / (double)pageSize); 22 | TotalCount = count; 23 | } 24 | 25 | public static PaginatedResult Failure(List messages) 26 | { 27 | return new PaginatedResult(false, default, messages); 28 | } 29 | 30 | public static PaginatedResult Success(List data, int count, int page, int pageSize) 31 | { 32 | return new PaginatedResult(true, data, null, count, page, pageSize); 33 | } 34 | 35 | public int CurrentPage { get; set; } 36 | 37 | public int TotalPages { get; set; } 38 | 39 | public int TotalCount { get; set; } 40 | public int PageSize { get; set; } 41 | 42 | public bool HasPreviousPage => CurrentPage > 1; 43 | 44 | public bool HasNextPage => CurrentPage < TotalPages; 45 | } 46 | } --------------------------------------------------------------------------------