From ec2af0e6e64dbbe7947d0c1670d924ce278f1d54 Mon Sep 17 00:00:00 2001 From: Robert Good Date: Thu, 29 Jan 2026 23:48:56 -0800 Subject: [PATCH 1/3] flipped to fluentui --- .azure/modules/afd-azurefrontdoor.bicep | 31 + .azure/modules/api-appservice.bicep | 68 +- .azure/modules/apim-apimanagement.bicep | 30 +- .../modules/appcs-appconfigurationstore.bicep | 45 +- .azure/modules/appi-applicationinsights.bicep | 29 +- .azure/modules/bot-botservice.bicep | 54 +- .../modules/cdn-contentdeliverynetwork.bicep | 21 +- .azure/modules/cog-cognitiveservices.bicep | 10 +- .azure/modules/cosmos-cosmosdb.bicep | 18 +- .azure/modules/dgw-onpremdatagateway.bicep | 18 +- .azure/modules/ftpcn-ftpapiconnection.bicep | 42 +- .azure/modules/func-functionsapp.bicep | 35 + .azure/modules/hcn-hybridconnection.bicep | 10 +- .azure/modules/hnbind-hostNameBinding.bicep | 7 +- .azure/modules/kv-keyvault.bicep | 52 +- .../modules/luis-languageunderstanding.bicep | 16 + .../modules/nested_newWorkspaceTemplate.bicep | 7 - .azure/modules/nsg-networksecuritygroup.bicep | 33 + .../nsgrule-networksecuritygrouprule.bicep | 26 + .azure/modules/o365cn-o365apiconnection.bicep | 15 +- .azure/modules/plan-appplan.bicep | 39 - .azure/modules/plan-appserviceplan.bicep | 82 +- .azure/modules/qna-qnamaker.bicep | 4 - .azure/modules/relay-relaynamespace.bicep | 10 +- .azure/modules/rg-resourcegroup.bicep | 8 + .azure/modules/sb-servicebus.bicep | 30 +- .../modules/sent-loganalyticsworkspace.bicep | 45 + .../modules/sftpcn-sftpsshapiconnection.bicep | 39 +- .../modules/sjc-schedulejobcollection.bicep | 20 +- .../modules/snet-virtualnetworksubnet.bicep | 23 + .../spocn-sharepointonlineapiconnection.bicep | 17 +- .azure/modules/sql-sqlserver.bicep | 9 + .azure/modules/sql-sqlserverdatabase.bicep | 26 + .azure/modules/sqlcn-sqldgwconnection.bicep | 42 +- .azure/modules/sqldb-sqldatabase.bicep | 21 +- .azure/modules/st-storageaccount.bicep | 37 +- .../modules/st-storageaccount1container.bicep | 117 - .../modules/st-storageaccount2container.bicep | 142 - .../modules/st-storageaccountcontainers.bicep | 102 - .azure/modules/stapp-staticwebapp.bicep | 23 +- .../stblobcn-storageblobapiconnection.bicep | 22 +- ...sttablecn-storagetablesapiconnection.bicep | 18 +- .../modules/teamscn-teamsapiconnection.bicep | 15 +- .azure/modules/vnet-virtualnetwork.bicep | 31 + .../vnetpeer-virtualnetworkpeering.bicep | 36 + .azure/modules/wcert-webcertificate.bicep | 41 +- .azure/modules/web-appservice.bicep | 68 +- .../modules/work-loganalyticsworkspace.bicep | 17 +- .azure/templates/landingzone-api-sql.bicep | 101 - .azure/templates/landingzone-shared.bicep | 31 - .../templates/landingzone-web-api-sql.bicep | 70 + .azure/templates/platform-hub-mgmt.bicep | 47 + .../platform-hub-network-publicroute.bicep | 138 + .azure/templates/platform-spoke-mgmt.bicep | 88 + .../platform-spoke-network-publicroute.bicep | 73 + ...landingzone-api-sql-development.bicepparam | 37 - .../landingzone-api-sql-production.bicepparam | 37 - .../landingzone-shared-development.bicepparam | 14 - .../landingzone-shared-production.bicepparam | 14 - .../landingzone-web-api-sql-dev.bicepparam | 29 + .../platform-hub-mgmt-plat.bicepparam | 27 + ...rm-hub-network-publicroute-plat.bicepparam | 29 + .../platform-spoke-mgmt-dev.bicepparam | 36 + ...m-spoke-network-publicroute-dev.bicepparam | 25 + .../COMPANY-PRODUCT-api-cd-appservice.yml | 71 - .../COMPANY-PRODUCT-api-ci-cd-AppService.yml | 200 - .../COMPANY-PRODUCT-api-ci-cd-IIS.yml | 296 - .../pipelines/COMPANY-PRODUCT-api-ci.yml | 87 - .azuredevops/pipelines/COMPANY-PRODUCT-ef.yml | 125 - .../pipelines/COMPANY-PRODUCT-func.yml | 185 - .../COMPANY-PRODUCT-infrastructure-bicep.yml | 144 - ...OMPANY-PRODUCT-infrastructure-template.yml | 154 - .../pipelines/COMPANY-PRODUCT-landingzone.yml | 85 - .../pipelines/COMPANY-PRODUCT-nuget.yml | 115 - .../pipelines/COMPANY-PRODUCT-tests.yml | 42 - .../pipelines/COMPANY-PRODUCT-webjob.yml | 149 - .../pipelines/COMPANY-PRODUCT-worker.yml | 174 - .azuredevops/scripts/Set-Version.ps1 | 82 - .azuredevops/scripts/System.psm1 | 1500 -- .azuredevops/steps/api-deploy-steps.yml | 35 - .../steps/api-infrastructure-steps.yml | 95 - .azuredevops/steps/api-settings-steps.yml | 45 - .../steps/appcs-infrastructure-steps.yml | 53 - .azuredevops/steps/appcs-settings-steps.yml | 56 - .../steps/appsetting-deploy-steps.yml | 35 - .../steps/appsettings-deploy-steps.yml | 36 - .azuredevops/steps/blob-deploy-steps.yml | 30 - .azuredevops/steps/cog-deploy-steps.yml | 62 - .azuredevops/steps/dotnet-archive-steps.yml | 90 - .azuredevops/steps/dotnet-build-steps.yml | 44 - .azuredevops/steps/dotnet-output-steps.yml | 69 - .azuredevops/steps/dotnet-pack-steps.yml | 71 - .azuredevops/steps/dotnet-publish-steps.yml | 71 - .azuredevops/steps/dotnet-test-steps.yml | 71 - .azuredevops/steps/ef-deploy-steps.yml | 106 - .azuredevops/steps/ef-publish-steps.yml | 69 - .azuredevops/steps/func-build-steps.yml | 90 - .azuredevops/steps/func-deploy-steps.yml | 35 - .../steps/func-infrastructure-plan-steps.yml | 73 - .../func-infrastructure-plan-vnet-steps.yml | 120 - .../steps/func-infrastructure-steps.yml | 107 - .azuredevops/steps/func-settings-steps.yml | 51 - .../steps/hcm-infrastructure-steps.yml | 53 - .azuredevops/steps/integration-test-steps.yml | 46 - .../landingzone-infrastructure-steps.yml | 147 - .../steps/logic-infrastructure-steps.yml | 53 - .azuredevops/steps/msbuild-build-steps.yml | 51 - .azuredevops/steps/npm-build-steps.yml | 83 - .azuredevops/steps/npm-test-steps.yml | 35 - .../steps/nuget-deploy-external-steps.yml | 38 - .../steps/nuget-deploy-internal-steps.yml | 33 - .azuredevops/steps/nuget-pack-steps.yml | 63 - .../steps/sb-infrastructure-steps.yml | 53 - .../steps/sqldb-infrastructure-steps.yml | 67 - .../steps/stapp-infrastructure-steps.yml | 59 - .azuredevops/steps/web-deploy-steps.yml | 35 - .../steps/web-infrastructure-steps.yml | 89 - .azuredevops/steps/web-settings-steps.yml | 48 - .azuredevops/steps/webjob-build-steps.yml | 96 - .azuredevops/steps/webjob-deploy-steps.yml | 35 - .../steps/webjob-infrastructure-steps.yml | 89 - .azuredevops/steps/webjob-settings-steps.yml | 48 - .azuredevops/steps/webstatic-pack-steps.yml | 62 - .azuredevops/variables/common-ci.yml | 17 - .azuredevops/variables/development-cd.yml | 15 - .../variables/development-infrastructure.yml | 25 - .azuredevops/variables/production-cd.yml | 15 - .../variables/production-infrastructure.yml | 25 - .github/copilot-instructions.md | 43 + .github/scripts/Get-AzureAd.ps1 | 50 - .github/scripts/Install-AzureCli.ps1 | 1 - .../scripts/New-AzResourceGroupDeployment.ps1 | 7 - .github/scripts/Remove-LogicApp.ps1 | 50 - .github/scripts/Remove-SQLServer.ps1 | 50 - .github/scripts/Remove-StorageAccount.ps1 | 50 - .github/scripts/Set-ApiConnectorAuth.ps1 | 79 - .github/scripts/Set-Version.ps1 | 82 - .../{ => cd}/Add-ManagedIdentityRole.ps1 | 0 .../{ => cd}/Get-ManagedIdentityRole.ps1 | 0 .../{ => cd}/Remove-ManagedIdentityRole.ps1 | 0 .github/scripts/cd/Remove-NugetPackage.ps1 | 58 + .github/scripts/ci/Get-Version.ps1 | 94 + .github/scripts/ci/Set-Version.ps1 | 101 + .../scripts/{ => iac}/Copy-Custompolicy.ps1 | 0 .../scripts/iac}/Install-AzureCli.ps1 | 0 .../{ => iac}/New-AzServicePrinciple.ps1 | 2 +- .../scripts/{ => iac}/New-SelfSignedCert.ps1 | 38 +- .../scripts/{ => iac}/Remove-LandingZone.ps1 | 2 +- .../{ => iac}/Set-AzKeyVaultPolicy.ps1 | 2 +- .../scripts/repo/New-GithubRepoBootstrap.ps1 | 254 + .github/scripts/repo/New-GithubSecret.ps1 | 69 + .../scripts/{ => repo}/az-create-for-rbac.cmd | 0 .github/workflows/gtc-platform-hub-iac.yml | 127 + .github/workflows/gtc-platform-spoke-iac.yml | 128 + .../workflows/gtc-rg-semkernel-api-ci-cd.yml | 318 - ...g-semkernel-iac.yml => gtc-semker-iac.yml} | 76 +- .github/workflows/gtc-semker-web-api-sql.yml | 326 + .gitignore | 1 + README.md | 9 +- data/.vscode/tasks.json | 17 + data/Admin/Drop Tables.sql | 45 + .../Chat/ChatMessages.sql | 0 data/Chat/ChatSessions.sql | 0 data/Chat/Multimedia.sql | 1 + data/Data.sqlproj | 19 + data/Identity.sql | 0 src/.editorconfig | 84 +- src/App.razor | 0 .../Analytics/ClarityAnalytics.razor | 4 +- .../Aspects.Components.csproj | 34 + .../Auth/Components/UserAuthMenu.razor | 31 + .../Auth/Components/UserProfile.razor | 78 + .../Auth/IUserClaimsInfo.cs | 50 + .../DownstreamApiAccessTokenProvider.cs | 26 + .../Middleware/MsGraphAccessTokenProvider.cs | 21 + ...ginLogoutEndpointRouteBuilderExtensions.cs | 34 + .../Auth/Routing/RedirectToAccessDenied.razor | 11 + .../Routing/RedirectToResetPassword.razor | 10 + .../Auth/Routing/RedirectToSignIn.razor | 10 + .../Auth/Routing/RedirectToSignOut.razor | 12 + .../Auth/Routing/RouteConstants.cs | 12 + .../Auth/Services/IUserSyncService.cs | 9 + src/Aspects.Components/Auth/UserClaimsInfo.cs | 64 + src/Aspects.Components/FormChangeType.cs | 8 + src/Aspects.Components/Icons/ArrowIcon.razor | 15 + .../Skeleton/SkeletonList.razor | 21 +- .../Skeleton/SkeletonTable.razor | 53 + .../Typography/H1Label.razor | 17 + .../Typography/H2Label.razor | 17 + .../Typography/H3Label.razor | 17 + .../Typography/PLabel.razor | 18 + .../Wizard/WizardExample.razor | 20 + .../Wizard/WizardLayout.razor | 101 + .../Wizard/WizardStep.razor | 27 + src/Aspects.Components/_Imports.razor | 6 + .../AssertionFailedException.cs | 7 - src/Common.Assertion/AssertionRules.cs | 55 - src/Common.Assertion/AssertionScope.cs | 31 - src/Common.Assertion/Common.Assertion.csproj | 13 - .../Common.Domain.Types.csproj | 11 - .../DomainEntity/DomainEntity.cs | 95 - .../DomainEntity/IDomainEntity.cs | 15 - .../DomainEvent/DomainEventExample.cs | 143 - .../DomainEvent/IDomainEvent.cs | 6 - ...mon.HttpClient.ClientCredentialFlow.csproj | 20 - .../ConfigureServices.cs | 38 - .../Middleware/BearerToken.cs | 65 - .../Middleware/TokenHandler.cs | 21 - .../Options/ClientCredential.cs | 17 - src/Common.Mediator/Common.Mediator.csproj | 17 - src/Common.Mediator/IPipelineBehavior.cs | 14 - src/Common.Mediator/IRequest.cs | 5 - src/Common.Mediator/IRequestDispatcher.cs | 7 - src/Common.Mediator/IRequestHandler.cs | 13 - src/Common.Mediator/IRequestPreProcessor.cs | 6 - src/Common.Mediator/ISender.cs | 7 - src/Common.Mediator/RequestDispatcher.cs | 51 - src/Common.Mediator/Sender.cs | 14 - .../Common.Validation.csproj | 13 - .../CustomValidationException.cs | 20 - src/Common.Validation/IRuleBuilder.cs | 9 - src/Common.Validation/IValidator.cs | 6 - src/Common.Validation/RuleBuilder.cs | 106 - src/Common.Validation/ValidationFailure.cs | 7 - src/Common.Validation/ValidationResult.cs | 18 - src/Common.Validation/ValidationRule.cs | 8 - src/Common.Validation/Validator.cs | 52 - .../{IAuthorResponse.cs => IActorResponse.cs} | 4 +- .../Abstractions/IActorsPlugin.cs | 7 + .../Abstractions/IAuthorsPlugin.cs | 7 - .../Abstractions/ISemanticKernelContext.cs | 13 +- .../Abstractions/ISemanticPluginCompatible.cs | 1 - .../Abstractions/IUserInfoRequest.cs | 16 + src/Core.Application/Actor/ActorDto.cs | 34 + .../Actor/CreateActorCommand.cs | 57 + .../Actor/CreateActorCommandValidator.cs | 9 + .../Actor/DeleteActorByExternalIdCommand.cs | 30 + ...DeleteActorByExternalIdCommandValidator.cs | 8 + .../Actor/DeleteActorCommand.cs | 30 + .../Actor/DeleteActorCommandValidator.cs | 9 + .../GetActorChatSessionQuery.cs} | 12 +- .../GetActorChatSessionQueryValidator.cs | 10 + .../GetActorChatSessionsPaginatedQuery.cs} | 12 +- ...torChatSessionsPaginatedQueryValidator.cs} | 8 +- .../GetActorChatSessionsQuery.cs} | 12 +- .../GetActorChatSessionsQueryValidator.cs | 18 + src/Core.Application/Actor/GetActorQuery.cs | 29 + .../Actor/GetActorQueryValidator.cs | 9 + src/Core.Application/Actor/GetMyActorQuery.cs | 30 + .../Actor/GetMyActorQueryValidator.cs | 9 + .../Actor/SaveMyActorCommand.cs | 49 + .../Actor/SaveMyActorCommandValidator.cs | 10 + .../Actor/UpdateActorCommand.cs | 41 + .../Actor/UpdateActorCommandValidator.cs | 9 + .../Audio/CreateTextToAudioCommand.cs | 22 +- src/Core.Application/Audio/TextAudioDto.cs | 7 +- src/Core.Application/Author/AuthorDto.cs | 26 - .../Author/CreateAuthorCommand.cs | 56 - .../Author/CreateAuthorCommandValidator.cs | 11 - .../Author/DeleteAuthorCommand.cs | 30 - .../Author/DeleteAuthorCommandValidator.cs | 9 - .../GetAuthorChatSessionQueryValidator.cs | 10 - .../GetAuthorChatSessionsQueryValidator.cs | 20 - src/Core.Application/Author/GetAuthorQuery.cs | 29 - .../Author/GetAuthorQueryValidator.cs | 9 - .../Author/UpdateAuthorCommand.cs | 32 - .../Author/UpdateAuthorCommandValidator.cs | 10 - .../ChatCompletion/ChatSessionDto.cs | 4 +- .../CreateChatMessageCommand.cs | 28 +- .../CreateChatSessionCommand.cs | 46 +- .../DeleteTextImageCommand.cs | 2 +- .../DeleteTextImageCommandValidator.cs | 2 +- .../ChatCompletion/GetMyChatSessionQuery.cs | 40 + .../GetMyChatSessionQueryValidator.cs | 10 + .../GetMyChatSessionsPaginatedQuery.cs | 40 + ...etMyChatSessionsPaginatedQueryValidator.cs | 22 + .../ChatCompletion/GetMyChatSessionsQuery.cs | 34 + .../GetMyChatSessionsQueryValidator.cs | 18 + .../ChatCompletion/PatchChatSessionCommand.cs | 2 +- .../Common/Behaviors/CustomLoggingBehavior.cs | 3 +- .../Behaviors/CustomPerformanceBehavior.cs | 26 +- .../CustomUnhandledExceptionBehavior.cs | 10 +- .../Common/CustomLoggerExtensions.cs | 16 + .../Exceptions/CustomConflictException.cs | 23 + .../CustomForbiddenAccessException.cs | 21 +- src/Core.Application/Core.Application.csproj | 11 +- .../Image/CreateTextToImageCommand.cs | 9 +- src/Core.Application/Image/TextImageDto.cs | 7 +- .../TextGeneration/CreateTextPromptCommand.cs | 11 +- .../TextGeneration/TextPromptDto.cs | 11 +- .../TextGeneration/TextResponseDto.cs | 1 - src/Core.Domain/Actor/ActorEntity.cs | 46 + src/Core.Domain/Audio/TextAudioEntity.cs | 18 +- src/Core.Domain/Auth/IUserEntity.cs | 14 + src/Core.Domain/Auth/UserEntity.cs | 35 + src/Core.Domain/Author/AuthorEntity.cs | 27 - .../ChatCompletion/ChatMessageEntity.cs | 10 +- ...ChatMessageRole.cs => ChatMessageRoles.cs} | 0 .../ChatCompletion/ChatSessionEntity.cs | 19 +- src/Core.Domain/Core.Domain.csproj | 7 +- src/Core.Domain/GlobalUsings.cs | 1 - src/Core.Domain/Image/TextImageEntity.cs | 18 +- .../TextGeneration/TextPromptEntity.cs | 13 +- .../TextGeneration/TextResponseEntity.cs | 8 +- src/Get-CodeCoverage.ps1 | 20 +- .../AiModels/OpenAiModels.cs | 7 +- .../ConfigureServices.cs | 6 +- .../Infrastructure.SemanticKernel.csproj | 16 +- .../Plugins/ActorsPlugin.cs | 124 + .../Plugins/AuthorsPlugin.cs | 104 - .../Plugins/ChatMessagesPlugin.cs | 11 +- .../Plugins/ChatSessionsPlugin.cs | 15 +- .../ConfigureServices.cs | 2 - .../Infrastructure.SqlServer.csproj | 18 +- .../JsonSerializerOptionsProvider.cs | 12 + .../20240707165858_InitialCreate.Designer.cs | 444 - .../Migrations/20250816225410_v1.1.1.cs | 54 - ... 20260130074609_InitialCreate.Designer.cs} | 125 +- ...ate.cs => 20260130074609_InitialCreate.cs} | 78 +- .../SemanticKernelContextModelSnapshot.cs | 121 +- .../Configurations/ActorsConfig.cs | 49 + .../Configurations/AuthorsConfig.cs | 26 - .../Configurations/ChatSessionsConfig.cs | 4 - .../Configurations/TextAudioConfig.cs | 8 - .../Configurations/TextImagesConfig.cs | 4 - .../Configurations/TextPromptsConfig.cs | 8 - .../Persistence/SemanticKernelContext.cs | 54 +- .../SemanticKernelContextInitializer.cs | 46 - ...ebApiClient.g.cs => BackendApiClient.g.cs} | 1760 +-- src/Presentation.Blazor/Components/App.razor | 10 +- .../Components/Layout/Error.razor | 40 + .../Components/Layout/MainLayout.razor | 104 +- .../Components/Layout/MainLayout.razor.css | 39 - .../Components/Layout/NavMenu.razor | 75 - .../Components/Layout/NavMenu.razor.css | 129 - .../Components/Layout/NotFound.razor | 26 + .../Components/Routes.razor | 33 +- .../Skeleton/SkeletonList.razor.css | 23 - .../Components/_Imports.razor | 12 +- src/Presentation.Blazor/ConfigureServices.cs | 44 +- .../ConfigureServicesAuth.cs | 149 + ...{WebApiOptions.cs => BackendApiOptions.cs} | 6 +- .../Options/ResilientHttpClientOptions.cs | 7 + src/Presentation.Blazor/Pages/Chat/Chat.razor | 71 - .../Pages/Chat/Chat.razor.css | 6 - .../Chat/Components/ChatMessageList.razor | 27 + .../Pages/Chat/Components/ChatMessages.razor | 61 - .../Chat/Components/ChatMessages.razor.css | 46 - .../Chat/Components/ChatSessionEditForm.razor | 42 + .../Chat/Components/ChatSessionList.razor | 103 + .../Chat/Components/ChatSessionScroll.razor | 85 + .../Pages/Chat/Components/ChatSessions.razor | 104 - .../Chat/Components/ChatSessions.razor.css | 37 - .../Chat/Components/NewChatMessage.razor.css | 8 - ...Message.razor => NewChatMessageCard.razor} | 45 +- .../Chat/Components/NewChatMessageInput.razor | 91 + .../Chat/Components/NewChatSession.razor | 16 - .../Components/NewChatSessionButton.razor | 17 + .../Pages/Chat/Models/ChatSessionModel.cs | 6 +- .../Chat/Models/ChatSessionTreeViewItem.cs | 47 + .../Pages/Chat/Models/ChatSessionsModel.cs | 3 +- .../Pages/Chat/Services/ChatService.cs | 39 +- src/Presentation.Blazor/Pages/ChatPage.razor | 120 + src/Presentation.Blazor/Pages/Counter.razor | 21 - src/Presentation.Blazor/Pages/Error.razor | 39 - src/Presentation.Blazor/Pages/HomePage.razor | 60 + src/Presentation.Blazor/Pages/Weather.razor | 68 - src/Presentation.Blazor/Pages/_Imports.razor | 17 + .../Presentation.Blazor.csproj | 24 +- src/Presentation.Blazor/Program.cs | 28 +- .../Properties/launchSettings.json | 4 +- .../Services/ApiService.cs | 51 + .../Services/UserService.cs | 52 - .../Services/UserSyncService.cs | 73 + .../appsettings.Development.json | 15 +- .../appsettings.Local.json | 15 +- .../appsettings.Production.json | 15 +- src/Presentation.Blazor/wwwroot/app.css | 52 +- src/Presentation.Blazor/wwwroot/css/site.css | 0 src/Presentation.Blazor/wwwroot/favicon.png | Bin 1380 -> 5916 bytes .../wwwroot/img/goodtocode-logo.png | Bin 2415 -> 6661 bytes .../lib/bootstrap/dist/css/bootstrap-grid.css | 4085 ------ .../bootstrap/dist/css/bootstrap-grid.css.map | 1 - .../bootstrap/dist/css/bootstrap-grid.min.css | 6 - .../dist/css/bootstrap-grid.min.css.map | 1 - .../bootstrap/dist/css/bootstrap-grid.rtl.css | 4084 ------ .../dist/css/bootstrap-grid.rtl.css.map | 1 - .../dist/css/bootstrap-grid.rtl.min.css | 6 - .../dist/css/bootstrap-grid.rtl.min.css.map | 1 - .../bootstrap/dist/css/bootstrap-reboot.css | 597 - .../dist/css/bootstrap-reboot.css.map | 1 - .../dist/css/bootstrap-reboot.min.css | 6 - .../dist/css/bootstrap-reboot.min.css.map | 1 - .../dist/css/bootstrap-reboot.rtl.css | 594 - .../dist/css/bootstrap-reboot.rtl.css.map | 1 - .../dist/css/bootstrap-reboot.rtl.min.css | 6 - .../dist/css/bootstrap-reboot.rtl.min.css.map | 1 - .../dist/css/bootstrap-utilities.css | 5402 ------- .../dist/css/bootstrap-utilities.css.map | 1 - .../dist/css/bootstrap-utilities.min.css | 6 - .../dist/css/bootstrap-utilities.min.css.map | 1 - .../dist/css/bootstrap-utilities.rtl.css | 5393 ------- .../dist/css/bootstrap-utilities.rtl.css.map | 1 - .../dist/css/bootstrap-utilities.rtl.min.css | 6 - .../css/bootstrap-utilities.rtl.min.css.map | 1 - .../lib/bootstrap/dist/css/bootstrap.css | 12057 ---------------- .../lib/bootstrap/dist/css/bootstrap.css.map | 1 - .../lib/bootstrap/dist/css/bootstrap.min.css | 6 - .../bootstrap/dist/css/bootstrap.min.css.map | 1 - .../lib/bootstrap/dist/css/bootstrap.rtl.css | 12030 --------------- .../bootstrap/dist/css/bootstrap.rtl.css.map | 1 - .../bootstrap/dist/css/bootstrap.rtl.min.css | 6 - .../dist/css/bootstrap.rtl.min.css.map | 1 - .../lib/bootstrap/dist/js/bootstrap.bundle.js | 6314 -------- .../bootstrap/dist/js/bootstrap.bundle.js.map | 1 - .../bootstrap/dist/js/bootstrap.bundle.min.js | 7 - .../dist/js/bootstrap.bundle.min.js.map | 1 - .../lib/bootstrap/dist/js/bootstrap.esm.js | 4447 ------ .../bootstrap/dist/js/bootstrap.esm.js.map | 1 - .../bootstrap/dist/js/bootstrap.esm.min.js | 7 - .../dist/js/bootstrap.esm.min.js.map | 1 - .../lib/bootstrap/dist/js/bootstrap.js | 4494 ------ .../lib/bootstrap/dist/js/bootstrap.js.map | 1 - .../lib/bootstrap/dist/js/bootstrap.min.js | 7 - .../bootstrap/dist/js/bootstrap.min.js.map | 1 - .../Actor/MyActorController.cs | 73 + ...oController.cs => AdminAudioController.cs} | 23 +- .../Auth/ClaimsUserInfo.cs | 62 + .../Auth/ConfigureServices.cs | 44 + .../Auth/IClaimsUserInfo.cs | 50 + .../Auth/UserInfoBehavior.cs | 62 + .../Authors/AuthorsController.cs | 217 - ...oller.cs => AdminChatMessageController.cs} | 20 +- ...oller.cs => AdminChatSessionController.cs} | 25 +- .../ChatCompletion/MyChatSessionController.cs | 101 + .../Common/ApiControllerBase.cs | 16 +- .../Common/ApiExceptionFilterAttribute.cs | 24 +- src/Presentation.WebApi/ConfigureServices.cs | 46 +- .../Generate-NswagClientCode.json | 6 +- .../Generate-NswagClientCode.ps1 | 8 +- src/Presentation.WebApi/GlobalUsings.cs | 1 - ...eController.cs => AdminImageController.cs} | 27 +- .../Presentation.WebApi.csproj | 34 +- src/Presentation.WebApi/Program.cs | 28 +- .../Properties/launchSettings.json | 4 +- ...er.cs => AdminTextGenerationController.cs} | 23 +- .../appsettings.Development.json | 25 +- .../appsettings.Production.json | 33 +- .../appsettings.local.json | 23 +- src/Presentation.WebApi/dotnet-tools.json | 5 + src/SemanticKernelBlazor.sln | 38 +- src/SemanticKernelMicroservice.sln | 34 +- .../Actor/CreateActorCommand.feature | 21 + .../CreateActorCommand.feature.cs} | 52 +- .../CreateActorCommandStepDefinitions.cs} | 48 +- .../DeleteActorCommand.feature} | 19 +- .../DeleteActorCommand.feature.cs} | 36 +- .../DeleteActorCommandStepDefinitions.cs} | 26 +- .../Actor/GetActorByExternalIdQuery.feature | 19 + .../GetActorByExternalIdQuery.feature.cs | 179 + ...etActorByExternalIdQueryStepDefinitions.cs | 81 + .../GetActorChatSessionQuery.feature} | 9 +- .../GetActorChatSessionQuery.feature.cs} | 38 +- ...etActorChatSessionQueryStepDefinitions.cs} | 22 +- ...etActorChatSessionsPaginatedQuery.feature} | 10 +- ...ctorChatSessionsPaginatedQuery.feature.cs} | 54 +- ...tSessionsPaginatedQueryStepDefinitions.cs} | 23 +- .../GetActorChatSessionsQuery.feature} | 8 +- .../GetActorChatSessionsQuery.feature.cs} | 44 +- ...tActorChatSessionsQueryStepDefinitions.cs} | 25 +- .../GetActorQuery.feature} | 18 +- .../GetActorQuery.feature.cs} | 38 +- .../GetActorQueryStepDefinitions.cs} | 26 +- .../Actor/SaveActorCommand.feature | 21 + .../Actor/SaveActorCommand.feature.cs | 192 + .../Actor/SaveActorCommandStepDefinitions.cs | 104 + .../UpdateActorCommand.feature} | 17 +- .../UpdateActorCommand.feature.cs} | 37 +- .../UpdateActorCommandStepDefinitions.cs} | 24 +- .../Audio/CreateTextToAudioCommand.feature | 2 +- .../Audio/CreateTextToAudioCommand.feature.cs | 24 +- ...CreateTextToAudioCommandStepDefinitions.cs | 2 +- .../Audio/DeleteTextAudioCommand.feature.cs | 18 +- .../Audio/GetTextAudioQuery.feature | 2 +- .../Audio/GetTextAudioQuery.feature.cs | 20 +- .../GetTextAudiosPaginatedQuery.feature.cs | 24 +- ...TextAudiosPaginatedQueryStepDefinitions.cs | 6 +- .../Audio/GetTextAudiosQuery.feature.cs | 20 +- .../GetTextAudiosQueryStepDefinitions.cs | 4 +- .../Author/CreateAuthorCommand.feature | 20 - .../CreateChatMessageCommand.feature | 2 +- .../CreateChatMessageCommand.feature.cs | 40 +- ...CreateChatMessageCommandStepDefinitions.cs | 10 +- .../CreateChatSessionCommand.feature.cs | 20 +- ...CreateChatSessionCommandStepDefinitions.cs | 9 +- .../DeleteChatSessionCommand.feature.cs | 19 +- .../GetChatMessageQuery.feature.cs | 17 +- .../GetChatMessagesPaginatedQuery.feature.cs | 24 +- ...atMessagesPaginatedQueryStepDefinitions.cs | 6 +- .../GetChatMessagesQuery.feature.cs | 20 +- .../GetChatMessagesQueryStepDefinitions.cs | 7 +- .../GetChatSessionQuery.feature.cs | 18 +- .../GetChatSessionQueryStepDefinitions.cs | 2 +- .../GetChatSessionsPaginatedQuery.feature.cs | 24 +- ...atSessionsPaginatedQueryStepDefinitions.cs | 6 +- .../GetChatSessionsQuery.feature.cs | 20 +- .../GetChatSessionsQueryStepDefinitions.cs | 6 +- .../PatchChatSessionCommand.feature.cs | 20 +- .../UpdateChatSessionCommand.feature.cs | 19 +- .../Image/CreateTextToImageCommand.feature | 10 +- .../Image/CreateTextToImageCommand.feature.cs | 24 +- .../Image/DeleteTextImageCommand.feature.cs | 18 +- .../DeleteTextImageCommandStepDefinitions.cs | 2 +- .../Image/GetTextImageQuery.feature | 2 +- .../Image/GetTextImageQuery.feature.cs | 20 +- .../GetTextImagesPaginatedQuery.feature.cs | 24 +- ...TextImagesPaginatedQueryStepDefinitions.cs | 4 +- .../Image/GetTextImagesQuery.feature.cs | 20 +- .../GetTextImagesQueryStepDefinitions.cs | 4 +- src/Tests.Specs.Integration/TestBase.cs | 15 +- src/Tests.Specs.Integration/TestUserInfo.cs | 18 + .../Tests.Specs.Integration.csproj | 15 +- .../CreateTextPromptCommand.feature | 2 +- .../CreateTextPromptCommand.feature.cs | 24 +- .../CreateTextPromptCommandStepDefinitions.cs | 7 +- .../DeleteTextPromptCommand.feature.cs | 18 +- .../DeleteTextPromptCommandStepDefinitions.cs | 5 +- .../TextGeneration/GetTextPromptQuery.feature | 2 +- .../GetTextPromptQuery.feature.cs | 21 +- .../GetTextPromptQueryStepDefinitions.cs | 5 +- .../GetTextPromptsPaginatedQuery.feature.cs | 24 +- ...extPromptsPaginatedQueryStepDefinitions.cs | 9 +- .../GetTextPromptsQuery.feature.cs | 20 +- .../GetTextPromptsQueryStepDefinitions.cs | 9 +- 534 files changed, 9127 insertions(+), 72714 deletions(-) create mode 100644 .azure/modules/afd-azurefrontdoor.bicep delete mode 100644 .azure/modules/nested_newWorkspaceTemplate.bicep create mode 100644 .azure/modules/nsg-networksecuritygroup.bicep create mode 100644 .azure/modules/nsgrule-networksecuritygrouprule.bicep delete mode 100644 .azure/modules/plan-appplan.bicep create mode 100644 .azure/modules/sent-loganalyticsworkspace.bicep create mode 100644 .azure/modules/snet-virtualnetworksubnet.bicep delete mode 100644 .azure/modules/st-storageaccount1container.bicep delete mode 100644 .azure/modules/st-storageaccount2container.bicep delete mode 100644 .azure/modules/st-storageaccountcontainers.bicep create mode 100644 .azure/modules/vnet-virtualnetwork.bicep create mode 100644 .azure/modules/vnetpeer-virtualnetworkpeering.bicep delete mode 100644 .azure/templates/landingzone-api-sql.bicep delete mode 100644 .azure/templates/landingzone-shared.bicep create mode 100644 .azure/templates/landingzone-web-api-sql.bicep create mode 100644 .azure/templates/platform-hub-mgmt.bicep create mode 100644 .azure/templates/platform-hub-network-publicroute.bicep create mode 100644 .azure/templates/platform-spoke-mgmt.bicep create mode 100644 .azure/templates/platform-spoke-network-publicroute.bicep delete mode 100644 .azure/variables/landingzone-api-sql-development.bicepparam delete mode 100644 .azure/variables/landingzone-api-sql-production.bicepparam delete mode 100644 .azure/variables/landingzone-shared-development.bicepparam delete mode 100644 .azure/variables/landingzone-shared-production.bicepparam create mode 100644 .azure/variables/landingzone-web-api-sql-dev.bicepparam create mode 100644 .azure/variables/platform-hub-mgmt-plat.bicepparam create mode 100644 .azure/variables/platform-hub-network-publicroute-plat.bicepparam create mode 100644 .azure/variables/platform-spoke-mgmt-dev.bicepparam create mode 100644 .azure/variables/platform-spoke-network-publicroute-dev.bicepparam delete mode 100644 .azuredevops/pipelines/COMPANY-PRODUCT-api-cd-appservice.yml delete mode 100644 .azuredevops/pipelines/COMPANY-PRODUCT-api-ci-cd-AppService.yml delete mode 100644 .azuredevops/pipelines/COMPANY-PRODUCT-api-ci-cd-IIS.yml delete mode 100644 .azuredevops/pipelines/COMPANY-PRODUCT-api-ci.yml delete mode 100644 .azuredevops/pipelines/COMPANY-PRODUCT-ef.yml delete mode 100644 .azuredevops/pipelines/COMPANY-PRODUCT-func.yml delete mode 100644 .azuredevops/pipelines/COMPANY-PRODUCT-infrastructure-bicep.yml delete mode 100644 .azuredevops/pipelines/COMPANY-PRODUCT-infrastructure-template.yml delete mode 100644 .azuredevops/pipelines/COMPANY-PRODUCT-landingzone.yml delete mode 100644 .azuredevops/pipelines/COMPANY-PRODUCT-nuget.yml delete mode 100644 .azuredevops/pipelines/COMPANY-PRODUCT-tests.yml delete mode 100644 .azuredevops/pipelines/COMPANY-PRODUCT-webjob.yml delete mode 100644 .azuredevops/pipelines/COMPANY-PRODUCT-worker.yml delete mode 100644 .azuredevops/scripts/Set-Version.ps1 delete mode 100644 .azuredevops/scripts/System.psm1 delete mode 100644 .azuredevops/steps/api-deploy-steps.yml delete mode 100644 .azuredevops/steps/api-infrastructure-steps.yml delete mode 100644 .azuredevops/steps/api-settings-steps.yml delete mode 100644 .azuredevops/steps/appcs-infrastructure-steps.yml delete mode 100644 .azuredevops/steps/appcs-settings-steps.yml delete mode 100644 .azuredevops/steps/appsetting-deploy-steps.yml delete mode 100644 .azuredevops/steps/appsettings-deploy-steps.yml delete mode 100644 .azuredevops/steps/blob-deploy-steps.yml delete mode 100644 .azuredevops/steps/cog-deploy-steps.yml delete mode 100644 .azuredevops/steps/dotnet-archive-steps.yml delete mode 100644 .azuredevops/steps/dotnet-build-steps.yml delete mode 100644 .azuredevops/steps/dotnet-output-steps.yml delete mode 100644 .azuredevops/steps/dotnet-pack-steps.yml delete mode 100644 .azuredevops/steps/dotnet-publish-steps.yml delete mode 100644 .azuredevops/steps/dotnet-test-steps.yml delete mode 100644 .azuredevops/steps/ef-deploy-steps.yml delete mode 100644 .azuredevops/steps/ef-publish-steps.yml delete mode 100644 .azuredevops/steps/func-build-steps.yml delete mode 100644 .azuredevops/steps/func-deploy-steps.yml delete mode 100644 .azuredevops/steps/func-infrastructure-plan-steps.yml delete mode 100644 .azuredevops/steps/func-infrastructure-plan-vnet-steps.yml delete mode 100644 .azuredevops/steps/func-infrastructure-steps.yml delete mode 100644 .azuredevops/steps/func-settings-steps.yml delete mode 100644 .azuredevops/steps/hcm-infrastructure-steps.yml delete mode 100644 .azuredevops/steps/integration-test-steps.yml delete mode 100644 .azuredevops/steps/landingzone-infrastructure-steps.yml delete mode 100644 .azuredevops/steps/logic-infrastructure-steps.yml delete mode 100644 .azuredevops/steps/msbuild-build-steps.yml delete mode 100644 .azuredevops/steps/npm-build-steps.yml delete mode 100644 .azuredevops/steps/npm-test-steps.yml delete mode 100644 .azuredevops/steps/nuget-deploy-external-steps.yml delete mode 100644 .azuredevops/steps/nuget-deploy-internal-steps.yml delete mode 100644 .azuredevops/steps/nuget-pack-steps.yml delete mode 100644 .azuredevops/steps/sb-infrastructure-steps.yml delete mode 100644 .azuredevops/steps/sqldb-infrastructure-steps.yml delete mode 100644 .azuredevops/steps/stapp-infrastructure-steps.yml delete mode 100644 .azuredevops/steps/web-deploy-steps.yml delete mode 100644 .azuredevops/steps/web-infrastructure-steps.yml delete mode 100644 .azuredevops/steps/web-settings-steps.yml delete mode 100644 .azuredevops/steps/webjob-build-steps.yml delete mode 100644 .azuredevops/steps/webjob-deploy-steps.yml delete mode 100644 .azuredevops/steps/webjob-infrastructure-steps.yml delete mode 100644 .azuredevops/steps/webjob-settings-steps.yml delete mode 100644 .azuredevops/steps/webstatic-pack-steps.yml delete mode 100644 .azuredevops/variables/common-ci.yml delete mode 100644 .azuredevops/variables/development-cd.yml delete mode 100644 .azuredevops/variables/development-infrastructure.yml delete mode 100644 .azuredevops/variables/production-cd.yml delete mode 100644 .azuredevops/variables/production-infrastructure.yml create mode 100644 .github/copilot-instructions.md delete mode 100644 .github/scripts/Get-AzureAd.ps1 delete mode 100644 .github/scripts/Install-AzureCli.ps1 delete mode 100644 .github/scripts/New-AzResourceGroupDeployment.ps1 delete mode 100644 .github/scripts/Remove-LogicApp.ps1 delete mode 100644 .github/scripts/Remove-SQLServer.ps1 delete mode 100644 .github/scripts/Remove-StorageAccount.ps1 delete mode 100644 .github/scripts/Set-ApiConnectorAuth.ps1 delete mode 100644 .github/scripts/Set-Version.ps1 rename .github/scripts/{ => cd}/Add-ManagedIdentityRole.ps1 (100%) rename .github/scripts/{ => cd}/Get-ManagedIdentityRole.ps1 (100%) rename .github/scripts/{ => cd}/Remove-ManagedIdentityRole.ps1 (100%) create mode 100644 .github/scripts/cd/Remove-NugetPackage.ps1 create mode 100644 .github/scripts/ci/Get-Version.ps1 create mode 100644 .github/scripts/ci/Set-Version.ps1 rename .github/scripts/{ => iac}/Copy-Custompolicy.ps1 (100%) rename {.azuredevops/scripts => .github/scripts/iac}/Install-AzureCli.ps1 (100%) rename .github/scripts/{ => iac}/New-AzServicePrinciple.ps1 (98%) rename .github/scripts/{ => iac}/New-SelfSignedCert.ps1 (72%) rename .github/scripts/{ => iac}/Remove-LandingZone.ps1 (98%) rename .github/scripts/{ => iac}/Set-AzKeyVaultPolicy.ps1 (98%) create mode 100644 .github/scripts/repo/New-GithubRepoBootstrap.ps1 create mode 100644 .github/scripts/repo/New-GithubSecret.ps1 rename .github/scripts/{ => repo}/az-create-for-rbac.cmd (100%) create mode 100644 .github/workflows/gtc-platform-hub-iac.yml create mode 100644 .github/workflows/gtc-platform-spoke-iac.yml delete mode 100644 .github/workflows/gtc-rg-semkernel-api-ci-cd.yml rename .github/workflows/{gtc-rg-semkernel-iac.yml => gtc-semker-iac.yml} (55%) create mode 100644 .github/workflows/gtc-semker-web-api-sql.yml create mode 100644 data/.vscode/tasks.json create mode 100644 data/Admin/Drop Tables.sql rename src/Presentation.Blazor/Pages/Chat/Components/NewChatSession.razor.css => data/Chat/ChatMessages.sql (100%) create mode 100644 data/Chat/ChatSessions.sql create mode 100644 data/Chat/Multimedia.sql create mode 100644 data/Data.sqlproj create mode 100644 data/Identity.sql create mode 100644 src/App.razor rename src/{Presentation.Blazor/Components => Aspects.Components}/Analytics/ClarityAnalytics.razor (84%) create mode 100644 src/Aspects.Components/Aspects.Components.csproj create mode 100644 src/Aspects.Components/Auth/Components/UserAuthMenu.razor create mode 100644 src/Aspects.Components/Auth/Components/UserProfile.razor create mode 100644 src/Aspects.Components/Auth/IUserClaimsInfo.cs create mode 100644 src/Aspects.Components/Auth/Middleware/DownstreamApiAccessTokenProvider.cs create mode 100644 src/Aspects.Components/Auth/Middleware/MsGraphAccessTokenProvider.cs create mode 100644 src/Aspects.Components/Auth/Routing/LoginLogoutEndpointRouteBuilderExtensions.cs create mode 100644 src/Aspects.Components/Auth/Routing/RedirectToAccessDenied.razor create mode 100644 src/Aspects.Components/Auth/Routing/RedirectToResetPassword.razor create mode 100644 src/Aspects.Components/Auth/Routing/RedirectToSignIn.razor create mode 100644 src/Aspects.Components/Auth/Routing/RedirectToSignOut.razor create mode 100644 src/Aspects.Components/Auth/Routing/RouteConstants.cs create mode 100644 src/Aspects.Components/Auth/Services/IUserSyncService.cs create mode 100644 src/Aspects.Components/Auth/UserClaimsInfo.cs create mode 100644 src/Aspects.Components/FormChangeType.cs create mode 100644 src/Aspects.Components/Icons/ArrowIcon.razor rename src/{Presentation.Blazor/Components => Aspects.Components}/Skeleton/SkeletonList.razor (54%) create mode 100644 src/Aspects.Components/Skeleton/SkeletonTable.razor create mode 100644 src/Aspects.Components/Typography/H1Label.razor create mode 100644 src/Aspects.Components/Typography/H2Label.razor create mode 100644 src/Aspects.Components/Typography/H3Label.razor create mode 100644 src/Aspects.Components/Typography/PLabel.razor create mode 100644 src/Aspects.Components/Wizard/WizardExample.razor create mode 100644 src/Aspects.Components/Wizard/WizardLayout.razor create mode 100644 src/Aspects.Components/Wizard/WizardStep.razor create mode 100644 src/Aspects.Components/_Imports.razor delete mode 100644 src/Common.Assertion/AssertionFailedException.cs delete mode 100644 src/Common.Assertion/AssertionRules.cs delete mode 100644 src/Common.Assertion/AssertionScope.cs delete mode 100644 src/Common.Assertion/Common.Assertion.csproj delete mode 100644 src/Common.Domain.Types/Common.Domain.Types.csproj delete mode 100644 src/Common.Domain.Types/DomainEntity/DomainEntity.cs delete mode 100644 src/Common.Domain.Types/DomainEntity/IDomainEntity.cs delete mode 100644 src/Common.Domain.Types/DomainEvent/DomainEventExample.cs delete mode 100644 src/Common.Domain.Types/DomainEvent/IDomainEvent.cs delete mode 100644 src/Common.HttpClient.ClientCredentialFlow/Common.HttpClient.ClientCredentialFlow.csproj delete mode 100644 src/Common.HttpClient.ClientCredentialFlow/ConfigureServices.cs delete mode 100644 src/Common.HttpClient.ClientCredentialFlow/Middleware/BearerToken.cs delete mode 100644 src/Common.HttpClient.ClientCredentialFlow/Middleware/TokenHandler.cs delete mode 100644 src/Common.HttpClient.ClientCredentialFlow/Options/ClientCredential.cs delete mode 100644 src/Common.Mediator/Common.Mediator.csproj delete mode 100644 src/Common.Mediator/IPipelineBehavior.cs delete mode 100644 src/Common.Mediator/IRequest.cs delete mode 100644 src/Common.Mediator/IRequestDispatcher.cs delete mode 100644 src/Common.Mediator/IRequestHandler.cs delete mode 100644 src/Common.Mediator/IRequestPreProcessor.cs delete mode 100644 src/Common.Mediator/ISender.cs delete mode 100644 src/Common.Mediator/RequestDispatcher.cs delete mode 100644 src/Common.Mediator/Sender.cs delete mode 100644 src/Common.Validation/Common.Validation.csproj delete mode 100644 src/Common.Validation/CustomValidationException.cs delete mode 100644 src/Common.Validation/IRuleBuilder.cs delete mode 100644 src/Common.Validation/IValidator.cs delete mode 100644 src/Common.Validation/RuleBuilder.cs delete mode 100644 src/Common.Validation/ValidationFailure.cs delete mode 100644 src/Common.Validation/ValidationResult.cs delete mode 100644 src/Common.Validation/ValidationRule.cs delete mode 100644 src/Common.Validation/Validator.cs rename src/Core.Application/Abstractions/{IAuthorResponse.cs => IActorResponse.cs} (72%) create mode 100644 src/Core.Application/Abstractions/IActorsPlugin.cs delete mode 100644 src/Core.Application/Abstractions/IAuthorsPlugin.cs create mode 100644 src/Core.Application/Abstractions/IUserInfoRequest.cs create mode 100644 src/Core.Application/Actor/ActorDto.cs create mode 100644 src/Core.Application/Actor/CreateActorCommand.cs create mode 100644 src/Core.Application/Actor/CreateActorCommandValidator.cs create mode 100644 src/Core.Application/Actor/DeleteActorByExternalIdCommand.cs create mode 100644 src/Core.Application/Actor/DeleteActorByExternalIdCommandValidator.cs create mode 100644 src/Core.Application/Actor/DeleteActorCommand.cs create mode 100644 src/Core.Application/Actor/DeleteActorCommandValidator.cs rename src/Core.Application/{Author/GetAuthorChatSessionQuery.cs => Actor/GetActorChatSessionQuery.cs} (66%) create mode 100644 src/Core.Application/Actor/GetActorChatSessionQueryValidator.cs rename src/Core.Application/{Author/GetAuthorChatSessionsPaginatedQuery.cs => Actor/GetActorChatSessionsPaginatedQuery.cs} (70%) rename src/Core.Application/{Author/GetAuthorChatSessionsPaginatedQueryValidator.cs => Actor/GetActorChatSessionsPaginatedQueryValidator.cs} (59%) rename src/Core.Application/{Author/GetAuthorChatSessionsQuery.cs => Actor/GetActorChatSessionsQuery.cs} (62%) create mode 100644 src/Core.Application/Actor/GetActorChatSessionsQueryValidator.cs create mode 100644 src/Core.Application/Actor/GetActorQuery.cs create mode 100644 src/Core.Application/Actor/GetActorQueryValidator.cs create mode 100644 src/Core.Application/Actor/GetMyActorQuery.cs create mode 100644 src/Core.Application/Actor/GetMyActorQueryValidator.cs create mode 100644 src/Core.Application/Actor/SaveMyActorCommand.cs create mode 100644 src/Core.Application/Actor/SaveMyActorCommandValidator.cs create mode 100644 src/Core.Application/Actor/UpdateActorCommand.cs create mode 100644 src/Core.Application/Actor/UpdateActorCommandValidator.cs delete mode 100644 src/Core.Application/Author/AuthorDto.cs delete mode 100644 src/Core.Application/Author/CreateAuthorCommand.cs delete mode 100644 src/Core.Application/Author/CreateAuthorCommandValidator.cs delete mode 100644 src/Core.Application/Author/DeleteAuthorCommand.cs delete mode 100644 src/Core.Application/Author/DeleteAuthorCommandValidator.cs delete mode 100644 src/Core.Application/Author/GetAuthorChatSessionQueryValidator.cs delete mode 100644 src/Core.Application/Author/GetAuthorChatSessionsQueryValidator.cs delete mode 100644 src/Core.Application/Author/GetAuthorQuery.cs delete mode 100644 src/Core.Application/Author/GetAuthorQueryValidator.cs delete mode 100644 src/Core.Application/Author/UpdateAuthorCommand.cs delete mode 100644 src/Core.Application/Author/UpdateAuthorCommandValidator.cs rename src/Core.Application/{Image => ChatCompletion}/DeleteTextImageCommand.cs (93%) rename src/Core.Application/{Image => ChatCompletion}/DeleteTextImageCommandValidator.cs (71%) create mode 100644 src/Core.Application/ChatCompletion/GetMyChatSessionQuery.cs create mode 100644 src/Core.Application/ChatCompletion/GetMyChatSessionQueryValidator.cs create mode 100644 src/Core.Application/ChatCompletion/GetMyChatSessionsPaginatedQuery.cs create mode 100644 src/Core.Application/ChatCompletion/GetMyChatSessionsPaginatedQueryValidator.cs create mode 100644 src/Core.Application/ChatCompletion/GetMyChatSessionsQuery.cs create mode 100644 src/Core.Application/ChatCompletion/GetMyChatSessionsQueryValidator.cs create mode 100644 src/Core.Application/Common/CustomLoggerExtensions.cs create mode 100644 src/Core.Application/Common/Exceptions/CustomConflictException.cs create mode 100644 src/Core.Domain/Actor/ActorEntity.cs create mode 100644 src/Core.Domain/Auth/IUserEntity.cs create mode 100644 src/Core.Domain/Auth/UserEntity.cs delete mode 100644 src/Core.Domain/Author/AuthorEntity.cs rename src/Core.Domain/ChatCompletion/{ChatMessageRole.cs => ChatMessageRoles.cs} (100%) create mode 100644 src/Infrastructure.SemanticKernel/Plugins/ActorsPlugin.cs delete mode 100644 src/Infrastructure.SemanticKernel/Plugins/AuthorsPlugin.cs create mode 100644 src/Infrastructure.SqlServer/JsonSerializerOptionsProvider.cs delete mode 100644 src/Infrastructure.SqlServer/Migrations/20240707165858_InitialCreate.Designer.cs delete mode 100644 src/Infrastructure.SqlServer/Migrations/20250816225410_v1.1.1.cs rename src/Infrastructure.SqlServer/Migrations/{20250816225410_v1.1.1.Designer.cs => 20260130074609_InitialCreate.Designer.cs} (88%) rename src/Infrastructure.SqlServer/Migrations/{20240707165858_InitialCreate.cs => 20260130074609_InitialCreate.cs} (84%) create mode 100644 src/Infrastructure.SqlServer/Persistence/Configurations/ActorsConfig.cs delete mode 100644 src/Infrastructure.SqlServer/Persistence/Configurations/AuthorsConfig.cs delete mode 100644 src/Infrastructure.SqlServer/Persistence/SemanticKernelContextInitializer.cs rename src/Presentation.Blazor/Clients/{WebApiClient.g.cs => BackendApiClient.g.cs} (86%) create mode 100644 src/Presentation.Blazor/Components/Layout/Error.razor delete mode 100644 src/Presentation.Blazor/Components/Layout/MainLayout.razor.css delete mode 100644 src/Presentation.Blazor/Components/Layout/NavMenu.razor delete mode 100644 src/Presentation.Blazor/Components/Layout/NavMenu.razor.css create mode 100644 src/Presentation.Blazor/Components/Layout/NotFound.razor delete mode 100644 src/Presentation.Blazor/Components/Skeleton/SkeletonList.razor.css create mode 100644 src/Presentation.Blazor/ConfigureServicesAuth.cs rename src/Presentation.Blazor/Options/{WebApiOptions.cs => BackendApiOptions.cs} (53%) create mode 100644 src/Presentation.Blazor/Options/ResilientHttpClientOptions.cs delete mode 100644 src/Presentation.Blazor/Pages/Chat/Chat.razor delete mode 100644 src/Presentation.Blazor/Pages/Chat/Chat.razor.css create mode 100644 src/Presentation.Blazor/Pages/Chat/Components/ChatMessageList.razor delete mode 100644 src/Presentation.Blazor/Pages/Chat/Components/ChatMessages.razor delete mode 100644 src/Presentation.Blazor/Pages/Chat/Components/ChatMessages.razor.css create mode 100644 src/Presentation.Blazor/Pages/Chat/Components/ChatSessionEditForm.razor create mode 100644 src/Presentation.Blazor/Pages/Chat/Components/ChatSessionList.razor create mode 100644 src/Presentation.Blazor/Pages/Chat/Components/ChatSessionScroll.razor delete mode 100644 src/Presentation.Blazor/Pages/Chat/Components/ChatSessions.razor delete mode 100644 src/Presentation.Blazor/Pages/Chat/Components/ChatSessions.razor.css delete mode 100644 src/Presentation.Blazor/Pages/Chat/Components/NewChatMessage.razor.css rename src/Presentation.Blazor/Pages/Chat/Components/{NewChatMessage.razor => NewChatMessageCard.razor} (68%) create mode 100644 src/Presentation.Blazor/Pages/Chat/Components/NewChatMessageInput.razor delete mode 100644 src/Presentation.Blazor/Pages/Chat/Components/NewChatSession.razor create mode 100644 src/Presentation.Blazor/Pages/Chat/Components/NewChatSessionButton.razor create mode 100644 src/Presentation.Blazor/Pages/Chat/Models/ChatSessionTreeViewItem.cs create mode 100644 src/Presentation.Blazor/Pages/ChatPage.razor delete mode 100644 src/Presentation.Blazor/Pages/Counter.razor delete mode 100644 src/Presentation.Blazor/Pages/Error.razor create mode 100644 src/Presentation.Blazor/Pages/HomePage.razor delete mode 100644 src/Presentation.Blazor/Pages/Weather.razor create mode 100644 src/Presentation.Blazor/Pages/_Imports.razor create mode 100644 src/Presentation.Blazor/Services/ApiService.cs delete mode 100644 src/Presentation.Blazor/Services/UserService.cs create mode 100644 src/Presentation.Blazor/Services/UserSyncService.cs create mode 100644 src/Presentation.Blazor/wwwroot/css/site.css delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.css.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.min.css.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.css.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-utilities.rtl.min.css.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap.css delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.css.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap.rtl.min.css.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.js.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/js/bootstrap.esm.min.js.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/js/bootstrap.js delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js delete mode 100644 src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map create mode 100644 src/Presentation.WebApi/Actor/MyActorController.cs rename src/Presentation.WebApi/Audio/{AudioController.cs => AdminAudioController.cs} (85%) create mode 100644 src/Presentation.WebApi/Auth/ClaimsUserInfo.cs create mode 100644 src/Presentation.WebApi/Auth/ConfigureServices.cs create mode 100644 src/Presentation.WebApi/Auth/IClaimsUserInfo.cs create mode 100644 src/Presentation.WebApi/Auth/UserInfoBehavior.cs delete mode 100644 src/Presentation.WebApi/Authors/AuthorsController.cs rename src/Presentation.WebApi/ChatCompletion/{ChatMessageController.cs => AdminChatMessageController.cs} (86%) rename src/Presentation.WebApi/ChatCompletion/{ChatSessionController.cs => AdminChatSessionController.cs} (88%) create mode 100644 src/Presentation.WebApi/ChatCompletion/MyChatSessionController.cs rename src/Presentation.WebApi/Image/{ImageController.cs => AdminImageController.cs} (83%) rename src/Presentation.WebApi/TextGeneration/{TextGenerationController.cs => AdminTextGenerationController.cs} (88%) create mode 100644 src/Presentation.WebApi/dotnet-tools.json create mode 100644 src/Tests.Specs.Integration/Actor/CreateActorCommand.feature rename src/Tests.Specs.Integration/{Author/CreateAuthorCommand.feature.cs => Actor/CreateActorCommand.feature.cs} (72%) rename src/Tests.Specs.Integration/{Author/CreateAuthorCommandStepDefinitions.cs => Actor/CreateActorCommandStepDefinitions.cs} (57%) rename src/Tests.Specs.Integration/{Author/DeleteAuthorCommand.feature => Actor/DeleteActorCommand.feature} (56%) rename src/Tests.Specs.Integration/{Author/DeleteAuthorCommand.feature.cs => Actor/DeleteActorCommand.feature.cs} (78%) rename src/Tests.Specs.Integration/{Author/DeleteAuthorCommandStepDefinitions.cs => Actor/DeleteActorCommandStepDefinitions.cs} (69%) create mode 100644 src/Tests.Specs.Integration/Actor/GetActorByExternalIdQuery.feature create mode 100644 src/Tests.Specs.Integration/Actor/GetActorByExternalIdQuery.feature.cs create mode 100644 src/Tests.Specs.Integration/Actor/GetActorByExternalIdQueryStepDefinitions.cs rename src/Tests.Specs.Integration/{Author/GetAuthorChatSessionQuery.feature => Actor/GetActorChatSessionQuery.feature} (76%) rename src/Tests.Specs.Integration/{Author/GetAuthorChatSessionQuery.feature.cs => Actor/GetActorChatSessionQuery.feature.cs} (78%) rename src/Tests.Specs.Integration/{Author/GetAuthorChatSessionQueryStepDefinitions.cs => Actor/GetActorChatSessionQueryStepDefinitions.cs} (78%) rename src/Tests.Specs.Integration/{Author/GetAuthorChatSessionsPaginatedQuery.feature => Actor/GetActorChatSessionsPaginatedQuery.feature} (91%) rename src/Tests.Specs.Integration/{Author/GetAuthorChatSessionsPaginatedQuery.feature.cs => Actor/GetActorChatSessionsPaginatedQuery.feature.cs} (79%) rename src/Tests.Specs.Integration/{Author/GetAuthorChatSessionsPaginatedQueryStepDefinitions.cs => Actor/GetActorChatSessionsPaginatedQueryStepDefinitions.cs} (90%) rename src/Tests.Specs.Integration/{Author/GetAuthorChatSessionsQuery.feature => Actor/GetActorChatSessionsQuery.feature} (93%) rename src/Tests.Specs.Integration/{Author/GetAuthorChatSessionsQuery.feature.cs => Actor/GetActorChatSessionsQuery.feature.cs} (80%) rename src/Tests.Specs.Integration/{Author/GetAuthorChatSessionsQueryStepDefinitions.cs => Actor/GetActorChatSessionsQueryStepDefinitions.cs} (82%) rename src/Tests.Specs.Integration/{Author/GetAuthorQuery.feature => Actor/GetActorQuery.feature} (63%) rename src/Tests.Specs.Integration/{Author/GetAuthorQuery.feature.cs => Actor/GetActorQuery.feature.cs} (78%) rename src/Tests.Specs.Integration/{Author/GetAuthorQueryStepDefinitions.cs => Actor/GetActorQueryStepDefinitions.cs} (74%) create mode 100644 src/Tests.Specs.Integration/Actor/SaveActorCommand.feature create mode 100644 src/Tests.Specs.Integration/Actor/SaveActorCommand.feature.cs create mode 100644 src/Tests.Specs.Integration/Actor/SaveActorCommandStepDefinitions.cs rename src/Tests.Specs.Integration/{Author/UpdateAuthorCommand.feature => Actor/UpdateActorCommand.feature} (60%) rename src/Tests.Specs.Integration/{Author/UpdateAuthorCommand.feature.cs => Actor/UpdateActorCommand.feature.cs} (77%) rename src/Tests.Specs.Integration/{Author/UpdateAuthorCommandStepDefinitions.cs => Actor/UpdateActorCommandStepDefinitions.cs} (73%) delete mode 100644 src/Tests.Specs.Integration/Author/CreateAuthorCommand.feature create mode 100644 src/Tests.Specs.Integration/TestUserInfo.cs diff --git a/.azure/modules/afd-azurefrontdoor.bicep b/.azure/modules/afd-azurefrontdoor.bicep new file mode 100644 index 0000000..88c7c03 --- /dev/null +++ b/.azure/modules/afd-azurefrontdoor.bicep @@ -0,0 +1,31 @@ + + +@description('Name of the Azure Front Door instance. Must be globally unique and 3-63 characters, using only lowercase letters, numbers, and hyphens, starting and ending with a letter or number.') +@minLength(3) +@maxLength(63) +param name string + +@description('Location for the Azure Front Door resource. Must be set to global.') +@allowed(['global']) +param location string = 'global' + +@description('Tags to apply to the Azure Front Door resource.') +param tags object = {} + +@description('SKU for Azure Front Door. Allowed values: Standard_AzureFrontDoor, Premium_AzureFrontDoor. Default is Standard_AzureFrontDoor.') +@allowed([ + 'Standard_AzureFrontDoor' + 'Premium_AzureFrontDoor' +]) +param sku string = 'Standard_AzureFrontDoor' + +resource afd 'Microsoft.Cdn/profiles@2023-05-01' = { + name: name + location: location + tags: tags + sku: { + name: sku + } +} + +output id string = afd.id diff --git a/.azure/modules/api-appservice.bicep b/.azure/modules/api-appservice.bicep index 92e7dcc..81c296d 100644 --- a/.azure/modules/api-appservice.bicep +++ b/.azure/modules/api-appservice.bicep @@ -1,26 +1,72 @@ -param name string -param location string -param tags object = {} + +@description('The name of the App Service Web App. Must be 1-60 characters, using only alphanumeric characters and hyphens.') @minLength(1) -@allowed(['Development', 'QA', 'Staging', 'Production']) +@maxLength(60) +param name string + +@description('The Azure region where the Web App will be deployed.') +param location string + +@description('Tags to apply to the Web App resource.') +param tags object = {} + +@description('The environment for the Web App. Allowed values: Development, QA, Staging, Production. Default is Development.') +@allowed([ + 'Development' + 'QA' + 'Staging' + 'Production' +]) param environment string = 'Development' + +@description('The Application Insights instrumentation key for the Web App.') +@minLength(1) param appiKey string + +@description('The Application Insights connection string for the Web App.') +@minLength(1) param appiConnection string + +@description('The resource ID of the App Service Plan.') +@minLength(1) param planId string -@allowed(['api', 'app', 'app,linux', 'functionapp', 'functionapp,linux']) + +@description('The kind of the Web App. Allowed values: api, app, app,linux, functionapp, functionapp,linux. Default is app.') +@allowed([ + 'api' + 'app' + 'app,linux' + 'functionapp' + 'functionapp,linux' +]) param kind string = 'app' -@allowed(['v4.8', 'v6.0', 'v7.0', 'v8.0']) -param dotnetVersion string = 'v8.0' -resource apiResource 'Microsoft.Web/sites@2023-12-01' = { +@description('The .NET version for the Web App. Allowed values: v4.8 (for .NET Framework), 6.0, 7.0, 8.0, 9.0, 10.0 (for .NET). Default is 10.0.') +@allowed([ + 'v4.8' + '6.0' + '7.0' + '8.0' + '9.0' + '10.0' +]) +param dotnetVersion string = '10.0' + +@description('Enable Always On for the App Service') +param alwaysOn bool = false + +resource apiAppResource 'Microsoft.Web/sites@2023-12-01' = { name: name location: location kind: kind tags: empty(tags) ? null : tags properties: { serverFarmId: planId + httpsOnly: true siteConfig: { netFrameworkVersion: dotnetVersion + ftpsState: 'Disabled' + alwaysOn: alwaysOn appSettings: [ { name: 'APPINSIGHTS_INSTRUMENTATIONKEY' @@ -34,6 +80,10 @@ resource apiResource 'Microsoft.Web/sites@2023-12-01' = { name: 'ASPNETCORE_ENVIRONMENT' value: environment } + { + name: 'WEBSITE_RUN_FROM_PACKAGE' + value: '1' + } ] } } @@ -42,4 +92,4 @@ resource apiResource 'Microsoft.Web/sites@2023-12-01' = { } } -output id string = apiResource.id +output id string = apiAppResource.id diff --git a/.azure/modules/apim-apimanagement.bicep b/.azure/modules/apim-apimanagement.bicep index c6f33e6..2a1a0b7 100644 --- a/.azure/modules/apim-apimanagement.bicep +++ b/.azure/modules/apim-apimanagement.bicep @@ -1,15 +1,19 @@ -@description('The name of the API Management service instance') -param apiManagementServiceName string = 'apiservice${uniqueString(resourceGroup().id)}' -@description('The email address of the owner of the service') +@description('The name of the API Management service instance. Must be 1-50 characters, use only letters, numbers, and hyphens, and start/end with a letter or number.') @minLength(1) +@maxLength(50) +param name string = 'apiservice${uniqueString(resourceGroup().id)}' + +@description('The email address of the owner of the API Management service. Must be a valid email address.') +@minLength(5) +@maxLength(100) param publisherEmail string -@description('The name of the owner of the service') +@description('The name of the owner of the API Management service. Must be at least 1 character.') @minLength(1) param publisherName string -@description('The pricing tier of this API Management service') +@description('The pricing tier (SKU) of this API Management service. Allowed values: Developer, Standard, Premium. Default is Developer.') @allowed([ 'Developer' 'Standard' @@ -17,22 +21,26 @@ param publisherName string ]) param sku string = 'Developer' -@description('The instance size of this API Management service.') +@description('Tags to apply to the API Management service resource.') +param tags object = {} + +@description('The instance size (capacity) of this API Management service. Allowed values: 1, 2. Default is 1.') @allowed([ 1 2 ]) -param skuCount int = 1 +param capacity int = 1 -@description('Location for all resources.') +@description('Location for all resources. Defaults to the resource group location.') param location string = resourceGroup().location -resource apiManagementServiceName_resource 'Microsoft.ApiManagement/service@2022-08-01' = { - name: apiManagementServiceName +resource apimResource 'Microsoft.ApiManagement/service@2023-05-01-preview' = { + name: name location: location + tags: tags sku: { name: sku - capacity: skuCount + capacity: capacity } properties: { publisherEmail: publisherEmail diff --git a/.azure/modules/appcs-appconfigurationstore.bicep b/.azure/modules/appcs-appconfigurationstore.bicep index ad3b74c..09372d2 100644 --- a/.azure/modules/appcs-appconfigurationstore.bicep +++ b/.azure/modules/appcs-appconfigurationstore.bicep @@ -1,4 +1,6 @@ -@description('Specifies the name of the App Configuration store.') +@description('Specifies the name of the App Configuration store. 5-50 chars, lowercase letters, numbers, and -') +@minLength(5) +@maxLength(50) param name string @description('Specifies the sku of the App Configuration store.') @@ -11,46 +13,19 @@ param sku string = 'free' @description('Specifies the Azure location where the app configuration store should be created.') param location string = toLower(replace(resourceGroup().location, ' ', '')) -@description('Sentinel key to refresh configs for no label and Development label.') -param appcsKeys array = [ - 'Shared:Sentinel' - 'Shared:Sentinel$Development' -] - -@description('Sentinel value is 1. When changed to 0, will trigger config refreshes.') -param appcsValues array = [ - '1' - '1' -] - -@description('Specifies the content type of the key-value resources. For feature flag, the value should be application/vnd.microsoft.appconfig.ff+json;charset=utf-8. For Key Value reference, the value should be application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8. Otherwise, it\'s optional.') -param contentType string = 'text/plain' - -@description('Adds tags for the key-value resources. It\'s optional') -param tags object = { - tag1: 'tag-value-1' - tag2: 'tag-value-2' -} - resource name_resource 'Microsoft.AppConfiguration/configurationStores@2023-03-01' = { name: name location: location sku: { name: sku } -} - -resource name_appcsKeys 'Microsoft.AppConfiguration/configurationStores/keyValues@2023-03-01' = [for (item, i) in appcsKeys: { - name: '${name}/${item}' + identity: { + type: 'SystemAssigned' + } properties: { - value: appcsValues[i] - contentType: contentType - tags: empty(tags) ? null : tags + disableLocalAuth: true + publicNetworkAccess: 'Disabled' } - dependsOn: [ - name_resource - ] -}] +} + -output reference_key_value_value string = reference(resourceId('Microsoft.AppConfiguration/configurationStores/keyValues', name, appcsKeys[0]), '2020-07-01-preview').value -output reference_key_value_object object = reference(resourceId('Microsoft.AppConfiguration/configurationStores/keyValues', name, appcsKeys[1]), '2020-07-01-preview') diff --git a/.azure/modules/appi-applicationinsights.bicep b/.azure/modules/appi-applicationinsights.bicep index b69eb6b..972a187 100644 --- a/.azure/modules/appi-applicationinsights.bicep +++ b/.azure/modules/appi-applicationinsights.bicep @@ -1,9 +1,25 @@ -param location string + +@description('The Azure region where the Application Insights resource will be deployed. Allowed: eastus, eastus2, westus, westus2, centralus.') +@allowed([ + 'eastus' + 'eastus2' + 'westus' + 'westus2' + 'centralus' +]) +param location string + +@description('Tags to apply to the Application Insights resource.') param tags object = {} -param name string -param Application_Type string -param Flow_Type string + +@description('Specifies the name of the Application Insights resource. 1-255 characters, letters, numbers, and -') +@minLength(1) +@maxLength(255) +param name string + +@description('The resource ID of the Log Analytics workspace to link to Application Insights. Must be a valid resourceId string.') +@minLength(1) param workResourceId string resource appiResource 'Microsoft.Insights/components@2020-02-02' = { @@ -12,10 +28,9 @@ resource appiResource 'Microsoft.Insights/components@2020-02-02' = { tags: empty(tags) ? null : tags kind:'web' properties: { - Application_Type: Application_Type - Flow_Type: Flow_Type + Application_Type: 'web' + Flow_Type: 'Bluefield' WorkspaceResourceId: workResourceId - } } diff --git a/.azure/modules/bot-botservice.bicep b/.azure/modules/bot-botservice.bicep index 26937d7..907d131 100644 --- a/.azure/modules/bot-botservice.bicep +++ b/.azure/modules/bot-botservice.bicep @@ -1,28 +1,41 @@ + +@description('The name of the Bot Service resource. Must be 2-64 characters, using only letters, numbers, and hyphens, starting and ending with a letter or number.') +@minLength(2) +@maxLength(64) param name string + +@description('The SKU (pricing tier) for the Bot Service. Allowed values: F0 (Free), S1 (Standard). Default is S1.') @allowed([ 'F0' 'S1' ]) param sku string = 'S1' + +@description('The Microsoft App ID for the Bot Service. Must be a valid GUID.') +@minLength(1) param msAppId string + +@description('The Microsoft App password/secret value for the Bot Service. Required for authentication.') +@minLength(1) param msAppValue string + +@description('The display name for the Bot Service. Optional, defaults to the resource name if not provided.') +@maxLength(64) param displayName string = '' -param resourceTags object = { - 'Microsoft.BotService/botServices': {} -} + +@description('Tags to apply to the Bot Service resource.') +param tags object = {} var location = resourceGroup().location var uniqueSuffix = toLower(substring(uniqueString(resourceGroup().id, 'Microsoft.BotService/bots', name), 0, 6)) -var botDisplayName = (empty(displayName) ? name : displayName) -var keyVaultName_var = 'kv-${name}' +var botDisplayName = empty(displayName) ? name : displayName +var kvName = 'kv-${name}' var appPasswordSecret = 'bot-${replace(name, '_', '-')}-pwd-${uniqueSuffix}' -var appPasswordSecretId = keyVaultName_appPasswordSecret.id -var empty = {} -var botTags = (contains(resourceTags, 'Microsoft.BotService/botServices') ? resourceTags.Microsoft.BotService / botServices : empty) +var appPasswordSecretId = empty(msAppValue) ? '' : keyVaultName_appPasswordSecret.id resource keyVaultName 'Microsoft.KeyVault/vaults@2023-07-01' = { - name: keyVaultName_var + name: kvName location: location properties: { tenantId: subscription().tenantId @@ -30,19 +43,21 @@ resource keyVaultName 'Microsoft.KeyVault/vaults@2023-07-01' = { family: 'A' name: 'standard' } - enableRbacAuthorization: true + accessPolicies: [] enabledForTemplateDeployment: true } } + resource keyVaultName_appPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = if (!empty(msAppValue)) { parent: keyVaultName - name: '${appPasswordSecret}' + name: appPasswordSecret properties: { value: msAppValue } } + resource name_resource 'Microsoft.BotService/botServices@2022-09-15' = { name: name kind: 'azurebot' @@ -50,25 +65,12 @@ resource name_resource 'Microsoft.BotService/botServices@2022-09-15' = { sku: { name: sku } - tags: botTags + tags: tags properties: { displayName: botDisplayName msaAppId: msAppId openWithHint: 'bfcomposer://' appPasswordHint: appPasswordSecretId + endpoint: 'https://REPLACE-WITH-YOUR-BOT-ENDPOINT/api/messages' } - dependsOn: [] -} - -resource keyVaultName_appPasswordSecret_Microsoft_Resources_provisioned_for 'Microsoft.KeyVault/vaults/secrets/providers/links@2018-02-01' = { - name: '${keyVaultName_var}/${appPasswordSecret}/Microsoft.Resources/provisioned-for' - location: location - properties: { - targetId: resourceId('Microsoft.BotService/bots', name) - sourceId: appPasswordSecretId - } - dependsOn: [ - keyVaultName - name_resource - ] } diff --git a/.azure/modules/cdn-contentdeliverynetwork.bicep b/.azure/modules/cdn-contentdeliverynetwork.bicep index 6c1d7fc..9d18a51 100644 --- a/.azure/modules/cdn-contentdeliverynetwork.bicep +++ b/.azure/modules/cdn-contentdeliverynetwork.bicep @@ -1,16 +1,25 @@ -@minLength(1) + +@description('The name of the Storage Account for CDN. Must be 3-24 characters, using only lowercase letters and numbers.') +@minLength(3) +@maxLength(24) param name string -@minLength(1) +@description('The SKU (pricing tier) for the Storage Account. Allowed values: Standard_LRS, Standard_GRS, Standard_RAGRS, Standard_ZRS, Premium_LRS, Premium_ZRS. Default is Standard_LRS.') +@allowed([ + 'Standard_LRS' + 'Standard_GRS' + 'Standard_RAGRS' + 'Standard_ZRS' + 'Premium_LRS' + 'Premium_ZRS' +]) param sku string = 'Standard_LRS' -var storageAccountName_var = name - resource storageAccountName 'Microsoft.Storage/storageAccounts@2023-05-01' = { - name: storageAccountName_var + name: name location: resourceGroup().location tags: { - displayName: storageAccountName_var + displayName: name } sku: { name: sku diff --git a/.azure/modules/cog-cognitiveservices.bicep b/.azure/modules/cog-cognitiveservices.bicep index 6abddba..e64cf55 100644 --- a/.azure/modules/cog-cognitiveservices.bicep +++ b/.azure/modules/cog-cognitiveservices.bicep @@ -1,6 +1,11 @@ -@description('That name is the name of our application. It has to be unique.Type a name followed by your resource group name. (-)') + +@description('The name of the Cognitive Services account. Must be unique within Azure. Recommended format: -. 3-64 characters, lowercase letters, numbers, and hyphens.') +@minLength(3) +@maxLength(64) param name string = 'CognitiveService-${uniqueString(resourceGroup().id)}' + +@description('The SKU (pricing tier) for the Cognitive Services account. Allowed values: S0 (Standard). Default is S0.') @allowed([ 'S0' ]) @@ -13,7 +18,4 @@ resource name_resource 'Microsoft.CognitiveServices/accounts@2023-05-01' = { name: sku } kind: 'CognitiveServices' - properties: { - statisticsEnabled: false - } } diff --git a/.azure/modules/cosmos-cosmosdb.bicep b/.azure/modules/cosmos-cosmosdb.bicep index 809b742..78b8f61 100644 --- a/.azure/modules/cosmos-cosmosdb.bicep +++ b/.azure/modules/cosmos-cosmosdb.bicep @@ -1,8 +1,10 @@ -@description('The DocumentDB database account name.') + +@description('The name of the Cosmos DB account. Must be 3-44 characters, using only lowercase letters, numbers, and hyphens.') @minLength(3) +@maxLength(44) param name string -@description('The DocumentDB default consistency level for this account.') +@description('The default consistency level for the Cosmos DB account. Allowed values: Eventual, Strong, Session, BoundedStaleness. Default is Session.') @allowed([ 'Eventual' 'Strong' @@ -11,12 +13,12 @@ param name string ]) param consistencyLevel string = 'Session' -@description('When consistencyLevel is set to BoundedStaleness, then this value is required, otherwise it can be ignored.') +@description('The max staleness prefix for BoundedStaleness consistency. Required if consistencyLevel is BoundedStaleness. Default is 10.') @minValue(10) @maxValue(1000) param maxStalenessPrefix int = 10 -@description('When consistencyLevel is set to BoundedStaleness, then this value is required, otherwise it can be ignored.') +@description('The max interval in seconds for BoundedStaleness consistency. Required if consistencyLevel is BoundedStaleness. Default is 5.') @minValue(5) @maxValue(600) param maxIntervalInSeconds int = 5 @@ -27,12 +29,18 @@ resource name_resource 'Microsoft.DocumentDB/databaseAccounts@2024-05-15' = { name: name location: resourceGroup().location properties: { - name: name databaseAccountOfferType: offerType consistencyPolicy: { defaultConsistencyLevel: consistencyLevel maxStalenessPrefix: maxStalenessPrefix maxIntervalInSeconds: maxIntervalInSeconds } + locations: [ + { + locationName: resourceGroup().location + failoverPriority: 0 + isZoneRedundant: false + } + ] } } diff --git a/.azure/modules/dgw-onpremdatagateway.bicep b/.azure/modules/dgw-onpremdatagateway.bicep index 1bfc20d..bcd9894 100644 --- a/.azure/modules/dgw-onpremdatagateway.bicep +++ b/.azure/modules/dgw-onpremdatagateway.bicep @@ -1,8 +1,22 @@ + +@description('The name of the On-premises Data Gateway. Must be 1-80 characters, using only alphanumeric characters and hyphens.') +@minLength(1) +@maxLength(80) param name string + +@description('The Azure region where the On-premises Data Gateway will be deployed.') param location string = resourceGroup().location + +@description('The installation ID for the On-premises Data Gateway.') +@minLength(1) +@maxLength(128) param dgwInstallationId string + +@description('The subscription ID for the On-premises Data Gateway. Defaults to the current subscription.') param subscriptionId string = subscription().id -param tagsByResource object + +@description('Tags to apply to the On-premises Data Gateway resource.') +param tags object = {} var locationShortName = toLower(replace(location, ' ', '')) var gatewayInstallationId = '${subscriptionId}/providers/Microsoft.Web/locations/${locationShortName}/connectionGatewayInstallations/${dgwInstallationId}' @@ -10,7 +24,7 @@ var gatewayInstallationId = '${subscriptionId}/providers/Microsoft.Web/locations resource name_resource 'Microsoft.Web/connectionGateways@2016-06-01' = { name: name location: locationShortName - tags: (contains(tagsByResource, 'Microsoft.Web/connectionGateways') ? tagsByResource.Microsoft.Web / connectionGateways : json('{}')) + tags: tags properties: { connectionGatewayInstallation: { id: gatewayInstallationId diff --git a/.azure/modules/ftpcn-ftpapiconnection.bicep b/.azure/modules/ftpcn-ftpapiconnection.bicep index f78a78d..e95bbb2 100644 --- a/.azure/modules/ftpcn-ftpapiconnection.bicep +++ b/.azure/modules/ftpcn-ftpapiconnection.bicep @@ -1,25 +1,45 @@ + +@description('The name of the FTP API Connection. Must be 1-80 characters, using only alphanumeric characters and hyphens. Default is azureblob.') +@minLength(1) +@maxLength(80) param name string = 'azureblob' + +@description('The address of the FTP server.') +@minLength(1) +@maxLength(255) param ftpServerAddress string -param ftpServerPort string + +@description('The port of the FTP server. Default is 21.') +@minLength(1) +@maxLength(5) +param ftpServerPort string = '21' + +@description('The username for the FTP server.') +@minLength(1) +@maxLength(128) param ftpUsername string + +@description('The password for the FTP server.') +@minLength(1) +@maxLength(128) +@secure() param ftpPassword string +@description('Tags to apply to the FTP API Connection resource.') +param tags object = {} + var locationShortName = toLower(replace(resourceGroup().location, ' ', '')) -var nameLower_var = toLower(replace(replace(name, '-', ''), ' ', '')) -var ftpIsSsl = 'true' -var ftpIsBinaryTransport = 'true' -var acceptAnySshHostKey = 'true' +var nameLower = toLower(replace(replace(name, '-', ''), ' ', '')) -resource nameLower 'Microsoft.Web/connections@2016-06-01' = { - name: nameLower_var +resource connection 'Microsoft.Web/connections@2016-06-01' = { + name: nameLower location: locationShortName - kind: 'V1' - scale: null + tags: tags properties: { displayName: name customParameterValues: {} api: { - name: '${nameLower_var}sftpwithssh' + name: '${nameLower}sftpwithssh' displayName: 'SFTP - SSH' description: 'SFTP (SSH File Transfer Protocol) is a network protocol that provides file access, file transfer, and file management over any reliable data stream. It was designed by the Internet Engineering Task Force (IETF) as an extension of the Secure Shell protocol (SSH) version 2.0 to provide secure file transfer capabilities.' iconUri: 'https://connectoricons-prod.azureedge.net/releases/v1.0.1518/1.0.1518.2564/sftpwithssh/icon.png' @@ -32,8 +52,6 @@ resource nameLower 'Microsoft.Web/connections@2016-06-01' = { userName: ftpUsername password: ftpPassword serverPort: ftpServerPort - isssl: false - disableCertificateValidation: true } } dependsOn: [] diff --git a/.azure/modules/func-functionsapp.bicep b/.azure/modules/func-functionsapp.bicep index 10de799..18ecc57 100644 --- a/.azure/modules/func-functionsapp.bicep +++ b/.azure/modules/func-functionsapp.bicep @@ -1,14 +1,43 @@ + +@description('The name of the Azure Function App. Must be 1-60 characters, using only alphanumeric characters and hyphens.') +@minLength(1) +@maxLength(60) param name string + +@description('The Azure region where the Function App will be deployed.') param location string + +@description('Tags to apply to the Function App resource.') param tags object = {} + +@description('The resource ID of the App Service Plan.') +@minLength(1) param planId string + +@description('The name of the Storage Account for the Function App.') +@minLength(3) +@maxLength(24) param stName string + +@description('The subscription ID for the Storage Account. Defaults to the current subscription.') param stSubscriptionId string = subscription().subscriptionId + +@description('The resource group name for the Storage Account. Defaults to the current resource group.') param stResourceGroupName string = resourceGroup().name + +@description('The Application Insights instrumentation key for the Function App.') +@minLength(1) param appiKey string + +@description('The Application Insights connection string for the Function App.') +@minLength(1) param appiConnection string + +@description('Whether to use a 32-bit worker process. Default is true.') param use32BitWorkerProcess bool = true + +@description('The environment for the Function App. Allowed values: Development, QA, Staging, Production.') @allowed([ 'Development' 'QA' @@ -17,6 +46,8 @@ param use32BitWorkerProcess bool = true ]) param environmentApp string + +@description('The runtime for the Function App. Allowed values: dotnet, python, dotnet-isolated. Default is dotnet.') @allowed([ 'dotnet' 'python' @@ -24,6 +55,8 @@ param environmentApp string ]) param funcRuntime string = 'dotnet' + +@description('The version of the Azure Functions runtime. Allowed values: 1, 2, 3, 4. Default is 4.') @allowed([ 1 2 @@ -32,6 +65,8 @@ param funcRuntime string = 'dotnet' ]) param funcVersion int = 4 + +@description('Whether the Function App is always on. Default is false.') param alwaysOn bool = false resource functionapp 'Microsoft.Web/sites@2023-12-01' = { diff --git a/.azure/modules/hcn-hybridconnection.bicep b/.azure/modules/hcn-hybridconnection.bicep index a25dd0a..e354e26 100644 --- a/.azure/modules/hcn-hybridconnection.bicep +++ b/.azure/modules/hcn-hybridconnection.bicep @@ -1,10 +1,16 @@ + +@description('The name of the Hybrid Connection. Must be 1-80 characters, using only alphanumeric characters and hyphens.') +@minLength(1) +@maxLength(80) param name string + +@description('The name of the Azure Relay namespace. Must be 6-50 characters, using only alphanumeric characters and hyphens.') +@minLength(6) +@maxLength(50) param relayName string -param location string = resourceGroup().location resource relayName_name 'Microsoft.Relay/namespaces/hybridConnections@2021-11-01' = { name: '${relayName}/${name}' - location: location properties: { requiresClientAuthorization: true } diff --git a/.azure/modules/hnbind-hostNameBinding.bicep b/.azure/modules/hnbind-hostNameBinding.bicep index 8c3b862..e4f4e1d 100644 --- a/.azure/modules/hnbind-hostNameBinding.bicep +++ b/.azure/modules/hnbind-hostNameBinding.bicep @@ -1,15 +1,20 @@ + +@description('The fully qualified domain name (FQDN) to bind to the App Service. Must be 1-128 characters.') @minLength(1) @maxLength(128) param fqdn string +@description('The headless domain name to bind to the App Service. Must be 1-128 characters.') @minLength(1) @maxLength(128) param headlessDn string +@description('The name of the App Service site. Must be 1-128 characters.') @minLength(1) @maxLength(128) param siteName string +@description('The certificate thumbprint for SSL binding. Must be 1-256 characters.') @minLength(1) @maxLength(256) param thumbprint string @@ -19,7 +24,6 @@ var deployHeadlessDn = (empty(headlessDn) ? bool('false') : bool('true')) resource siteName_fqdn 'Microsoft.Web/sites/hostNameBindings@2023-12-01' = if (deployFqdn) { name: '${siteName}/${fqdn}' - location: resourceGroup().location properties: { siteName: siteName sslState: 'SniEnabled' @@ -29,7 +33,6 @@ resource siteName_fqdn 'Microsoft.Web/sites/hostNameBindings@2023-12-01' = if (d resource siteName_headlessDn 'Microsoft.Web/sites/hostNameBindings@2023-12-01' = if (deployHeadlessDn) { name: '${siteName}/${headlessDn}' - location: resourceGroup().location properties: { siteName: siteName sslState: 'SniEnabled' diff --git a/.azure/modules/kv-keyvault.bicep b/.azure/modules/kv-keyvault.bicep index 88187b7..0bbcd3e 100644 --- a/.azure/modules/kv-keyvault.bicep +++ b/.azure/modules/kv-keyvault.bicep @@ -1,11 +1,45 @@ -param name string -param location string -param sku string -param tenantId string + +@description('The name of the Key Vault. Must be 3-24 characters, using only alphanumeric characters and hyphens.') +@minLength(3) +@maxLength(24) +param name string + +@description('The Azure region where the Key Vault will be deployed.') +param location string = resourceGroup().location + +@description('The SKU (pricing tier) for the Key Vault. Allowed values: standard, premium. Default is standard.') +@allowed([ + 'standard' + 'premium' +]) +param sku string = 'standard' + +@description('The tenant ID of the Azure Active Directory that will be used for authentication.') +@minLength(36) +@maxLength(36) +param tenantId string = tenant().tenantId + +@description('Tags to apply to the Key Vault resource.') param tags object = {} + +@description('Access policies to assign to the Key Vault.') param accessPolicies array = [] + +@description('Enable RBAC authorization for the Key Vault. Default is true.') param enableRbacAuthorization bool = true +@description('List of allowed IP addresses for Key Vault access. Default is empty (no IPs allowed).') +param allowedIpRules array = [] + +@description('List of allowed Virtual Network resource IDs for Key Vault access. Default is empty (no VNets allowed).') +param allowedVirtualNetworkResourceIds array = [] + +@description('Enable soft delete for the Key Vault. Default is true.') +param enableSoftDelete bool = true + +@description('Enable purge protection for the Key Vault. Default is true.') +param enablePurgeProtection bool = true + resource kvResource 'Microsoft.KeyVault/vaults@2023-07-01' = { name: name location: location @@ -15,18 +49,20 @@ resource kvResource 'Microsoft.KeyVault/vaults@2023-07-01' = { enabledForDiskEncryption: true enabledForTemplateDeployment: true tenantId: tenantId - publicNetworkAccess: 'Enabled' + publicNetworkAccess: 'Disabled' sku: { name: sku family: 'A' } accessPolicies: accessPolicies == [] && enableRbacAuthorization == true ? null : accessPolicies enableRbacAuthorization: enableRbacAuthorization + enableSoftDelete: enableSoftDelete + enablePurgeProtection: enablePurgeProtection networkAcls: { - defaultAction: 'Allow' + defaultAction: 'Deny' bypass: 'AzureServices' - virtualNetworkRules: [] - ipRules: [] + virtualNetworkRules: allowedVirtualNetworkResourceIds + ipRules: allowedIpRules } } } diff --git a/.azure/modules/luis-languageunderstanding.bicep b/.azure/modules/luis-languageunderstanding.bicep index dda1234..219229d 100644 --- a/.azure/modules/luis-languageunderstanding.bicep +++ b/.azure/modules/luis-languageunderstanding.bicep @@ -1,19 +1,35 @@ + +@description('The name of the LUIS Cognitive Service resource. Must be 2-64 characters, using only alphanumeric characters and hyphens.') +@minLength(2) +@maxLength(64) param name string + +@description('The Azure region where the LUIS Cognitive Service resource will be deployed.') param location string = resourceGroup().location + +@description('The SKU (pricing tier) for the LUIS Cognitive Service resource. Allowed values: F0, S0. Default is F0.') @allowed([ 'F0' 'S0' ]) param sku string = 'F0' + +@description('The name of the LUIS Authoring resource. Must be 2-64 characters, using only alphanumeric characters and hyphens.') +@minLength(2) +@maxLength(64) param authoringName string + +@description('The Azure region for the LUIS Authoring resource. Allowed values: westus, eastus. Default is westus.') @allowed([ 'westus' 'eastus' ]) param authoringLocation string = 'westus' + +@description('The SKU (pricing tier) for the LUIS Authoring resource. Allowed value: F0. Default is F0.') @allowed([ 'F0' ]) diff --git a/.azure/modules/nested_newWorkspaceTemplate.bicep b/.azure/modules/nested_newWorkspaceTemplate.bicep deleted file mode 100644 index 0acb0e6..0000000 --- a/.azure/modules/nested_newWorkspaceTemplate.bicep +++ /dev/null @@ -1,7 +0,0 @@ -param workName string - -resource workName_resource 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { - name: workName - location: resourceGroup().location - properties: {} -} diff --git a/.azure/modules/nsg-networksecuritygroup.bicep b/.azure/modules/nsg-networksecuritygroup.bicep new file mode 100644 index 0000000..f1b6426 --- /dev/null +++ b/.azure/modules/nsg-networksecuritygroup.bicep @@ -0,0 +1,33 @@ +@description('Array of NSG security rules to apply to this NSG') +param securityRules array = [] +@description('The name of the Network Security Group (NSG)') +param name string + +@description('Tags to apply to the NSG') +param tags object = {} + +@description('Location for the NSG') +param location string = resourceGroup().location + +resource nsgResource 'Microsoft.Network/networkSecurityGroups@2023-05-01' = { + name: name + location: location + tags: tags +} + +resource rule 'Microsoft.Network/networkSecurityGroups/securityRules@2023-05-01' = [for ruleObj in securityRules: { + name: ruleObj.name + parent: nsgResource + properties: { + priority: ruleObj.priority + direction: ruleObj.direction + access: ruleObj.access + protocol: ruleObj.protocol + sourcePortRange: ruleObj.sourcePortRange + destinationPortRange: ruleObj.destinationPortRange + sourceAddressPrefix: ruleObj.sourceAddressPrefix + destinationAddressPrefix: ruleObj.destinationAddressPrefix + } +}] + +output id string = nsgResource.id diff --git a/.azure/modules/nsgrule-networksecuritygrouprule.bicep b/.azure/modules/nsgrule-networksecuritygrouprule.bicep new file mode 100644 index 0000000..7391880 --- /dev/null +++ b/.azure/modules/nsgrule-networksecuritygrouprule.bicep @@ -0,0 +1,26 @@ +@description('The name of the Network Security Group (NSG)') +param nsgName string + +@description('Array of NSG security rules to apply to this NSG') +param securityRules array = [] + +resource nsgResource 'Microsoft.Network/networkSecurityGroups@2023-05-01' existing = { + name: nsgName +} + +resource rule 'Microsoft.Network/networkSecurityGroups/securityRules@2023-05-01' = [for ruleObj in securityRules: { + name: ruleObj.name + parent: nsgResource + properties: { + priority: ruleObj.priority + direction: ruleObj.direction + access: ruleObj.access + protocol: ruleObj.protocol + sourcePortRange: ruleObj.sourcePortRange + destinationPortRange: ruleObj.destinationPortRange + sourceAddressPrefix: ruleObj.sourceAddressPrefix + destinationAddressPrefix: ruleObj.destinationAddressPrefix + } +}] + +output id string = nsgResource.id diff --git a/.azure/modules/o365cn-o365apiconnection.bicep b/.azure/modules/o365cn-o365apiconnection.bicep index bdd94a6..db7de5f 100644 --- a/.azure/modules/o365cn-o365apiconnection.bicep +++ b/.azure/modules/o365cn-o365apiconnection.bicep @@ -1,20 +1,23 @@ + +@description('The name of the Office 365 API Connection. Must be 1-80 characters, using only alphanumeric characters and hyphens. Default is teams.') +@minLength(1) +@maxLength(80) param name string = 'teams' var locationLower = toLower(replace(resourceGroup().location, ' ', '')) -var nameLower_var = toLower(replace(replace(name, '-', ''), ' ', '')) +var nameLower = toLower(replace(replace(name, '-', ''), ' ', '')) -resource nameLower 'Microsoft.Web/connections@2016-06-01' = { - name: nameLower_var +resource connection 'Microsoft.Web/connections@2016-06-01' = { + name: nameLower location: locationLower - kind: 'V1' properties: { displayName: name customParameterValues: {} api: { - name: nameLower_var + name: nameLower displayName: 'Office 365' description: 'Microsoft Teams enables you to get all your content, tools and conversations in the Team workspace with Office 365.' - iconUri: 'https://connectoricons-prod.azureedge.net/releases/v1.0.1505/1.0.1505.2520/${nameLower_var}/icon.png' + iconUri: 'https://connectoricons-prod.azureedge.net/releases/v1.0.1505/1.0.1505.2520/${nameLower}/icon.png' brandColor: '#4B53BC' id: '${subscription().id}/providers/Microsoft.Web/locations/${resourceGroup().location}/managedApis/office365' type: 'Microsoft.Web/locations/managedApis' diff --git a/.azure/modules/plan-appplan.bicep b/.azure/modules/plan-appplan.bicep deleted file mode 100644 index 094005e..0000000 --- a/.azure/modules/plan-appplan.bicep +++ /dev/null @@ -1,39 +0,0 @@ -@minLength(1) -param name string - -@description('Describes plan\'s pricing tier and capacity. Check details at https://azure.microsoft.com/en-us/pricing/details/app-service/') -@allowed([ - 'F1' - 'D1' - 'B1' - 'B2' - 'B3' - 'S1' - 'S2' - 'S3' - 'P1' - 'P2' - 'P3' - 'P4' - 'Y1' -]) -param sku string = 'F1' - -@description('Describes plan\'s instance count') -@minValue(1) -param skuCapacity int = 1 - -resource name_resource 'Microsoft.Web/serverfarms@2023-12-01' = { - name: name - location: resourceGroup().location - tags: { - displayName: 'HostingPlan' - } - sku: { - name: sku - capacity: skuCapacity - } - properties: { - name: name - } -} diff --git a/.azure/modules/plan-appserviceplan.bicep b/.azure/modules/plan-appserviceplan.bicep index 86c6511..5cd63cf 100644 --- a/.azure/modules/plan-appserviceplan.bicep +++ b/.azure/modules/plan-appserviceplan.bicep @@ -1,21 +1,87 @@ +@description('The name of the App Service Plan. Must be 1-40 characters, using only alphanumeric characters and hyphens.') +@minLength(1) +@maxLength(40) +param name string -param name string -param location string -param sku string +@description('The Azure region where the App Service Plan will be deployed.') +param location string + +@description('The SKU (pricing tier) for the App Service Plan. Allowed values: F1, D1, B1, B2, B3, S1, S2, S3, P1, P2, P3, P4, Y1. Default is F1.') +@allowed([ + 'F1' + 'D1' + 'B1' + 'B2' + 'B3' + 'S1' + 'S2' + 'S3' + 'P1' + 'P2' + 'P3' + 'P4' + 'Y1' +]) +param sku string = 'F1' + +@description('Tags to apply to the App Service Plan resource.') param tags object = {} +@description('The OS type for the App Service Plan. Allowed values: Windows, Linux.') +@allowed([ + 'Windows' + 'Linux' +]) +param osType string = 'Windows' + +@description('Enable zone redundancy for the App Service Plan (PremiumV2 and higher only).') +param zoneRedundant bool = false + +@description('The number of worker instances.') +@minValue(1) +param capacity int = 1 + +@description('Enable per-site scaling (dedicated plans only).') +param perSiteScaling bool = false + +@description('Enable elastic scale (Premium plans only).') +param elasticScaleEnabled bool = false + +@description('Indicates if the plan is reserved for Linux (true) or Windows (false).') +param reserved bool = (osType == 'Linux') + +@description('Enable diagnostics settings for the App Service Plan.') +param enableDiagnostics bool = false + +@description('Diagnostics settings configuration (if enabled).') +param diagnosticsSettings object = {} + resource planResource 'Microsoft.Web/serverfarms@2023-01-01' = { name: name - kind:'Windows' location: location + kind: osType tags: empty(tags) ? null : tags - properties: { - reserved: false - } sku: { name: sku + capacity: capacity } - + properties: { + reserved: reserved + perSiteScaling: perSiteScaling + elasticScaleEnabled: elasticScaleEnabled + zoneRedundant: zoneRedundant + } +} + +resource diag 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (enableDiagnostics) { + name: '${planResource.name}-diagnostics' + scope: planResource + properties: diagnosticsSettings } output id string = planResource.id +output name string = planResource.name +output location string = planResource.location +output kind string = planResource.kind +output sku object = planResource.sku +output properties object = planResource.properties diff --git a/.azure/modules/qna-qnamaker.bicep b/.azure/modules/qna-qnamaker.bicep index 48be1e9..22d3931 100644 --- a/.azure/modules/qna-qnamaker.bicep +++ b/.azure/modules/qna-qnamaker.bicep @@ -51,10 +51,6 @@ resource name_resource 'Microsoft.CognitiveServices/accounts@2023-05-01' = { } customSubDomainName: name } - dependsOn: [ - azureSearchName - resourceId('microsoft.insights/components/', appiName) - ] } resource azureSearchName 'Microsoft.Search/searchServices@2023-11-01' = { diff --git a/.azure/modules/relay-relaynamespace.bicep b/.azure/modules/relay-relaynamespace.bicep index 142b184..2cbaf75 100644 --- a/.azure/modules/relay-relaynamespace.bicep +++ b/.azure/modules/relay-relaynamespace.bicep @@ -1,7 +1,13 @@ + +@description('The name of the Azure Relay namespace. Must be 6-50 characters, using only alphanumeric characters and hyphens.') +@minLength(6) +@maxLength(50) param name string + +@description('The Azure region where the Relay namespace will be deployed.') param location string = resourceGroup().location -@description('Describes plan\'s pricing tier and capacity. Check details at https://azure.microsoft.com/en-us/pricing/details/app-service/') +@description('The SKU (pricing tier) for the Azure Relay namespace. Allowed value: Standard. Default is Standard.') @allowed([ 'Standard' ]) @@ -20,7 +26,6 @@ resource name_resource 'Microsoft.Relay/namespaces@2018-01-01-preview' = { resource name_RootManageSharedAccessKey 'Microsoft.Relay/namespaces/authorizationRules@2021-11-01' = { parent: name_resource name: 'RootManageSharedAccessKey' - location: location properties: { rights: [ 'Listen' @@ -33,7 +38,6 @@ resource name_RootManageSharedAccessKey 'Microsoft.Relay/namespaces/authorizatio resource name_default 'Microsoft.Relay/namespaces/networkRuleSets@2021-11-01' = { parent: name_resource name: 'default' - location: location properties: { defaultAction: 'Deny' ipRules: [] diff --git a/.azure/modules/rg-resourcegroup.bicep b/.azure/modules/rg-resourcegroup.bicep index d71eb61..bff3347 100644 --- a/.azure/modules/rg-resourcegroup.bicep +++ b/.azure/modules/rg-resourcegroup.bicep @@ -1,7 +1,15 @@ targetScope='subscription' + +@description('The name of the Resource Group. Must be 1-90 characters, using only alphanumeric characters, hyphens, underscores, parentheses, and periods.') +@minLength(1) +@maxLength(90) param name string + +@description('The Azure region where the Resource Group will be deployed.') param location string + +@description('Tags to apply to the Resource Group.') param tags object = {} resource rgResource 'Microsoft.Resources/resourceGroups@2024-03-01' = { diff --git a/.azure/modules/sb-servicebus.bicep b/.azure/modules/sb-servicebus.bicep index 53538dd..57584cb 100644 --- a/.azure/modules/sb-servicebus.bicep +++ b/.azure/modules/sb-servicebus.bicep @@ -1,13 +1,25 @@ + +@description('The name of the Service Bus namespace. Must be 6-50 characters, using only alphanumeric characters and hyphens.') +@minLength(6) +@maxLength(50) param name string + +@description('The SKU (pricing tier) for the Service Bus namespace. Allowed values: Basic, Standard, Premium. Default is Basic.') +@allowed([ + 'Basic' + 'Standard' + 'Premium' +]) param sku string = 'Basic' -@description('Specifies the Azure location where the resource should be created.') + +@description('Specifies the Azure location where the Service Bus namespace should be created.') param location string = toLower(replace(resourceGroup().location, ' ', '')) -var nameAlphaNumeric_var = replace(replace(name, '-', ''), '.', '') +var nameAlphanumeric = replace(replace(name, '-', ''), '.', '') -resource nameAlphaNumeric 'Microsoft.ServiceBus/namespaces@2021-11-01' = { - name: nameAlphaNumeric_var +resource namespace 'Microsoft.ServiceBus/namespaces@2021-11-01' = { + name: nameAlphanumeric location: location sku: { name: sku @@ -18,10 +30,9 @@ resource nameAlphaNumeric 'Microsoft.ServiceBus/namespaces@2021-11-01' = { } } -resource nameAlphaNumeric_RootManageSharedAccessKey 'Microsoft.ServiceBus/namespaces/AuthorizationRules@2021-11-01' = { - parent: nameAlphaNumeric +resource authrule 'Microsoft.ServiceBus/namespaces/AuthorizationRules@2021-11-01' = { + parent: namespace name: 'RootManageSharedAccessKey' - location: location properties: { rights: [ 'Listen' @@ -31,10 +42,9 @@ resource nameAlphaNumeric_RootManageSharedAccessKey 'Microsoft.ServiceBus/namesp } } -resource nameAlphaNumeric_default 'Microsoft.ServiceBus/namespaces/networkRuleSets@2021-11-01' = if (sku == 'Premium') { - parent: nameAlphaNumeric +resource netruleset 'Microsoft.ServiceBus/namespaces/networkRuleSets@2021-11-01' = if (sku == 'Premium') { + parent: namespace name: 'default' - location: location properties: { defaultAction: 'Deny' virtualNetworkRules: [] diff --git a/.azure/modules/sent-loganalyticsworkspace.bicep b/.azure/modules/sent-loganalyticsworkspace.bicep new file mode 100644 index 0000000..7118bd7 --- /dev/null +++ b/.azure/modules/sent-loganalyticsworkspace.bicep @@ -0,0 +1,45 @@ + +@description('The name of the Log Analytics workspace. Must be 4-63 characters, using only letters, numbers, and hyphens.') +@minLength(4) +@maxLength(63) +param name string + +@description('The Azure region where the Log Analytics workspace will be deployed.') +param location string + +@description('The SKU (pricing tier) for the Log Analytics workspace. Allowed values: Free, PerGB2018, CapacityReservation. Default is PerGB2018.') +@allowed([ + 'Free' + 'PerGB2018' + 'CapacityReservation' +]) +param sku string = 'PerGB2018' + +@description('Tags to apply to the Log Analytics workspace resource.') +param tags object = {} + +@description('The data retention period in days. Minimum is 30. Default is 30.') +@minValue(30) +param retentionInDays int = 30 + +resource workResource 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { + name: name + location: location + tags: empty(tags) ? null : tags + properties: { + sku: { + name: sku + } + retentionInDays: retentionInDays + } +} + + +resource onboarding 'Microsoft.SecurityInsights/onboardingStates@2021-03-01-preview' = { + name: 'default' + scope: workResource + properties: {} +} + +output id string = workResource.id +output onboardingId string = onboarding.id diff --git a/.azure/modules/sftpcn-sftpsshapiconnection.bicep b/.azure/modules/sftpcn-sftpsshapiconnection.bicep index b498ee8..2e4c800 100644 --- a/.azure/modules/sftpcn-sftpsshapiconnection.bicep +++ b/.azure/modules/sftpcn-sftpsshapiconnection.bicep @@ -1,8 +1,33 @@ + +@description('The name of the SFTP SSH API Connection. Must be 1-80 characters, using only alphanumeric characters and hyphens. Default is azureblob.') +@minLength(1) +@maxLength(80) param name string = 'azureblob' + +@description('The address of the SFTP server.') +@minLength(1) +@maxLength(255) param ftpServerAddress string -param ftpServerPort string + +@description('The port of the SFTP server. Default is 22.') +@minLength(1) +@maxLength(5) +param ftpServerPort string = '22' + +@description('The username for the SFTP server.') +@minLength(1) +@maxLength(128) param ftpUsername string + +@description('The password for the SFTP server.') +@minLength(1) +@maxLength(128) +@secure() param ftpPassword string + +@description('The root folder for the SFTP connection. Default is /.') +@minLength(1) +@maxLength(255) param ftpRootFolder string = '/' @description('SSH private key (the content of the file should be provided entirely as is, in the multiline format)') @@ -20,16 +45,16 @@ param ftpAcceptAnySshKey bool = true param ftpHostKeyFingerprint string = '' var locationShortName = toLower(replace(resourceGroup().location, ' ', '')) -var nameLower_var = toLower(replace(replace(name, '-', ''), ' ', '')) +var nameLower = toLower(replace(replace(name, '-', ''), ' ', '')) -resource nameLower 'MICROSOFT.WEB/CONNECTIONS@2018-07-01-preview' = { - name: nameLower_var +resource connection 'Microsoft.Web/connections@2016-06-01' = { + name: nameLower location: locationShortName properties: { api: { id: '${subscription().id}/providers/Microsoft.Web/locations/${locationShortName}/managedApis/sftpwithssh' type: 'Microsoft.Web/locations/managedApis' - name: '${nameLower_var}sftpwithssh' + name: '${nameLower}sftpwithssh' displayName: 'SFTP - SSH' description: 'SFTP (SSH File Transfer Protocol) is a network protocol that provides file access, file transfer, and file management over any reliable data stream. It was designed by the Internet Engineering Task Force (IETF) as an extension of the Secure Shell protocol (SSH) version 2.0 to provide secure file transfer capabilities.' iconUri: 'https://connectoricons-prod.azureedge.net/releases/v1.0.1518/1.0.1518.2564/sftpwithssh/icon.png' @@ -43,9 +68,9 @@ resource nameLower 'MICROSOFT.WEB/CONNECTIONS@2018-07-01-preview' = { sshPrivateKey: ftpPrivateKey sshPrivateKeyPassphrase: ftpPassphrase portNumber: ftpServerPort - acceptAnySshHostKey: ftpAcceptAnySshKey + acceptAnySshHostKey: string(ftpAcceptAnySshKey) sshHostKeyFingerprint: ftpHostKeyFingerprint rootFolder: ftpRootFolder } } -} \ No newline at end of file +} diff --git a/.azure/modules/sjc-schedulejobcollection.bicep b/.azure/modules/sjc-schedulejobcollection.bicep index c6dc2cb..b1d97fe 100644 --- a/.azure/modules/sjc-schedulejobcollection.bicep +++ b/.azure/modules/sjc-schedulejobcollection.bicep @@ -1,23 +1,37 @@ + +@description('The name of the Job Collection. Must be 1-60 characters, using only alphanumeric characters and hyphens.') @minLength(1) @maxLength(60) param name string +@description('The Azure region where the Job Collection will be deployed.') @minLength(1) @maxLength(60) param location string = toLower(replace(resourceGroup().location, ' ', '')) -@description('Describes jobCollection pricing tier') +@description('The SKU (pricing tier) for the Job Collection. Allowed value: Standard. Default is Standard.') @allowed([ - 'standard' + 'Standard' ]) -param sku string = 'standard' +param sku string = 'Standard' +@description('The name of the associated Web App. Must be 1-60 characters, using only alphanumeric characters and hyphens.') @minLength(1) @maxLength(60) param webName string + +@description('The maximum number of jobs allowed in the Job Collection. Default is 10.') +@minValue(1) param maxJobs int = 10 + +@description('The interval for the job recurrence. Default is 5.') +@minValue(1) param timerInterval int = 5 + +@description('The frequency for the job recurrence. Default is minute.') param timerFrequency string = 'minute' + +@description('The start time for the scheduled job. Default is the current UTC time.') param startTime string = utcNow() var nameLower = toLower(replace(replace(name, '-', ''), ' ', '')) diff --git a/.azure/modules/snet-virtualnetworksubnet.bicep b/.azure/modules/snet-virtualnetworksubnet.bicep new file mode 100644 index 0000000..66da69c --- /dev/null +++ b/.azure/modules/snet-virtualnetworksubnet.bicep @@ -0,0 +1,23 @@ +@description('Name of the existing virtual network to add the subnet to') +param vnetName string + +@description('Name of the subnet to create') +param snetName string + +@description('Address prefix for the subnet (e.g., 10.0.0.0/24)') +param cidr string + +@description('Resource ID of the Network Security Group to associate with the subnet') +param nsgId string = '' + +resource subnet 'Microsoft.Network/virtualNetworks/subnets@2025-01-01' = { + name: '${vnetName}/${snetName}' + properties: { + addressPrefix: cidr + networkSecurityGroup: empty(nsgId) ? null : { + id: nsgId + } + } +} + +output id string = subnet.id diff --git a/.azure/modules/spocn-sharepointonlineapiconnection.bicep b/.azure/modules/spocn-sharepointonlineapiconnection.bicep index b56983d..52eaf6b 100644 --- a/.azure/modules/spocn-sharepointonlineapiconnection.bicep +++ b/.azure/modules/spocn-sharepointonlineapiconnection.bicep @@ -1,21 +1,26 @@ + +@description('The name of the SharePoint Online API Connection. Must be 1-80 characters, using only alphanumeric characters and hyphens.') +@minLength(1) +@maxLength(80) param name string + +@description('The Azure AD tenant ID for the SharePoint Online connection. Defaults to the current subscription tenant.') param tenantId string = subscription().tenantId var locationShortName = toLower(replace(resourceGroup().location, ' ', '')) -var nameLower_var = toLower(replace(replace(name, '-', ''), ' ', '')) +var nameLower = toLower(replace(replace(name, '-', ''), ' ', '')) -resource nameLower 'Microsoft.Web/connections@2016-06-01' = { - name: nameLower_var +resource connection 'Microsoft.Web/connections@2016-06-01' = { + name: nameLower location: locationShortName - kind: 'V1' properties: { displayName: name customParameterValues: {} api: { - name: nameLower_var + name: nameLower displayName: 'SharePoint' description: 'SharePoint helps organizations share and collaborate with colleagues, partners, and customers. You can connect to SharePoint Online or to an on-premises SharePoint 2013 or 2016 farm using the On-Premises Data Gateway to manage documents and list items.' - iconUri: 'https://connectoricons-prod.azureedge.net/releases/v1.0.1533/1.0.1533.2600/${nameLower_var}/icon.png' + iconUri: 'https://connectoricons-prod.azureedge.net/releases/v1.0.1533/1.0.1533.2600/${nameLower}/icon.png' brandColor: '#036C70' id: '${subscription().id}/providers/Microsoft.Web/locations/${locationShortName}/managedApis/sharepointonline' type: 'Microsoft.Web/locations/managedApis' diff --git a/.azure/modules/sql-sqlserver.bicep b/.azure/modules/sql-sqlserver.bicep index 9bfb674..2ccb2bd 100644 --- a/.azure/modules/sql-sqlserver.bicep +++ b/.azure/modules/sql-sqlserver.bicep @@ -1,21 +1,30 @@ + +@description('The name of the SQL Server. Must be 1-60 characters, using only alphanumeric characters and hyphens.') @minLength(1) @maxLength(60) param name string +@description('The Azure region where the SQL Server will be deployed.') param location string = resourceGroup().location +@description('Tags to apply to the SQL Server resource.') param tags object = {} +@description('The administrator login name for the SQL Server. Must be 1-60 characters.') @minLength(1) @maxLength(60) param adminLogin string +@description('The administrator password for the SQL Server. Must be 1-128 characters.') @minLength(1) @maxLength(128) @secure() param adminPassword string +@description('The starting IP address for the SQL Server firewall rule. Default is 0.0.0.0.') param startIpAddress string = '0.0.0.0' + +@description('The ending IP address for the SQL Server firewall rule. Default is 0.0.0.0.') param endIpAddress string = '0.0.0.0' diff --git a/.azure/modules/sql-sqlserverdatabase.bicep b/.azure/modules/sql-sqlserverdatabase.bicep index f5c95a3..0e37952 100644 --- a/.azure/modules/sql-sqlserverdatabase.bicep +++ b/.azure/modules/sql-sqlserverdatabase.bicep @@ -1,30 +1,56 @@ + // Sql Server +@description('The name of the SQL Server. Must be 1-60 characters, using only alphanumeric characters and hyphens.') @minLength(1) @maxLength(60) param name string + +@description('The Azure region where the SQL Server will be deployed.') param location string = resourceGroup().location + +@description('Tags to apply to the SQL Server resource.') param tags object = {} + +@description('The administrator login name for the SQL Server. Must be 1-60 characters.') @minLength(1) @maxLength(60) param adminLogin string + +@description('The administrator password for the SQL Server. Must be 1-128 characters.') @minLength(1) @maxLength(128) @secure() param adminPassword string + +@description('The starting IP address for the SQL Server firewall rule.') param startIpAddress string = '0.0.0.0' + +@description('The ending IP address for the SQL Server firewall rule.') param endIpAddress string = '0.0.0.0' + // Sql Database +@description('The name of the SQL Database. Must be 1-60 characters, using only alphanumeric characters and hyphens.') @minLength(1) @maxLength(60) param sqldbName string + +@description('The compute capacity for the SQL Database SKU. Default is 5.') +@minValue(1) param sqlCapacity int = 5 + +@description('The collation for the SQL Database. Default is SQL_Latin1_General_CP1_CI_AS.') param collation string = 'SQL_Latin1_General_CP1_CI_AS' + +@description('The SKU (pricing tier) for the SQL Database. Allowed values: Basic, Standard, Premium. Default is Basic.') @allowed([ 'Basic' 'Standard' 'Premium' ]) param sku string = 'Basic' + +@description('The maximum size of the SQL Database in bytes. Default is 1073741824 (1 GB).') +@minValue(1048576) param maxSizeBytes int = 1073741824 resource sqlServer 'Microsoft.Sql/servers@2023-08-01-preview' = { diff --git a/.azure/modules/sqlcn-sqldgwconnection.bicep b/.azure/modules/sqlcn-sqldgwconnection.bicep index 6d53bf3..0f158e3 100644 --- a/.azure/modules/sqlcn-sqldgwconnection.bicep +++ b/.azure/modules/sqlcn-sqldgwconnection.bicep @@ -1,27 +1,65 @@ + +@description('The name of the SQL Data Gateway Connection. Must be 1-80 characters, using only alphanumeric characters and hyphens.') +@minLength(1) +@maxLength(80) param name string + +@description('The Azure region where the SQL Data Gateway Connection will be deployed.') param location string = resourceGroup().location + +@description('The subscription ID for the connection. Defaults to the current subscription.') param subscriptionId string = subscription().subscriptionId + +@description('The name of the Data Gateway.') +@minLength(1) +@maxLength(80) param dgwName string + +@description('The resource group name of the Data Gateway.') +@minLength(1) +@maxLength(90) param dgwResourceGroupName string + +@description('The SQL authentication type. Allowed values: basic, windows. Default is windows.') @allowed([ 'basic' 'windows' ]) param sqlAuthType string = 'windows' + +@description('The name of the SQL Server.') +@minLength(1) +@maxLength(128) param sqlServerName string + +@description('The name of the SQL Database.') +@minLength(1) +@maxLength(128) param sqlDatabaseName string + +@description('The SQL user name.') +@minLength(1) +@maxLength(128) param sqlUserName string + +@description('The SQL user password.') +@minLength(1) +@maxLength(128) +@secure() param sqlUserPassword string + +@description('Whether to encrypt the SQL connection. Default is false.') param encryptConnection bool = false + +@description('The privacy setting for the connection. Default is None.') param privacySetting string = 'None' var locationShortName = toLower(replace(location, ' ', '')) -resource name_resource 'Microsoft.Web/connections@2016-06-01' = { +resource conection 'Microsoft.Web/connections@2016-06-01' = { name: name location: locationShortName - kind: 'V1' properties: { displayName: name customParameterValues: {} diff --git a/.azure/modules/sqldb-sqldatabase.bicep b/.azure/modules/sqldb-sqldatabase.bicep index 92726fa..b1445e8 100644 --- a/.azure/modules/sqldb-sqldatabase.bicep +++ b/.azure/modules/sqldb-sqldatabase.bicep @@ -1,18 +1,37 @@ + +@description('The name of the SQL Database. Must be 1-60 characters, using only alphanumeric characters and hyphens.') @minLength(1) @maxLength(60) param name string + +@description('The Azure region where the SQL Database will be deployed.') param location string = resourceGroup().location + +@description('Tags to apply to the SQL Database resource.') param tags object = {} -@description('Sku for the database') + +@description('The SKU (pricing tier) for the SQL Database. Allowed values: Basic, Standard, Premium. Default is Basic.') @allowed([ 'Basic' 'Standard' 'Premium' ]) param sku string = 'Basic' + +@description('The compute capacity for the SQL Database SKU. Default is 5.') +@minValue(1) param sqlCapacity int = 5 + +@description('The collation for the SQL Database. Default is SQL_Latin1_General_CP1_CI_AS.') param collation string = 'SQL_Latin1_General_CP1_CI_AS' + +@description('The maximum size of the SQL Database in bytes. Default is 1073741824 (1 GB).') +@minValue(1048576) param maxSizeBytes int = 1073741824 + +@description('The name of the parent SQL Server resource.') +@minLength(1) +@maxLength(60) param sqlName string resource sqlDatabase 'Microsoft.Sql/servers/databases@2023-08-01-preview' = { diff --git a/.azure/modules/st-storageaccount.bicep b/.azure/modules/st-storageaccount.bicep index 113208c..3fc2333 100644 --- a/.azure/modules/st-storageaccount.bicep +++ b/.azure/modules/st-storageaccount.bicep @@ -1,7 +1,29 @@ + +@description('The Azure region where the storage account will be deployed.') param location string + +@description('Resource tags to apply to the storage account.') param tags object = {} -param name string -param sku string + +@description('The name of the storage account. Must be globally unique, 3-24 characters, using only lowercase letters and numbers.') +@minLength(3) +@maxLength(24) +param name string + +@description('The SKU (pricing tier) of the storage account. Allowed values: Standard_LRS, Standard_GRS, Standard_RAGRS, Standard_ZRS, Premium_LRS, Premium_ZRS. Default is Standard_LRS.') +@allowed([ + 'Standard_LRS' + 'Standard_GRS' + 'Standard_RAGRS' + 'Standard_ZRS' + 'Premium_LRS' + 'Premium_ZRS' +]) +param sku string = 'Standard_LRS' + + +@description('List of allowed IP addresses for Storage Account access. Default is empty (no IPs allowed).') +param allowedIpRules array = [] resource stResource 'Microsoft.Storage/storageAccounts@2023-01-01' = { name: name @@ -14,9 +36,18 @@ resource stResource 'Microsoft.Storage/storageAccounts@2023-01-01' = { properties: { allowBlobPublicAccess: false supportsHttpsTrafficOnly: true + minimumTlsVersion: 'TLS1_2' + allowSharedKeyAccess: false + publicNetworkAccess: 'Disabled' + networkAcls: { + defaultAction: 'Deny' + bypass: 'AzureServices' + ipRules: allowedIpRules + virtualNetworkRules: [] + } encryption: { keySource: 'Microsoft.Storage' - requireInfrastructureEncryption: false + requireInfrastructureEncryption: true services: { blob: { enabled: true diff --git a/.azure/modules/st-storageaccount1container.bicep b/.azure/modules/st-storageaccount1container.bicep deleted file mode 100644 index fdc972d..0000000 --- a/.azure/modules/st-storageaccount1container.bicep +++ /dev/null @@ -1,117 +0,0 @@ -@description('Name of the Storage Account. (st)') -@minLength(3) -@maxLength(24) -param name string - -@description('Sku of the Storage Account.') -@allowed([ - 'Premium_LRS' - 'Premium_ZRS' - 'Standard_GRS' - 'Standard_GZRS' - 'Standard_LRS' - 'Standard_RAGRS' - 'Standard_RAGZRS' - 'Standard_ZRS' -]) -param sku string = 'Standard_LRS' - -@description('Kind of the Storage Account.') -@allowed([ - 'BlobStorage' - 'BlockBlobStorage' - 'FileStorage' - 'Storage' - 'StorageV2' -]) -param kind string = 'StorageV2' - -@description('Allow public access') -param allowBlobPublicAccess bool = false - -@description('Name of the Storage Account container 1') -@minLength(3) -@maxLength(24) -param container1Name string - -@description('Name of the Storage Account container 1') -@minLength(3) -@maxLength(24) -@allowed([ - 'Container' - 'None' -]) -param container1Access string = 'Container' - -resource name_resource 'Microsoft.Storage/storageAccounts@2023-05-01' = { - name: name - location: resourceGroup().location - tags: { - displayName: name - } - sku: { - name: sku - } - kind: kind - properties: { - supportsHttpsTrafficOnly: true - allowBlobPublicAccess: allowBlobPublicAccess - } -} - -resource name_default_container1Name 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-05-01' = { - parent: name_default - name: container1Name - properties: { - publicAccess: container1Access - } - dependsOn: [ - name_resource - ] -} - -resource name_default 'Microsoft.Storage/storageAccounts/blobServices@2023-05-01' = { - parent: name_resource - name: 'default' - sku: { - name: sku - } - properties: { - cors: { - corsRules: [] - } - } -} - -resource Microsoft_Storage_storageAccounts_fileServices_name_default 'Microsoft.Storage/storageAccounts/fileServices@2023-05-01' = { - parent: name_resource - name: 'default' - sku: { - name: sku - } - properties: { - cors: { - corsRules: [] - } - } -} - -resource Microsoft_Storage_storageAccounts_queueServices_name_default 'Microsoft.Storage/storageAccounts/queueServices@2023-05-01' = { - parent: name_resource - name: 'default' - properties: { - cors: { - corsRules: [] - } - } -} - -resource Microsoft_Storage_storageAccounts_tableServices_name_default 'Microsoft.Storage/storageAccounts/tableServices@2023-05-01' = { - parent: name_resource - name: 'default' - properties: { - cors: { - corsRules: [] - } - } -} diff --git a/.azure/modules/st-storageaccount2container.bicep b/.azure/modules/st-storageaccount2container.bicep deleted file mode 100644 index 3e4c91c..0000000 --- a/.azure/modules/st-storageaccount2container.bicep +++ /dev/null @@ -1,142 +0,0 @@ -@description('Name of the Storage Account. (st)') -@minLength(3) -@maxLength(24) -param name string - -@description('Sku of the Storage Account.') -@allowed([ - 'Premium_LRS' - 'Premium_ZRS' - 'Standard_GRS' - 'Standard_GZRS' - 'Standard_LRS' - 'Standard_RAGRS' - 'Standard_RAGZRS' - 'Standard_ZRS' -]) -param sku string = 'Standard_LRS' - -@description('Kind of the Storage Account.') -@allowed([ - 'BlobStorage' - 'BlockBlobStorage' - 'FileStorage' - 'Storage' - 'StorageV2' -]) -param kind string = 'StorageV2' - -@description('Allow public access') -param allowBlobPublicAccess bool = false - -@description('Name of the Storage Account container 1') -@minLength(3) -@maxLength(24) -param container1Name string - -@description('Name of the Storage Account container 1') -@minLength(3) -@maxLength(24) -@allowed([ - 'Container' - 'None' -]) -param container1Access string = 'Container' - -@description('Name of the Storage Account container 2') -@minLength(3) -@maxLength(24) -param container2Name string - -@description('Name of the Storage Account container 1') -@minLength(3) -@maxLength(24) -@allowed([ - 'Container' - 'None' -]) -param container2Access string = 'Container' - -resource name_resource 'Microsoft.Storage/storageAccounts@2021-06-01' = { - name: name - location: resourceGroup().location - tags: { - displayName: name - } - sku: { - name: sku - } - kind: kind - properties: { - supportsHttpsTrafficOnly: true - allowBlobPublicAccess: allowBlobPublicAccess - } -} - -resource name_default_container1Name 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-05-01' = { - parent: name_default - name: container1Name - properties: { - publicAccess: container1Access - } - dependsOn: [ - name_resource - ] -} - -resource name_default_container2Name 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-05-01' = { - parent: name_default - name: container2Name - properties: { - publicAccess: container2Access - } - dependsOn: [ - name_resource - ] -} - -resource name_default 'Microsoft.Storage/storageAccounts/blobServices@2023-05-01' = { - parent: name_resource - name: 'default' - sku: { - name: sku - } - properties: { - cors: { - corsRules: [] - } - } -} - -resource Microsoft_Storage_storageAccounts_fileServices_name_default 'Microsoft.Storage/storageAccounts/fileServices@2023-05-01' = { - parent: name_resource - name: 'default' - sku: { - name: sku - } - properties: { - cors: { - corsRules: [] - } - } -} - -resource Microsoft_Storage_storageAccounts_queueServices_name_default 'Microsoft.Storage/storageAccounts/queueServices@2023-05-01' = { - parent: name_resource - name: 'default' - properties: { - cors: { - corsRules: [] - } - } -} - -resource Microsoft_Storage_storageAccounts_tableServices_name_default 'Microsoft.Storage/storageAccounts/tableServices@2023-05-01' = { - parent: name_resource - name: 'default' - properties: { - cors: { - corsRules: [] - } - } -} diff --git a/.azure/modules/st-storageaccountcontainers.bicep b/.azure/modules/st-storageaccountcontainers.bicep deleted file mode 100644 index bb5dbf7..0000000 --- a/.azure/modules/st-storageaccountcontainers.bicep +++ /dev/null @@ -1,102 +0,0 @@ -@description('Name of the Storage Account. (st)') -@minLength(3) -@maxLength(24) -param name string - -@description('Sku of the Storage Account.') -@allowed([ - 'Premium_LRS' - 'Premium_ZRS' - 'Standard_GRS' - 'Standard_GZRS' - 'Standard_LRS' - 'Standard_RAGRS' - 'Standard_RAGZRS' - 'Standard_ZRS' -]) -param sku string = 'Standard_LRS' - -@description('Kind of the Storage Account.') -@allowed([ - 'BlobStorage' - 'BlockBlobStorage' - 'FileStorage' - 'Storage' - 'StorageV2' -]) -param kind string = 'StorageV2' - -@description('Allow public access') -param allowBlobPublicAccess bool = false - -@description('Array of container JSON objects. value: {containers:[name:, publicAccess:Container|None]}') -param containerResources object = { - resources: [ - { - name: 'mycontainer' - publicAccess: 'None' - } - ] -} - -resource name_resource 'Microsoft.Storage/storageAccounts@2023-05-01' = { - name: name - location: resourceGroup().location - tags: { - displayName: name - } - sku: { - name: sku - } - kind: kind - properties: { - supportsHttpsTrafficOnly: true - allowBlobPublicAccess: allowBlobPublicAccess - } -} - -resource name_default 'Microsoft.Storage/storageAccounts/blobServices@2023-05-01' = { - parent: name_resource - name: 'default' - sku: { - name: sku - } - properties: { - cors: { - corsRules: [] - } - } -} - -resource Microsoft_Storage_storageAccounts_fileServices_name_default 'Microsoft.Storage/storageAccounts/fileServices@2023-05-01' = { - parent: name_resource - name: 'default' - sku: { - name: sku - } - properties: { - cors: { - corsRules: [] - } - } -} - -resource Microsoft_Storage_storageAccounts_queueServices_name_default 'Microsoft.Storage/storageAccounts/queueServices@2023-05-01' = { - parent: name_resource - name: 'default' - properties: { - cors: { - corsRules: [] - } - } -} - -resource Microsoft_Storage_storageAccounts_tableServices_name_default 'Microsoft.Storage/storageAccounts/tableServices@2023-05-01' = { - parent: name_resource - name: 'default' - properties: { - cors: { - corsRules: [] - } - } -} diff --git a/.azure/modules/stapp-staticwebapp.bicep b/.azure/modules/stapp-staticwebapp.bicep index 0620df5..57ecfcf 100644 --- a/.azure/modules/stapp-staticwebapp.bicep +++ b/.azure/modules/stapp-staticwebapp.bicep @@ -1,22 +1,27 @@ -//https://www.aaron-powell.com/posts/2022-06-29-deploy-swa-with-bicep/ -//https://stackoverflow.com/questions/77002723/deploy-blazor-wasm-with-net-8-using-github-actions -//https://azure.github.io/static-web-apps-cli/docs/cli/swa-deploy/ -@description('Name of the Static Web App. (stapp)') +@description('The name of the Static Web App. Must be 1-40 characters, using only alphanumeric characters and hyphens.') +@minLength(1) +@maxLength(40) param name string -@description('Azure region of the deployment') +@description('The Azure region where the Static Web App will be deployed.') param location string = resourceGroup().location -@allowed([ 'Free', 'Standard' ]) +@description('The SKU (pricing tier) for the Static Web App. Allowed values: Free, Standard. Default is Free.') +@allowed([ + 'Free' + 'Standard' +]) param sku string = 'Free' -@description('Tags to add to the resources') +@description('Tags to add to the Static Web App resource.') param tags object = {} -@description('Git Repository URL') +@description('The Git repository URL for the Static Web App source code.') +@minLength(1) param repositoryUrl string -@description('Git Branch') +@description('The Git branch to deploy from. Default is main.') +@minLength(1) param branch string = 'main' resource name_resource 'Microsoft.Web/staticSites@2022-09-01' = { diff --git a/.azure/modules/stblobcn-storageblobapiconnection.bicep b/.azure/modules/stblobcn-storageblobapiconnection.bicep index 7a9a014..471c666 100644 --- a/.azure/modules/stblobcn-storageblobapiconnection.bicep +++ b/.azure/modules/stblobcn-storageblobapiconnection.bicep @@ -1,20 +1,26 @@ + +@description('The name of the Blob Storage API Connection. Must be 1-80 characters, using only alphanumeric characters and hyphens.') +@minLength(1) +@maxLength(80) param name string + +@description('The name of the target Storage Account for the connection.') +@minLength(3) +@maxLength(24) param stName string var locationShortName = toLower(replace(resourceGroup().location, ' ', '')) -var nameLower_var = toLower(replace(replace(name, '-', ''), ' ', '')) +var nameLower = toLower(replace(replace(name, '-', ''), ' ', '')) var storageId = resourceId('Microsoft.Storage/storageAccounts', stName) -resource nameLower 'Microsoft.Web/connections@2016-06-01' = { - name: nameLower_var +resource connection 'Microsoft.Web/connections@2016-06-01' = { + name: nameLower location: locationShortName - kind: 'V1' - scale: null - properties: { + properties: { displayName: name customParameterValues: {} api: { - name: '${nameLower_var}azureblob' + name: '${nameLower}azureblob' displayName: 'Azure Blob Storage' description: 'Microsoft Azure Storage provides a massively scalable, durable, and highly available storage for data on the cloud, and serves as the data storage solution for modern applications. Connect to Blob Storage to perform various operations such as create, update, get and delete on blobs in your Azure Storage account.' iconUri: 'https://connectoricons-prod.azureedge.net/releases/v1.0.1507/1.0.1507.2528/azureblob/icon.png' @@ -28,4 +34,4 @@ resource nameLower 'Microsoft.Web/connections@2016-06-01' = { } } dependsOn: [] -} \ No newline at end of file +} diff --git a/.azure/modules/sttablecn-storagetablesapiconnection.bicep b/.azure/modules/sttablecn-storagetablesapiconnection.bicep index 8a861c2..918c133 100644 --- a/.azure/modules/sttablecn-storagetablesapiconnection.bicep +++ b/.azure/modules/sttablecn-storagetablesapiconnection.bicep @@ -1,19 +1,25 @@ + +@description('The name of the Storage Tables API Connection. Must be 1-80 characters, using only alphanumeric characters and hyphens. Default is azureblob.') +@minLength(1) +@maxLength(80) param name string = 'azureblob' + +@description('The name of the target Storage Account for the connection.') +@minLength(3) +@maxLength(24) param stName string var locationShortName = toLower(replace(resourceGroup().location, ' ', '')) -var nameLower_var = toLower(replace(replace(name, '-', ''), ' ', '')) +var nameLower = toLower(replace(replace(name, '-', ''), ' ', '')) -resource nameLower 'Microsoft.Web/connections@2016-06-01' = { - name: nameLower_var +resource connection 'Microsoft.Web/connections@2016-06-01' = { + name: nameLower location: locationShortName - kind: 'V1' - scale: null properties: { displayName: name customParameterValues: {} api: { - name: nameLower_var + name: nameLower displayName: 'Azure Storage Tables' description: 'Microsoft Azure Storage provides a massively scalable, durable, and highly available storage for data on the cloud, and serves as the data storage solution for modern applications. Connect to Blob Storage to perform various operations such as create, update, get and delete on blobs in your Azure Storage account.' iconUri: 'https://connectoricons-prod.azureedge.net/azuretables/icon_1.0.1048.1234.png' diff --git a/.azure/modules/teamscn-teamsapiconnection.bicep b/.azure/modules/teamscn-teamsapiconnection.bicep index ac5a1e3..12fb3cf 100644 --- a/.azure/modules/teamscn-teamsapiconnection.bicep +++ b/.azure/modules/teamscn-teamsapiconnection.bicep @@ -1,20 +1,23 @@ + +@description('The name of the Teams API Connection. Must be 1-80 characters, using only alphanumeric characters and hyphens.') +@minLength(1) +@maxLength(80) param name string var locationShortName = toLower(replace(resourceGroup().location, ' ', '')) -var nameLower_var = toLower(replace(replace(name, '-', ''), ' ', '')) +var nameLower = toLower(replace(replace(name, '-', ''), ' ', '')) -resource nameLower 'Microsoft.Web/connections@2016-06-01' = { - name: nameLower_var +resource connection 'Microsoft.Web/connections@2016-06-01' = { + name: nameLower location: locationShortName - kind: 'V1' properties: { displayName: name customParameterValues: {} api: { - name: nameLower_var + name: nameLower displayName: 'Microsoft Teams' description: 'Microsoft Teams enables you to get all your content, tools and conversations in the Team workspace with Office 365.' - iconUri: 'https://connectoricons-prod.azureedge.net/releases/v1.0.1505/1.0.1505.2520/${nameLower_var}/icon.png' + iconUri: 'https://connectoricons-prod.azureedge.net/releases/v1.0.1505/1.0.1505.2520/${nameLower}/icon.png' brandColor: '#4B53BC' id: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Web/locations/${locationShortName}/managedApis/teams' type: 'Microsoft.Web/locations/managedApis' diff --git a/.azure/modules/vnet-virtualnetwork.bicep b/.azure/modules/vnet-virtualnetwork.bicep new file mode 100644 index 0000000..cbba7cd --- /dev/null +++ b/.azure/modules/vnet-virtualnetwork.bicep @@ -0,0 +1,31 @@ + +@description('The name of the Virtual Network. Must be 2-64 characters, using only alphanumeric characters and hyphens.') +@minLength(2) +@maxLength(64) +param name string + +@description('The address prefix (CIDR block) for the Virtual Network. Example: 10.0.0.0/16') +@minLength(9) +@maxLength(18) +param addressPrefix string + +@description('The Azure region where the Virtual Network will be deployed.') +param location string + +@description('Tags to apply to the Virtual Network resource.') +param tags object = {} + +resource vnetResource 'Microsoft.Network/virtualNetworks@2023-02-01' = { + name: name + location: location + tags: empty(tags) ? null : tags + properties: { + addressSpace: { + addressPrefixes: [ addressPrefix ] + } + enableDdosProtection: false + enableVmProtection: false + } +} + +output id string = vnetResource.id diff --git a/.azure/modules/vnetpeer-virtualnetworkpeering.bicep b/.azure/modules/vnetpeer-virtualnetworkpeering.bicep new file mode 100644 index 0000000..c9f7f9b --- /dev/null +++ b/.azure/modules/vnetpeer-virtualnetworkpeering.bicep @@ -0,0 +1,36 @@ +@description('Name of the local virtual network (parent)') +param localVnetName string + +@description('Peering display name (local -> remote)') +param peeringName string + +@description('Remote VNet full resource ID') +param remoteVnetId string + +@description('Enable gateway transit (only if this is the gateway owner)') +param allowGatewayTransit bool = false + +@description('Use remote gateways (only if the remote allows gateway transit)') +param useRemoteGateways bool = false + +@description('Allow VNet access') +param allowVnetAccess bool = true + +@description('Allow forwarded traffic') +param allowForwardedTraffic bool = false + +resource localVnet 'Microsoft.Network/virtualNetworks@2024-09-01' existing = { + name: localVnetName +} + +resource localToRemote 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2025-03-01' = { + name: peeringName + parent: localVnet + properties: { + remoteVirtualNetwork: { id: remoteVnetId } + allowVirtualNetworkAccess: allowVnetAccess + allowForwardedTraffic: allowForwardedTraffic + allowGatewayTransit: allowGatewayTransit + useRemoteGateways: useRemoteGateways + } +} diff --git a/.azure/modules/wcert-webcertificate.bicep b/.azure/modules/wcert-webcertificate.bicep index a38c345..e7c1ab8 100644 --- a/.azure/modules/wcert-webcertificate.bicep +++ b/.azure/modules/wcert-webcertificate.bicep @@ -1,12 +1,47 @@ + +@description('The name of the Web Certificate resource. Must be 1-80 characters.') +@minLength(1) +@maxLength(80) param name string + +@description('Tags to apply to the Web Certificate resource.') param tags object = {} + +@description('The password for the PFX certificate. Must be at least 8 characters.') +@minLength(8) +@secure() param password string + +@description('The resource ID of the Key Vault containing the certificate secret.') +@minLength(1) param keyVaultId string + +@description('The name of the secret in Key Vault containing the certificate.') +@minLength(1) param keyVaultSecretName string + +@description('The resource ID of the App Service Plan (server farm).') +@minLength(1) param serverFarmId string + +@description('The canonical name (CNAME) for domain validation.') +@minLength(1) param canonicalName string -param domainValidationMethod string + +@description('The domain validation method. Allowed values: email, dns, http. Default is dns.') +@allowed([ + 'email' + 'dns' + 'http' +]) +param domainValidationMethod string = 'dns' + +@description('The list of hostnames for the certificate.') +@minLength(1) param hostnames array + +@description('The PFX certificate blob as a byte array.') +@minLength(1) param pfxBlob array var location = resourceGroup().location @@ -16,9 +51,7 @@ resource name_resource 'Microsoft.Web/certificates@2023-12-01' = { location: location tags: empty(tags) ? null : tags properties: { - hostNames: [ - hostnames - ] + hostNames: hostnames pfxBlob: [ pfxBlob ] diff --git a/.azure/modules/web-appservice.bicep b/.azure/modules/web-appservice.bicep index a5d2485..b77fd7f 100644 --- a/.azure/modules/web-appservice.bicep +++ b/.azure/modules/web-appservice.bicep @@ -1,25 +1,75 @@ -param name string -param location string +@description('The name of the App Service Web App. Must be 1-60 characters, using only alphanumeric characters and hyphens.') +@minLength(1) +@maxLength(60) +param name string + +@description('The Azure region where the Web App will be deployed.') +param location string + +@description('Tags to apply to the Web App resource.') param tags object = {} -@allowed(['Development', 'QA', 'Staging', 'Production']) + +@description('The environment for the Web App. Allowed values: Development, QA, Staging, Production. Default is Development.') +@allowed([ + 'Development' + 'QA' + 'Staging' + 'Production' +]) param environment string = 'Development' + +@description('The Application Insights instrumentation key for the Web App.') +@minLength(1) param appiKey string + +@description('The Application Insights connection string for the Web App.') +@minLength(1) param appiConnection string + +@description('The resource ID of the App Service Plan.') +@minLength(1) param planId string -@allowed(['api', 'app', 'app,linux', 'functionapp', 'functionapp,linux']) + +@description('The kind of the Web App. Allowed values: api, app, app,linux, functionapp, functionapp,linux. Default is app.') +@allowed([ + 'api' + 'app' + 'app,linux' + 'functionapp' + 'functionapp,linux' +]) param kind string = 'app' -@allowed(['v4.8', 'v6.0', 'v7.0', 'v8.0', 'v9.0']) -param dotnetVersion string = 'v8.0' + +@description('The .NET version for the Web App. Allowed values: v4.8 (for .NET Framework), 6.0, 7.0, 8.0, 9.0, 10.0 (for .NET). Default is 10.0.') +@allowed([ + 'v4.8' + '6.0' + '7.0' + '8.0' + '9.0' + '10.0' +]) +param dotnetVersion string = '10.0' + +@description('Enable Always On for the App Service') +param alwaysOn bool = false + +@description('Enable WebSockets for the App Service') +param websockets bool = true resource webAppResource 'Microsoft.Web/sites@2023-12-01' = { name: name location: location - kind: kind - tags: empty(tags) ? null : tags - properties: { + kind: kind + tags: empty(tags) ? null : tags + properties: { serverFarmId: planId + httpsOnly: true siteConfig: { netFrameworkVersion: dotnetVersion + ftpsState: 'Disabled' + webSocketsEnabled: websockets + alwaysOn: alwaysOn appSettings: [ { name: 'APPINSIGHTS_INSTRUMENTATIONKEY' diff --git a/.azure/modules/work-loganalyticsworkspace.bicep b/.azure/modules/work-loganalyticsworkspace.bicep index 53372c2..487b9fb 100644 --- a/.azure/modules/work-loganalyticsworkspace.bicep +++ b/.azure/modules/work-loganalyticsworkspace.bicep @@ -1,6 +1,21 @@ + +@description('The name of the Log Analytics workspace. Must be 4-63 characters, using only letters, numbers, and hyphens.') +@minLength(4) +@maxLength(63) param name string + +@description('The Azure region where the Log Analytics workspace will be deployed.') param location string -param sku string + +@description('The SKU (pricing tier) for the Log Analytics workspace. Allowed values: Free, PerGB2018, CapacityReservation. Default is PerGB2018.') +@allowed([ + 'Free' + 'PerGB2018' + 'CapacityReservation' +]) +param sku string = 'PerGB2018' + +@description('Tags to apply to the Log Analytics workspace resource.') param tags object = {} resource workResource 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { diff --git a/.azure/templates/landingzone-api-sql.bicep b/.azure/templates/landingzone-api-sql.bicep deleted file mode 100644 index 443c399..0000000 --- a/.azure/templates/landingzone-api-sql.bicep +++ /dev/null @@ -1,101 +0,0 @@ -targetScope='resourceGroup' - -// Common -param tenantId string = tenant().tenantId -param location string = resourceGroup().location -param sharedSubscriptionId string = subscription().subscriptionId -param sharedResourceGroupName string -param environmentApp string -param tags object -// Azure Monitor -param appiName string -param Application_Type string -param Flow_Type string -// Key Vault -param kvName string -param kvSku string -// Storage Account -param stName string -param stSku string -// App Service -param planName string -param appName string -// workspace -param workName string -// Sql Server -// Sql Server -param sqlName string -param sqlAdminUser string -@secure() -param sqlAdminPassword string -param sqldbName string -param sqldbSku string - -resource workResource 'Microsoft.OperationalInsights/workspaces@2023-09-01' existing = { - name: workName - scope: resourceGroup(sharedSubscriptionId, sharedResourceGroupName) -} - -module appiModule '../modules/appi-applicationinsights.bicep' = { - name: 'appiModuleName' - params:{ - location: location - tags: tags - name: appiName - Application_Type: Application_Type - Flow_Type: Flow_Type - workResourceId: workResource.id - } -} - -module kvModule '../modules/kv-keyvault.bicep'= { - name:'kvModuleName' - params:{ - location: location - tags: tags - name: kvName - sku: kvSku - tenantId: tenantId - } -} - -module stModule '../modules/st-storageaccount.bicep' = { - name:'stModuleName' - params:{ - tags: tags - location: location - name: stName - sku: stSku - } -} - -resource planResource 'Microsoft.Web/serverfarms@2023-01-01' existing = { - name: planName - scope: resourceGroup(sharedSubscriptionId, sharedResourceGroupName) -} - -module apiModule '../modules/api-appservice.bicep' = { - name: 'apiModuleName' - params:{ - name: appName - location: location - tags: tags - environment: environmentApp - appiKey:appiModule.outputs.InstrumentationKey - appiConnection:appiModule.outputs.Connectionstring - planId: planResource.id - } -} - -module sqlModule '../modules/sql-sqlserverdatabase.bicep' = { - name: 'sqlModuleName' - params:{ - name: sqlName - location: location - tags: tags - adminLogin: sqlAdminUser - adminPassword: sqlAdminPassword - sqldbName: sqldbName - sku: sqldbSku - } -} diff --git a/.azure/templates/landingzone-shared.bicep b/.azure/templates/landingzone-shared.bicep deleted file mode 100644 index 5f2d4ba..0000000 --- a/.azure/templates/landingzone-shared.bicep +++ /dev/null @@ -1,31 +0,0 @@ -targetScope='resourceGroup' - -// Common -param tags object -param location string -// Workspace -param workName string -param workSku string -// App Service Plan -param planName string -param planSku string - -module workModule '../modules/work-loganalyticsworkspace.bicep' = { - name: 'workModuleName' - params: { - name: workName - location: location - tags: tags - sku: workSku - } -} - -module planModule '../modules/plan-appserviceplan.bicep' = { - name: 'planModuleName' - params: { - name: planName - sku: planSku - tags: tags - location: location - } -} diff --git a/.azure/templates/landingzone-web-api-sql.bicep b/.azure/templates/landingzone-web-api-sql.bicep new file mode 100644 index 0000000..8fa20d1 --- /dev/null +++ b/.azure/templates/landingzone-web-api-sql.bicep @@ -0,0 +1,70 @@ +targetScope='resourceGroup' + +// Common +param location string = resourceGroup().location +param spokeMgmtSubscriptionId string = subscription().subscriptionId +param spokeMgmtResourceGroupName string +param environmentApp string +param tags object +// Azure Monitor +param appiName string +// App Service +param planName string +param webName string +param apiName string +// Sql Server +param sqlName string +param sqlAdminUser string +@secure() +param sqlAdminPassword string +param sqldbName string +param sqldbSku string + +resource appiResource 'Microsoft.Insights/components@2020-02-02' existing = { + name: appiName + scope: resourceGroup(spokeMgmtSubscriptionId, spokeMgmtResourceGroupName) +} + +resource planResource 'Microsoft.Web/serverfarms@2023-01-01' existing = { + name: planName + scope: resourceGroup(spokeMgmtSubscriptionId, spokeMgmtResourceGroupName) +} + +module apiModule '../modules/api-appservice.bicep' = { + name: 'apiModuleName' + params:{ + name: apiName + location: location + tags: tags + environment: environmentApp + appiKey:appiResource.properties.InstrumentationKey + appiConnection:appiResource.properties.ConnectionString + planId: planResource.id + } +} + +module webModule '../modules/web-appservice.bicep' = { + name: 'webModuleName' + params:{ + name: webName + location: location + tags: tags + environment: environmentApp + appiKey:appiResource.properties.InstrumentationKey + appiConnection:appiResource.properties.ConnectionString + planId: planResource.id + } +} + +module sqlModule '../modules/sql-sqlserverdatabase.bicep' = { + name: 'sqlModuleName' + params:{ + name: sqlName + location: location + tags: tags + adminLogin: sqlAdminUser + adminPassword: sqlAdminPassword + sqldbName: sqldbName + sku: sqldbSku + } +} diff --git a/.azure/templates/platform-hub-mgmt.bicep b/.azure/templates/platform-hub-mgmt.bicep new file mode 100644 index 0000000..8f02c8c --- /dev/null +++ b/.azure/templates/platform-hub-mgmt.bicep @@ -0,0 +1,47 @@ +targetScope = 'resourceGroup' + +// Common +param tenantId string = tenant().tenantId +param location string = resourceGroup().location +param tags object + +// Management +param sentName string +param sentSku string +param appiName string +param kvName string +param kvSku string + +// +// Management +// +module sentModule '../modules/sent-loganalyticsworkspace.bicep' = { + name: 'sentName' + params: { + name: sentName + location: location + tags: tags + sku: sentSku + } +} + +module appiModule '../modules/appi-applicationinsights.bicep' = { + name: 'appiName' + params:{ + location: location + tags: tags + name: appiName + workResourceId: sentModule.outputs.id + } +} + +module kvModule '../modules/kv-keyvault.bicep' = { + name: 'kvName' + params: { + location: location + tags: tags + name: kvName + sku: kvSku + tenantId: tenantId + } +} diff --git a/.azure/templates/platform-hub-network-publicroute.bicep b/.azure/templates/platform-hub-network-publicroute.bicep new file mode 100644 index 0000000..eb5b0ae --- /dev/null +++ b/.azure/templates/platform-hub-network-publicroute.bicep @@ -0,0 +1,138 @@ +targetScope = 'resourceGroup' + +// Common +param location string = resourceGroup().location +param tags object + +// Networking +param afdSku string = 'Standard_AzureFrontDoor' // Required for public front door +param vnetName string +param vnetCidr string +param snetNameManagement string +param snetCidrManagement string +param snetNameAzureBastion string +param snetCidrAzureBastion string + +// +// Networking +// +// Bastion subnet - typically locked down, only Bastion management ports allowed +var bastionSecurityRules = [ + { + name: 'AllowBastionGateway' + priority: 100 + direction: 'Inbound' + access: 'Allow' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: 'GatewayManager' + destinationAddressPrefix: '*' + } + { + name: 'DenyAllInbound' + priority: 4096 + direction: 'Inbound' + access: 'Deny' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: '*' + destinationAddressPrefix: '*' + } +] + +// Platform Management Services subnet - allow internal platform traffic, block others +var platformManagementSecurityRules = [ + { + name: 'AllowPlatformHTTP' + priority: 100 + direction: 'Inbound' + access: 'Allow' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '80' + sourceAddressPrefix: 'VirtualNetwork' + destinationAddressPrefix: '*' + } + { + name: 'AllowPlatformHTTPS' + priority: 110 + direction: 'Inbound' + access: 'Allow' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: 'VirtualNetwork' + destinationAddressPrefix: '*' + } + { + name: 'DenyAllInbound' + priority: 4096 + direction: 'Inbound' + access: 'Deny' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: '*' + destinationAddressPrefix: '*' + } +] + +module vnet '../modules/vnet-virtualnetwork.bicep' = { + name: 'vnetName' + params: { + name: vnetName + addressPrefix: vnetCidr + location: location + tags: tags + } +} + +module nsgSnet '../modules/nsg-networksecuritygroup.bicep' = { + name: 'nsgNameManagement' + params: { + name: '${snetNameManagement}-nsg' + tags: tags + securityRules: platformManagementSecurityRules + } +} + +module snetHub '../modules/snet-virtualnetworksubnet.bicep' = { + name: 'snetNameManagement' + params: { + vnetName: vnetName + snetName: snetNameManagement + cidr: snetCidrManagement + nsgId: nsgSnet.outputs.id + } +} + +module nsgBastion '../modules/nsg-networksecuritygroup.bicep' = { + name: 'nsgNameAzureBastion' + params: { + name: '${snetNameAzureBastion}-nsg' + tags: tags + securityRules: bastionSecurityRules + } +} + +module snetBastion '../modules/snet-virtualnetworksubnet.bicep' = { + name: 'snetNameAzureBastion' + params: { + vnetName: vnetName + snetName: snetNameAzureBastion + cidr: snetCidrAzureBastion + nsgId: nsgBastion.outputs.id + } +} + +module afd '../modules/afd-azurefrontdoor.bicep' = { + name: 'afdName' + params: { + name: '${vnetName}-afd' + location: 'global' + tags: tags + sku: afdSku + } +} diff --git a/.azure/templates/platform-spoke-mgmt.bicep b/.azure/templates/platform-spoke-mgmt.bicep new file mode 100644 index 0000000..b9add45 --- /dev/null +++ b/.azure/templates/platform-spoke-mgmt.bicep @@ -0,0 +1,88 @@ + +targetScope = 'resourceGroup' + +param tenantId string = tenant().tenantId +param location string = resourceGroup().location +param tags object + +@minLength(1) +@maxLength(64) +@description('Specifies the subscription ID for the hub management. Must not be empty.') +param hubMgmtSubscriptionId string = subscription().subscriptionId + +@minLength(1) +@maxLength(90) +@description('Specifies the resource group name for hub management. Must not be empty. 1-90 characters, letters, numbers, -, _, (, ) and .') +param hubMgmtResourceGroupName string + +@minLength(4) +@maxLength(63) +@description('Specifies the name of the Log Analytics workspace. 4-63 characters, letters, numbers, and -') +param workName string + +@minLength(1) +@maxLength(255) +@description('Specifies the name of the Application Insights resource. 1-255 characters, letters, numbers, and -') +param appiName string + +@minLength(5) +@maxLength(50) +@description('Specifies the name of the App Configuration store. 5-50 characters, only lowercase letters, numbers, and -') +param appcsName string +param appcsSku string = 'free' + +// App Service Plan parameters +param planSku string = 'F1' +param planName string + +// '-appcs-kv' is 9 chars, so truncate base to 15 chars max for 24-char total +var kvNameBase = toLower(replace(appcsName, '-', '')) +var kvNameTrunc = substring(kvNameBase, 0, min(15, length(kvNameBase))) +var kvName = '${kvNameTrunc}-appcs-kv' +var kvSku = 'standard' + +resource workResource 'Microsoft.OperationalInsights/workspaces@2023-09-01' existing = { + name: workName + scope: resourceGroup(hubMgmtSubscriptionId, hubMgmtResourceGroupName) +} + +module appiModule '../modules/appi-applicationinsights.bicep' = { + name: 'appiName' + params:{ + location: location + tags: tags + name: appiName + workResourceId: workResource.id + } +} + +module kvModule '../modules/kv-keyvault.bicep' = { + name: 'kvName' + params: { + location: location + tags: tags + name: kvName + sku: kvSku + tenantId: tenantId + } +} + +module appcsModule '../modules/appcs-appconfigurationstore.bicep' = { + name: 'appcsName' + params: { + name: appcsName + sku: appcsSku + location: location + } +} + +module planModule '../modules/plan-appserviceplan.bicep' = { + name: 'planModule' + params: { + name: planName + sku: planSku + location: location + } +} + + diff --git a/.azure/templates/platform-spoke-network-publicroute.bicep b/.azure/templates/platform-spoke-network-publicroute.bicep new file mode 100644 index 0000000..917d7a6 --- /dev/null +++ b/.azure/templates/platform-spoke-network-publicroute.bicep @@ -0,0 +1,73 @@ +targetScope = 'resourceGroup' + +param location string = resourceGroup().location +param tags object +param vnetName string +param vnetCidr string +param snetNameManagement string +param snetCidrManagement string + +var platformManagementSecurityRules = [ + { + name: 'AllowPlatformHTTP' + priority: 100 + direction: 'Inbound' + access: 'Allow' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '80' + sourceAddressPrefix: 'VirtualNetwork' + destinationAddressPrefix: '*' + } + { + name: 'AllowPlatformHTTPS' + priority: 110 + direction: 'Inbound' + access: 'Allow' + protocol: 'Tcp' + sourcePortRange: '*' + destinationPortRange: '443' + sourceAddressPrefix: 'VirtualNetwork' + destinationAddressPrefix: '*' + } + { + name: 'DenyAllInbound' + priority: 4096 + direction: 'Inbound' + access: 'Deny' + protocol: '*' + sourcePortRange: '*' + destinationPortRange: '*' + sourceAddressPrefix: '*' + destinationAddressPrefix: '*' + } +] + +module vnet '../modules/vnet-virtualnetwork.bicep' = { + name: 'vnetName' + params: { + name: vnetName + addressPrefix: vnetCidr + location: location + tags: tags + } +} + +module nsgSnet '../modules/nsg-networksecuritygroup.bicep' = { + name: 'nsgNameManagement' + params: { + name: '${snetNameManagement}-nsg' + tags: tags + securityRules: platformManagementSecurityRules + } +} + +module snetHub '../modules/snet-virtualnetworksubnet.bicep' = { + name: 'snetNameManagement' + params: { + vnetName: vnetName + snetName: snetNameManagement + cidr: snetCidrManagement + nsgId: nsgSnet.outputs.id + } +} diff --git a/.azure/variables/landingzone-api-sql-development.bicepparam b/.azure/variables/landingzone-api-sql-development.bicepparam deleted file mode 100644 index b376203..0000000 --- a/.azure/variables/landingzone-api-sql-development.bicepparam +++ /dev/null @@ -1,37 +0,0 @@ -using '../templates/landingzone-api-sql.bicep' -// Common -var organizationName = 'gtc' -var productName = 'semkernel' -var subscriptionName = 'devtest' -param environmentApp = 'Development' -var environmentIac = 'dev' -param location = 'West US 2' -param tags = { Environment: environmentIac, CostCenter: '0000' } -// Workspace -param sharedResourceGroupName = '${organizationName}-rg-${subscriptionName}-shared-${environmentIac}-001' -param workName = 'work-shared-${environmentIac}-001' - -// Azure Monitor -param appiName = 'appi-${productName}-${environmentIac}-001' -param Flow_Type = 'Bluefield' -param Application_Type = 'web' - -// Storage -param stName = 'st${productName}${environmentIac}001' -param stSku = 'Standard_LRS' - -// Key Vault -param kvName = 'kv-${productName}-${environmentIac}-002' -param kvSku = 'standard' - -// App Service -var planSku = 'F1' -param appName = 'api-${productName}-${environmentIac}-001' -param planName = 'plan-shared-${planSku}-${environmentIac}-001' - -// SQL Server -param sqlName = 'sql-${productName}-${environmentIac}-001' -param sqlAdminUser = '' -param sqlAdminPassword = '' -param sqldbName = 'sqldb-${productName}-${environmentIac}-001' -param sqldbSku = 'Basic' diff --git a/.azure/variables/landingzone-api-sql-production.bicepparam b/.azure/variables/landingzone-api-sql-production.bicepparam deleted file mode 100644 index 0167367..0000000 --- a/.azure/variables/landingzone-api-sql-production.bicepparam +++ /dev/null @@ -1,37 +0,0 @@ -using '../templates/landingzone-api-sql.bicep' -// Common -var organizationName = 'gtc' -var productName = 'semkernel' -var subscriptionName = 'production' -param environmentApp = 'Production' -var environmentIac = 'prod' -param location = 'West US 2' -param tags = { Environment: environmentIac, CostCenter: '0000' } -// Workspace -param sharedResourceGroupName = '${organizationName}-rg-${subscriptionName}-shared-${environmentIac}-001' -param workName = 'work-shared-${environmentIac}-001' - -// Azure Monitor -param appiName = 'appi-${productName}-${environmentIac}-001' -param Flow_Type = 'Bluefield' -param Application_Type = 'web' - -// Storage -param stName = 'st${productName}${environmentIac}001' -param stSku = 'Standard_LRS' - -// Key Vault -param kvName = 'kv-${productName}-${environmentIac}-002' -param kvSku = 'standard' - -// App Service -var planSku = 'F1' -param appName = 'api-${productName}-${environmentIac}-001' -param planName = 'plan-shared-${planSku}-${environmentIac}-001' - -// SQL Server -param sqlName = 'sql-${productName}-${environmentIac}-001' -param sqlAdminUser = '' -param sqlAdminPassword = '' -param sqldbName = 'sqldb-${productName}-${environmentIac}-001' -param sqldbSku = 'Basic' diff --git a/.azure/variables/landingzone-shared-development.bicepparam b/.azure/variables/landingzone-shared-development.bicepparam deleted file mode 100644 index 79dc28b..0000000 --- a/.azure/variables/landingzone-shared-development.bicepparam +++ /dev/null @@ -1,14 +0,0 @@ -using '../templates/landingzone-shared.bicep' - -// Common -var environmentIac = 'dev' -param location = 'West US 2' -param tags = { Environment: environmentIac, CostCenter: '0000' } - -// Workspace -param workName = 'work-shared-${environmentIac}-001' -param workSku = 'PerGB2018' - -// App Service -param planSku = 'F1' -param planName = 'plan-shared-${planSku}-${environmentIac}-001' diff --git a/.azure/variables/landingzone-shared-production.bicepparam b/.azure/variables/landingzone-shared-production.bicepparam deleted file mode 100644 index ef120f4..0000000 --- a/.azure/variables/landingzone-shared-production.bicepparam +++ /dev/null @@ -1,14 +0,0 @@ -using '../templates/landingzone-shared.bicep' - -// Common -var environmentIac = 'prod' -param location = 'West US 2' -param tags = { Environment: environmentIac, CostCenter: '0000' } - -// Workspace -param workName = 'work-shared-${environmentIac}-001' -param workSku = 'PerGB2018' - -// App Service -param planSku = 'F1' -param planName = 'plan-shared-${planSku}-${environmentIac}-001' diff --git a/.azure/variables/landingzone-web-api-sql-dev.bicepparam b/.azure/variables/landingzone-web-api-sql-dev.bicepparam new file mode 100644 index 0000000..1597a6d --- /dev/null +++ b/.azure/variables/landingzone-web-api-sql-dev.bicepparam @@ -0,0 +1,29 @@ +using '../templates/landingzone-web-api-sql.bicep' + +// Common +var tenantIac = 'can' +var productIac = 'semker' +var environmentIac = 'dev' +var regionIac = 'wus2' +var instanceIac = '001' +var planSku = 'F1' + +param environmentApp = 'Development' +param location = 'westus2' +param tags = { Environment: environmentIac, CostCenter: '0000' } + +// Mgmt Resource Group (spoke) +param spokeMgmtResourceGroupName = '${tenantIac}-spokemgmt-${environmentIac}-${regionIac}-${instanceIac}-rg' +param appiName = 'spokemgmt-${environmentIac}-${regionIac}-${instanceIac}-appi' + +// App Service +param webName = '${productIac}-${environmentIac}-${regionIac}-${instanceIac}-web' +param apiName = '${productIac}-${environmentIac}-${regionIac}-${instanceIac}-api' +param planName = 'spokemgmt-${environmentIac}-${regionIac}-${planSku}-${instanceIac}-plan' + +// SQL Server +param sqlName = '${productIac}-${environmentIac}-${regionIac}-${instanceIac}-sql' +param sqlAdminUser = '' +param sqlAdminPassword = '' +param sqldbName = '${productIac}-${environmentIac}-${regionIac}-${instanceIac}-sqldb' +param sqldbSku = 'Basic' diff --git a/.azure/variables/platform-hub-mgmt-plat.bicepparam b/.azure/variables/platform-hub-mgmt-plat.bicepparam new file mode 100644 index 0000000..b05473f --- /dev/null +++ b/.azure/variables/platform-hub-mgmt-plat.bicepparam @@ -0,0 +1,27 @@ +using '../templates/platform-hub-mgmt.bicep' + +// ===================== +// Common +// ===================== +var tenantIac = 'can' +var productIac = 'hubmgmt' +var environmentIac = 'plat' +var regionIac = 'wus2' +var instanceIac = '001' +param location = 'westus2' +param tags = { + Environment: environmentIac + CostCenter: '0000' + project: productIac + owner: tenantIac +} + +// ===================== +// Management RG: ${tenantIac}-${productIac}-${environmentIac}-${regionIac}-${instanceIac}-rg +// rg: gtc-hubmgmt-plat-wus2-001 +// ===================== +param sentName = '${productIac}-${environmentIac}-${regionIac}-${instanceIac}-sent' +param sentSku = 'PerGB2018' +param appiName = '${productIac}-${environmentIac}-${regionIac}-${instanceIac}-appi' +param kvName = '${productIac}-${environmentIac}-${instanceIac}-kv' +param kvSku = 'standard' diff --git a/.azure/variables/platform-hub-network-publicroute-plat.bicepparam b/.azure/variables/platform-hub-network-publicroute-plat.bicepparam new file mode 100644 index 0000000..7306a49 --- /dev/null +++ b/.azure/variables/platform-hub-network-publicroute-plat.bicepparam @@ -0,0 +1,29 @@ +using '../templates/platform-hub-network-publicroute.bicep' + +// ===================== +// Common +// ===================== +var tenantIac = 'can' +var productIac = 'hubnetwork' +var environmentIac = 'plat' +var regionIac = 'wus2' +var instanceIac = '001' +param location = 'westus2' +param tags = { + Environment: environmentIac + CostCenter: '0000' + project: productIac + owner: tenantIac +} + +// ===================== +// Networking RG: ${tenantIac}-${productIac}-${environmentIac}-${regionIac}-${instanceIac}-rg +// rg: gtc-hubnetwork-plat-wus2-001 +// ===================== +param afdSku = 'Standard_AzureFrontDoor' +param vnetName = '${productIac}-${environmentIac}-${regionIac}-${instanceIac}-vnet' +param vnetCidr = '10.10.0.0/16' +param snetNameManagement = '${productIac}-hub-management-${instanceIac}-snet' +param snetCidrManagement = '10.10.1.0/24' +param snetNameAzureBastion = '${productIac}-azure-bastion-${instanceIac}-snet' +param snetCidrAzureBastion = '10.10.2.0/24' diff --git a/.azure/variables/platform-spoke-mgmt-dev.bicepparam b/.azure/variables/platform-spoke-mgmt-dev.bicepparam new file mode 100644 index 0000000..90ca967 --- /dev/null +++ b/.azure/variables/platform-spoke-mgmt-dev.bicepparam @@ -0,0 +1,36 @@ +using '../templates/platform-spoke-mgmt.bicep' + +// ===================== +// Common +// ===================== +var tenantIac = 'can' +var productIac = 'spokemgmt' +var environmentIac = 'dev' +var regionIac = 'wus2' +var instanceIac = '001' +param location = 'westus2' +param tags = { + Environment: environmentIac + CostCenter: '0000' + project: productIac + owner: tenantIac +} + +// ===================== +// Platform Hub Management RG: ${tenantIac}-${productIac}-${environmentIac}-${regionIac}-${instanceIac}-rg +// i.e. gtc-hubmgmt-plat-wus2-001 +// Note: Sentinel is the shared workspace log analytics +// ===================== +param hubMgmtSubscriptionId = '00000000-0000-0000-0000-000000000000' +param hubMgmtResourceGroupName = 'hubmgmt-plat-${regionIac}-${instanceIac}-rg' +param workName = 'hubmgmt-plat-${regionIac}-${instanceIac}-sent' + +// ===================== +// Platform Spoke Management RG: ${tenantIac}-${productIac}-${environmentIac}-${regionIac}-${instanceIac}-rg +// i.e. gtc-spokemgmt-dev-wus2-001 +// ===================== +param appiName = '${productIac}-${environmentIac}-${regionIac}-${instanceIac}-appi' +param appcsName = '${productIac}-${environmentIac}-${regionIac}-${instanceIac}-appcs' +param appcsSku = 'free' +param planSku = 'F1' +param planName = '${productIac}-${environmentIac}-${regionIac}-${planSku}-${instanceIac}-plan' diff --git a/.azure/variables/platform-spoke-network-publicroute-dev.bicepparam b/.azure/variables/platform-spoke-network-publicroute-dev.bicepparam new file mode 100644 index 0000000..85c6ae8 --- /dev/null +++ b/.azure/variables/platform-spoke-network-publicroute-dev.bicepparam @@ -0,0 +1,25 @@ +using '../templates/platform-spoke-network-publicroute.bicep' + +// ===================== +// Common +// ===================== +var tenantIac = 'can' +var productIac = 'spokenetwork' +var environmentIac = 'dev' +var regionIac = 'wus2' +var instanceIac = '001' +param location = 'westus2' +param tags = { + Environment: environmentIac + CostCenter: '0000' + project: productIac + owner: tenantIac +} + +// ===================== +// Networking RG +// ===================== +param vnetName = '${productIac}-${environmentIac}-${regionIac}-${instanceIac}-vnet' +param vnetCidr = '10.10.0.0/16' +param snetNameManagement = '${productIac}-hub-management-${instanceIac}-snet' +param snetCidrManagement = '10.10.1.0/24' diff --git a/.azuredevops/pipelines/COMPANY-PRODUCT-api-cd-appservice.yml b/.azuredevops/pipelines/COMPANY-PRODUCT-api-cd-appservice.yml deleted file mode 100644 index 30b0595..0000000 --- a/.azuredevops/pipelines/COMPANY-PRODUCT-api-cd-appservice.yml +++ /dev/null @@ -1,71 +0,0 @@ -trigger: -- main - -pool: - vmImage: 'windows-latest' - -parameters: - - name: subscriptionService - type: string - default: 'AZURE-DEVOPS-SERVICE-CONNECTION-NAME' - -variables: - - template: ../variables/common.yml - -stages: - - stage: Development - variables: - - template: ../variables/development.yml - jobs: - - job: Deploy_src - steps: - - task: DownloadPipelineArtifact@2 - inputs: - buildType: 'specific' - project: '65294700-f5dd-4170-8192-4b8b63d2132c' - definition: '24' - buildVersionToDownload: 'latest' - targetPath: '$(Pipeline.Workspace)' - artifactName: 'drop' - - task: AzureWebApp@1 - displayName: Deploy to webapp - inputs: - azureSubscription: '${{ parameters.subscriptionService }}' - appType: 'webApp' - appName: '$(appName)' - package: '$(Pipeline.Workspace)/**/*.zip' - deploymentMethod: 'zipDeploy' - - - stage: Production - variables: - - template: ../variables/production.yml - jobs: - - job: deploy_approval - pool: server - timeoutInMinutes: 4320 - steps: - - task: ManualValidation@0 - inputs: - notifyUsers: $(deployApprovers) - instructions: "Team: $(System.TeamProject)\r\nRepo: $(Build.Repository.Name)\r\nRequested by: $(Build.RequestedFor)\r\nBuild: $(Build.BuildNumber)\r\nUrl: $(Build.BuildUri)\r\nA new deployment has been requested. Please approve or reject the deployment." - onTimeout: reject - - job: Deploy_src - dependsOn: deploy_approval - steps: - - task: DownloadPipelineArtifact@2 - inputs: - buildType: 'specific' - project: '65294700-f5dd-4170-8192-4b8b63d2132c' - definition: '24' - buildVersionToDownload: 'latest' - targetPath: '$(Pipeline.Workspace)' - artifactName: 'drop' - - task: AzureWebApp@1 - displayName: Deploy to webapp - inputs: - azureSubscription: '${{ parameters.subscriptionService }}' - appType: 'webApp' - appName: '$(appName)' - package: '$(Pipeline.Workspace)/**/*.zip' - deploymentMethod: 'zipDeploy' - diff --git a/.azuredevops/pipelines/COMPANY-PRODUCT-api-ci-cd-AppService.yml b/.azuredevops/pipelines/COMPANY-PRODUCT-api-ci-cd-AppService.yml deleted file mode 100644 index 028cc63..0000000 --- a/.azuredevops/pipelines/COMPANY-PRODUCT-api-ci-cd-AppService.yml +++ /dev/null @@ -1,200 +0,0 @@ -trigger: - batch: true - branches: - include: - - 'main' - paths: - include: - - src/Core/** - - src/Infrastructure/** - - src/Specs/** - - src/Presentation/Api/** - -pr: - branches: - exclude: - - '*' - -pool: - vmImage: 'ubuntu-latest' - -parameters: - - name: appcsEnvironmentVariable - type: string - default: "AZURE_APPCS_CONNECTION_STRING_ENV_VAR" - - name: appcsConnection - type: string - default: "$(appcsConnection)" # from secrets (library) - - name: subscriptionService - type: string - default: "AZURE_DEVOPS_SERVICE_CONNECTION_NAME" - - name: apiSettings - type: object - default: - - name: 'APPINSIGHTS_INSTRUMENTATIONKEY' - value: '$(appiKey)' - slotSetting: false - - name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' - value: '$(appiConnection)' - slotSetting: false - - name: 'ASPNETCORE_ENVIRONMENT' - value: '$(environmentApp)' - slotSetting: false - - name: '$(appcsEnvironmentVariable)' - value: '$(appcsConnection)' - slotSetting: false - -variables: - - template: ../variables/common.yml - -stages: - - stage: build - jobs: - - job: build_api - steps: - - template: ../steps/dotnet-build-steps.yml - parameters: - dotnetVersion: '$(dotnetVersion)' - srcPath: '$(srcPath)' - srcProject: '$(apiProject)' - - - job: test_api - dependsOn: build_api - steps: - - template: ../steps/dotnet-test-steps.yml - parameters: - dotnetVersion: '$(dotnetVersion)' - testPath: '$(unitPath)' - testProject: '$(unitProject)' - restorePath: '$(srcPath)' - appcsEnvironmentVariable: '$(appcsEnvironmentVariable)' - appcsConnection: '$(appcsConnection)' - environmentApp: '$(environmentApp)' - - - job: package_api - dependsOn: test_api - steps: - - template: ../steps/dotnet-publish-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - artifactName: '$(artifactName)' - dotnetVersion: '$(dotnetVersion)' - restorePath: '$(srcPath)' - scriptsPath: '$(scriptsPath)' - srcPath: '$(apiPath)' - srcProject: '$(apiProject)' - - - stage: development - condition: eq(variables['Build.SourceBranch'], 'refs/heads/develop') - variables: - - template: ../variables/development.yml - - jobs: - - job: build_src - steps: - - template: ../steps/dotnet-build-steps.yml - parameters: - srcPath: '$(srcPath)' - - - job: test_unit - dependsOn: build_src - steps: - - template: ../steps/dotnet-test-steps.yml - parameters: - testPath: '$(unitPath)' - testProject: '$(unitProject)' - restorePath: '$(srcPath)' - appcsEnvironmentVariable: '$(appcsEnvironmentVariable)' - appcsConnection: '$(appcsConnection)' - environmentApp: '$(environmentApp)' - - - job: package_api - dependsOn: test_unit - steps: - - template: ../steps/dotnet-publish-steps.yml - parameters: - artifactsPath: '$(artifactspath)' - restorePath: '$(srcpath)' - scriptsPath: '$(scriptspath)' - srcPath: '$(apipath)' - srcProject: '$(apiProject)' - - - job: settings_api - dependsOn: package_api - steps: - - template: ../steps/api-settings-steps.yml - parameters: - subscriptionService: '${{ parameters.subscriptionService }}' - environmentApp: '$(environmentApp)' - apiName: '$(apiName)' - appSettings: '${{ parameters.apiSettings }}' - - - job: deploy_api - dependsOn: settings_api - steps: - - template: ../steps/api-deploy-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - archiveFile: '$(archiveFile)' - apiName: '$(apiName)' - subscriptionId: '$(subscriptionId)' - subscriptionService: '${{ parameters.subscriptionService }}' - - - stage: production - condition: eq(variables['Build.SourceBranch'], 'refs/heads/main') - variables: - - template: ../variables/production.yml - - jobs: - - job: build_src - steps: - - template: ../steps/dotnet-build-steps.yml - parameters: - dotnetVersion: '$(dotnetVersion)' - srcPath: '$(srcPath)' - - - job: test_unit - dependsOn: build_src - steps: - - template: ../steps/dotnet-test-steps.yml - parameters: - dotnetVersion: '$(dotnetVersion)' - testPath: '$(unitPath)' - testProject: '$(unitProject)' - restorePath: '$(srcPath)' - appcsEnvironmentVariable: '$(appcsEnvironmentVariable)' - appcsConnection: '$(appcsConnection)' - environmentApp: '$(environmentApp)' - - - job: package_api - dependsOn: test_unit - steps: - - template: ../steps/dotnet-publish-steps.yml - parameters: - artifactsPath: '$(artifactspath)' - dotnetVersion: '$(dotnetVersion)' - restorePath: '$(srcpath)' - scriptsPath: '$(scriptspath)' - srcPath: '$(apipath)' - srcProject: '$(apiProject)' - - - job: settings_api - dependsOn: package_api - steps: - - template: ../steps/api-settings-steps.yml - parameters: - subscriptionService: '${{ parameters.subscriptionService }}' - environmentApp: '$(environmentApp)' - apiName: '$(apiName)' - appSettings: '${{ parameters.apiSettings }}' - - - job: deploy_api - dependsOn: settings_api - steps: - - template: ../steps/api-deploy-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - archiveFile: '$(archiveFile)' - apiName: '$(apiName)' - subscriptionId: '$(subscriptionId)' - subscriptionService: '${{ parameters.subscriptionService }}' \ No newline at end of file diff --git a/.azuredevops/pipelines/COMPANY-PRODUCT-api-ci-cd-IIS.yml b/.azuredevops/pipelines/COMPANY-PRODUCT-api-ci-cd-IIS.yml deleted file mode 100644 index 9da8825..0000000 --- a/.azuredevops/pipelines/COMPANY-PRODUCT-api-ci-cd-IIS.yml +++ /dev/null @@ -1,296 +0,0 @@ -trigger: - batch: false - branches: - include: - - 'main' - paths: - include: - - .azure-devops/pipelines/aacn-eventeval-api-ci-cd-IIS.yml - - src/Core/** - - src/Infrastructure/** - - src/Presentation/WebApi/** - - tests/** - -pr: - branches: - include: - - 'main' - -pool: - vmImage: 'ubuntu-latest' - -parameters: - - name: CreateIis - type: boolean - default: false - - name: ForceDeployDev - type: boolean - default: false - - name: ForceDeployProd - type: boolean - default: false - -variables: - - name: ConnectionStrings.Default - value: '$(sqlConnection)' - -stages: - - stage: build - variables: - - template: ../variables/common-ci.yml - jobs: - - job: build_src - steps: - - task: PowerShell@2 - displayName: 'Set-Version.ps1' - inputs: - filePath: '$(scriptsPath)/Set-Version.ps1' - arguments: '-Path $(srcPath) -VersionToReplace 1.0.0 -Major 1 -Minor 0 -Revision 0 -CommitHash $(Build.SourceVersion) -Build $(Build.BuildNumber)' - workingDirectory: '$(scriptsPath)' - - - task: UseDotNet@2 - displayName: UseSDKVersion - inputs: - packageType: 'sdk' - version: $(dotnetVersion) - includePreviewVersions: true - - - task: DotNetCoreCLI@2 - displayName: dotnetrestore - inputs: - command: 'restore' - projects: "$(srcSolution)" - feedsToUse: 'config' - nugetConfigPath: "$(nugetConfig)" - - - task: DotNetCoreCLI@2 - displayName: dotnet build - inputs: - command: 'build' - projects: "$(srcProject)" - arguments: "--configuration $(buildConfiguration)" - - - job: test_src - dependsOn: build_src - steps: - - task: DotNetCoreCLI@2 - displayName: dotnet test - inputs: - command: 'test' - projects: "$(testProject)" - arguments: "--configuration $(buildConfiguration)" - publishTestResults: true - - - job: publish_src - dependsOn: test_src - steps: - - task: DotNetCoreCLI@2 - displayName: dotnet publish - inputs: - command: 'publish' - publishWebProjects: false - projects: "$(srcProject)" - arguments: "--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)" - zipAfterPublish: true - - - task: PublishBuildArtifacts@1 - displayName: Publishbuildartifacts - inputs: - PathtoPublish: "$(Build.ArtifactStagingDirectory)" - ArtifactName: 'drop' - publishLocation: "Container" - - - stage: deploy_dev - dependsOn: build - condition: and(succeeded(), or(eq(lower(${{ parameters.ForceDeployDev }}), 'true'), and(eq(variables['Build.SourceBranch'], 'refs/heads/main'), ne(variables['Build.Reason'], 'PullRequest')))) - variables: - - template: ../variables/development-cd.yml - - jobs: - - deployment: DeployAPI - environment: - name: Development - resourceName: ILSCWEB01-HWE - strategy: - runOnce: - deploy: - steps: - - task: CmdLine@2 - displayName: Remove HTTPS Bindings - condition: ${{ parameters.CreateIis }} - inputs: - script: | - netsh http delete sslcert ipport=0.0.0.0:443 - setx ASPNETCORE_ENVIRONMENT $(environmentApp) /M - - - task: IISWebAppManagementOnMachineGroup@0 - displayName: IIS Web App Create - $(siteDNS) - condition: ${{ parameters.CreateIis }} - inputs: - EnableIIS: true - IISDeploymentType: 'IISWebsite' - ActionIISWebsite: 'CreateOrUpdateWebsite' - WebsiteName: '$(siteDNS)' - WebsitePhysicalPath: '%SystemDrive%\sites\$(siteDNS)' - WebsitePhysicalPathAuth: 'WebsiteUserPassThrough' - AddBinding: true - Bindings: '{"bindings":[{"protocol":"https","ipAddress":"All Unassigned","port":"443","hostname":"$(siteDNS)","sslThumbprint":"$(thumbprint)","sniFlag":false}, {"protocol":"http","ipAddress":"All Unassigned","port":"80","hostname":"$(siteDNS)","sslThumbprint":"","sniFlag":false}]}' - CreateOrUpdateAppPoolForWebsite: true - ConfigureAuthenticationForWebsite: true - AnonymousAuthenticationForWebsite: true - WindowsAuthenticationForWebsite: false - AppPoolNameForWebsite: '$(siteDNS)' - DotNetVersionForWebsite: 'v4.0' - PipeLineModeForWebsite: 'Integrated' - AppPoolIdentityForWebsite: 'SpecificUser' - AppPoolUsernameForWebsite: '$(sqlUser)' - AppPoolPasswordForWebsite: '$(sqlPassword)' - - - task: IISWebAppManagementOnMachineGroup@0 - condition: ${{ parameters.CreateIis }} - continueOnError: true - inputs: - IISDeploymentType: 'IISApplicationPool' - ActionIISApplicationPool: 'StopAppPool' - StartStopRecycleAppPoolName: '$(siteDNS)-$(productName)' - - - task: IISWebAppManagementOnMachineGroup@0 - displayName: IIS App Pool - $(siteDNS)\$(productName) - condition: ${{ parameters.CreateIis }} - inputs: - IISDeploymentType: 'IISWebApplication' - ParentWebsiteNameForApplication: '$(siteDNS)' - VirtualPathForApplication: '/$(productName)' - PhysicalPathForApplication: '%SystemDrive%\sites\$(siteDNS)\$(productName)' - CreateOrUpdateAppPoolForApplication: true - AppPoolNameForApplication: '$(siteDNS)-$(productName)' - DotNetVersionForApplication: 'v4.0' - PipeLineModeForApplication: 'Integrated' - AppPoolIdentityForApplication: 'SpecificUser' - AppPoolUsernameForApplication: '$(sqlUser)' - AppPoolPasswordForApplication: '$(sqlPassword)' - - - task: IISWebAppManagementOnMachineGroup@0 - condition: ${{ parameters.CreateIis }} - inputs: - IISDeploymentType: 'IISApplicationPool' - ActionIISApplicationPool: 'StartAppPool' - StartStopRecycleAppPoolName: '$(siteDNS)-$(productName)' - - - task: FileTransform@1 - displayName: 'Variable transformation' - inputs: - folderPath: '$(Pipeline.Workspace)/**/*.zip' - fileType: 'json' - targetFiles: 'appsettings.$(environmentApp).json' - - - task: IISWebAppDeploymentOnMachineGroup@0 - displayName: Deploy IIS Website/App - $(siteDNS)\$(productName) - inputs: - WebSiteName: '$(siteDNS)' - VirtualApplication: '$(productName)' - Package: '$(Pipeline.Workspace)/**/*.zip' - TakeAppOfflineFlag: true - - - stage: deploy_prod - dependsOn: deploy_dev - condition: or(eq(lower(${{ parameters.ForceDeployProd }}), 'true'), and(succeeded(), and(eq(variables['Build.SourceBranch'], 'refs/heads/main'), ne(variables['Build.Reason'], 'PullRequest')))) - variables: - - template: ../variables/production-cd.yml - - jobs: - - job: deploy_approval - pool: server - timeoutInMinutes: 4320 - steps: - - task: ManualValidation@0 - inputs: - notifyUsers: $(deployApprovers) - instructions: "Team: $(System.TeamProject)\r\nRepo: $(Build.Repository.Name)\r\nRequested by: $(Build.RequestedFor)\r\nBuild: $(Build.BuildNumber)\r\nUrl: $(Build.BuildUri)\r\nA new deployment has been requested. Please approve or reject the deployment." - onTimeout: reject - - - deployment: DeployAPI - dependsOn: deploy_approval - environment: - name: Production - resourceName: ILSCWEB01-HWE - strategy: - runOnce: - deploy: - steps: - - task: CmdLine@2 - displayName: Remove HTTPS Bindings - condition: ${{ parameters.CreateIis }} - inputs: - script: | - netsh http delete sslcert ipport=0.0.0.0:443 - setx ASPNETCORE_ENVIRONMENT $(environmentApp) /M - - - task: IISWebAppManagementOnMachineGroup@0 - displayName: IIS Web App Create - $(siteDNS) - condition: ${{ parameters.CreateIis }} - inputs: - EnableIIS: true - IISDeploymentType: 'IISWebsite' - ActionIISWebsite: 'CreateOrUpdateWebsite' - WebsiteName: '$(siteDNS)' - WebsitePhysicalPath: '%SystemDrive%\sites\$(siteDNS)' - WebsitePhysicalPathAuth: 'WebsiteUserPassThrough' - AddBinding: true - Bindings: '{"bindings":[{"protocol":"https","ipAddress":"All Unassigned","port":"443","hostname":"$(siteDNS)","sslThumbprint":"$(thumbprint)","sniFlag":false}, {"protocol":"http","ipAddress":"All Unassigned","port":"80","hostname":"$(siteDNS)","sslThumbprint":"","sniFlag":false}]}' - CreateOrUpdateAppPoolForWebsite: true - ConfigureAuthenticationForWebsite: true - AnonymousAuthenticationForWebsite: true - WindowsAuthenticationForWebsite: false - AppPoolNameForWebsite: '$(siteDNS)' - DotNetVersionForWebsite: 'v4.0' - PipeLineModeForWebsite: 'Integrated' - AppPoolIdentityForWebsite: 'SpecificUser' - AppPoolUsernameForWebsite: '$(sqlUser)' - AppPoolPasswordForWebsite: '$(sqlPassword)' - - - task: IISWebAppManagementOnMachineGroup@0 - condition: ${{ parameters.CreateIis }} - continueOnError: true - inputs: - IISDeploymentType: 'IISApplicationPool' - ActionIISApplicationPool: 'StopAppPool' - StartStopRecycleAppPoolName: '$(siteDNS)-$(productName)' - - - task: IISWebAppManagementOnMachineGroup@0 - displayName: IIS App Pool - $(siteDNS)\$(productName) - condition: ${{ parameters.CreateIis }} - inputs: - IISDeploymentType: 'IISWebApplication' - ParentWebsiteNameForApplication: '$(siteDNS)' - VirtualPathForApplication: '/$(productName)' - PhysicalPathForApplication: '%SystemDrive%\sites\$(siteDNS)\$(productName)' - CreateOrUpdateAppPoolForApplication: true - AppPoolNameForApplication: '$(siteDNS)-$(productName)' - DotNetVersionForApplication: 'v4.0' - PipeLineModeForApplication: 'Integrated' - AppPoolIdentityForApplication: 'SpecificUser' - AppPoolUsernameForApplication: '$(sqlUser)' - AppPoolPasswordForApplication: '$(sqlPassword)' - - - task: IISWebAppManagementOnMachineGroup@0 - condition: ${{ parameters.CreateIis }} - inputs: - IISDeploymentType: 'IISApplicationPool' - ActionIISApplicationPool: 'StartAppPool' - StartStopRecycleAppPoolName: '$(siteDNS)-$(productName)' - - - task: FileTransform@1 - displayName: 'Variable transformation' - inputs: - folderPath: '$(Pipeline.Workspace)/**/*.zip' - fileType: 'json' - targetFiles: 'appsettings.$(environmentApp).json' - - - task: IISWebAppDeploymentOnMachineGroup@0 - displayName: Deploy IIS Website/App - $(siteDNS)\$(productName) - inputs: - WebSiteName: '$(siteDNS)' - VirtualApplication: '$(productName)' - Package: '$(Pipeline.Workspace)/**/*.zip' - TakeAppOfflineFlag: true \ No newline at end of file diff --git a/.azuredevops/pipelines/COMPANY-PRODUCT-api-ci.yml b/.azuredevops/pipelines/COMPANY-PRODUCT-api-ci.yml deleted file mode 100644 index 8e2fb65..0000000 --- a/.azuredevops/pipelines/COMPANY-PRODUCT-api-ci.yml +++ /dev/null @@ -1,87 +0,0 @@ -trigger: - batch: false - branches: - include: - - "*" - exclude: - - "main" - paths: - include: - - src/Core/** - - src/Infrastructure/** - - src/Presentation/** - - tests/** - -pr: - branches: - include: - - "main" - -pool: - vmImage: "ubuntu-latest" - -stages: - - stage: build - variables: - - template: ../variables/common-ci.yml - jobs: - - job: build_src - steps: - - task: PowerShell@2 - displayName: 'Set-Version.ps1' - inputs: - filePath: '$(scriptsPath)/Set-Version.ps1' - arguments: '-Path $(srcPath) -VersionToReplace 1.0.0 -Major 1 -Minor 0 -Revision 0 -CommitHash $(Build.SourceVersion) -Build $(Build.BuildNumber)' - workingDirectory: '$(scriptsPath)' - - - task: UseDotNet@2 - displayName: UseSDKVersion - inputs: - packageType: 'sdk' - version: $(dotnetVersion) - includePreviewVersions: true - - - task: DotNetCoreCLI@2 - displayName: dotnetrestore - inputs: - command: 'restore' - projects: "$(srcSolution)" - feedsToUse: 'config' - nugetConfigPath: "$(nugetConfig)" - - - task: DotNetCoreCLI@2 - displayName: dotnet build - inputs: - command: 'build' - projects: "$(srcProject)" - arguments: "--configuration $(buildConfiguration)" - - - job: test_src - dependsOn: build_src - steps: - - task: DotNetCoreCLI@2 - displayName: dotnet test - inputs: - command: 'test' - projects: "$(testProject)" - arguments: "--configuration $(buildConfiguration)" - publishTestResults: true - - - job: publish_src - dependsOn: test_src - steps: - - task: DotNetCoreCLI@2 - displayName: dotnet publish - inputs: - command: 'publish' - publishWebProjects: false - projects: "$(srcProject)" - arguments: "--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)" - zipAfterPublish: true - - - task: PublishBuildArtifacts@1 - displayName: Publishbuildartifacts - inputs: - PathtoPublish: "$(Build.ArtifactStagingDirectory)" - ArtifactName: 'drop' - publishLocation: "Container" diff --git a/.azuredevops/pipelines/COMPANY-PRODUCT-ef.yml b/.azuredevops/pipelines/COMPANY-PRODUCT-ef.yml deleted file mode 100644 index 2d9222d..0000000 --- a/.azuredevops/pipelines/COMPANY-PRODUCT-ef.yml +++ /dev/null @@ -1,125 +0,0 @@ -trigger: - batch: true - branches: - include: - - '*' - paths: - include: - - src/**/migrations/* - -pr: - branches: - exclude: - - '*' - -pool: - vmImage: 'windows-latest' - -parameters: - - name: releaseToDev - type: boolean - default: true - - name: releaseToQA - type: boolean - default: true - - name: releaseToStaging - type: boolean - default: false - - name: releaseToProd - type: boolean - default: false - - name: subscriptionService - type: string - default: "AZURE_DEVOPS_SERVICE_CONNECTION_NAME" - -variables: - - template: ../variables/common.yml - -stages: - - stage: development - condition: and(eq(lower(${{ parameters.releaseToDev }}), 'true'), and(ne(variables['Build.SourceBranch'], 'refs/heads/develop'), not(eq(variables['Build.Reason'], 'PullRequest')))) - variables: - - template: ../variables/development.yml - - jobs: - - job: deploy_ef_migration - steps: - - template: ../steps/ef-deploy-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - buildConfiguration: '$(buildConfiguration)' - dbContext: '$(dbContext)' - srcPath: '$(srcPath)' - sqldbName: '$(sqldbName)' - sqlName: '$(sqlName)' - sqlUser: '$(sqlUser)' - sqlPassword: '$(sqlPassword)' - efStartupPath: '$(efStartupPath)' - efTargetPath: '$(efTargetPath)' - subscriptionService: '${{ parameters.subscriptionService }}' - - - stage: qa - condition: and(eq(lower(${{ parameters.releaseToQA }}), 'true'), and(ne(variables['Build.SourceBranch'], 'refs/heads/develop'), ne(variables['Build.Reason'], 'PullRequest'))) - variables: - - template: ../variables/qa.yml - - jobs: - - job: deploy_ef_migration - steps: - - template: ../steps/ef-deploy-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - buildConfiguration: '$(buildConfiguration)' - dbContext: '$(dbContext)' - srcPath: '$(srcPath)' - sqldbName: '$(sqldbName)' - sqlName: '$(sqlName)' - sqlUser: '$(sqlUser)' - sqlPassword: '$(sqlPassword)' - efStartupPath: '$(efStartupPath)' - efTargetPath: '$(efTargetPath)' - subscriptionService: '${{ parameters.subscriptionService }}' - - - stage: staging - condition: and(eq(lower(${{ parameters.releaseToStaging }}), 'true'), and(ne(variables['Build.SourceBranch'], 'refs/heads/develop'), ne(variables['Build.Reason'], 'PullRequest'))) - variables: - - template: ../variables/staging.yml - - jobs: - - job: deploy_ef_migration - steps: - - template: ../steps/ef-deploy-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - buildConfiguration: '$(buildConfiguration)' - dbContext: '$(dbContext)' - srcPath: '$(srcPath)' - sqldbName: '$(sqldbName)' - sqlName: '$(sqlName)' - sqlUser: '$(sqlUser)' - sqlPassword: '$(sqlPassword)' - efStartupPath: '$(efStartupPath)' - efTargetPath: '$(efTargetPath)' - subscriptionService: '${{ parameters.subscriptionService }}' - - - stage: production - condition: and(eq(lower(${{ parameters.releaseToProd }}), 'true'), and(ne(variables['Build.SourceBranch'], 'refs/heads/main'), ne(variables['Build.Reason'], 'PullRequest'))) - variables: - - template: ../variables/production.yml - - jobs: - - job: deploy_ef_migration - steps: - - template: ../steps/ef-deploy-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - buildConfiguration: '$(buildConfiguration)' - dbContext: '$(dbContext)' - srcPath: '$(srcPath)' - sqldbName: '$(sqldbName)' - sqlName: '$(sqlName)' - sqlUser: '$(sqlUser)' - sqlPassword: '$(sqlPassword)' - efStartupPath: '$(efStartupPath)' - efTargetPath: '$(efTargetPath)' - subscriptionService: '${{ parameters.subscriptionService }}' diff --git a/.azuredevops/pipelines/COMPANY-PRODUCT-func.yml b/.azuredevops/pipelines/COMPANY-PRODUCT-func.yml deleted file mode 100644 index 6d16324..0000000 --- a/.azuredevops/pipelines/COMPANY-PRODUCT-func.yml +++ /dev/null @@ -1,185 +0,0 @@ -trigger: - batch: true - branches: - include: - - 'develop' - - 'main' - paths: - include: - - src/Core/* - - src/Infrastructure/* - - src/Specs/* - - src/Presentation/Function/* - -pr: - branches: - exclude: - - '*' - -pool: - vmImage: 'ubuntu-latest' - -parameters: - - name: subscriptionService - type: string - default: "AZURE_DEVOPS_SERVICE_CONNECTION_NAME" - - - name: funcSettings - type: object - default: - - name: 'APPINSIGHTS_INSTRUMENTATIONKEY' - value: '$(appiKey)' - slotSetting: false - - name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' - value: '$(appiConnection)' - slotSetting: false - - name: 'AZURE_FUNCTIONS_ENVIRONMENT' - value: '$(environmentApp)' - slotSetting: false - - name: 'FUNCTIONS_EXTENSION_VERSION' - value: '~$(funcVersion)' - slotSetting: false - - name: 'FUNCTIONS_WORKER_RUNTIME' - value: '$(funcRuntime)' - slotSetting: false - - name: 'AzureWebJobs..Disabled' - value: 'false' - slotSetting: false - - name: 'MyFunction.TimerSchedule' - value: '0 */5 * * * *' - slotSetting: false - -variables: - - template: ../variables/common.yml - -stages: - - stage: development - condition: and(or(eq(variables['Build.SourceBranch'], 'refs/heads/develop'), eq(variables['Build.SourceBranch'], 'refs/heads/main')), ne(variables['Build.Reason'], 'PullRequest')) - variables: - - template: ../variables/development.yml - - jobs: - - job: build_src - steps: - - template: ../steps/dotnet-build-steps.yml - parameters: - dotnetVersion: '$(dotnetVersion)' - srcPath: '$(srcPath)' - srcProject: '$(apiProject)' - - - - job: test_unit - dependsOn: build_src - steps: - - template: ../steps/dotnet-test-steps.yml - parameters: - dotnetVersion: '$(dotnetVersion)' - testPath: '$(unitPath)' - testProject: '$(unitProject)' - restorePath: '$(srcPath)' - appcsEnvironmentVariable: '$(appcsEnvironmentVariable)' - appcsConnection: '$(appcsConnection)' - environmentApp: '$(environmentApp)' - - - job: build_func - dependsOn: test_unit - steps: - - template: ../steps/func-build-steps.yml - parameters: - archiveFile: '$(archiveFileFunc)' - dotnetVersion: '$(dotnetVersion)' - scriptsPath: '$(scriptspath)' - funcPath: '$(funcPath)' - tempPath: '$(tempPath)' - - - job: settings_func - dependsOn: build_func - steps: - - template: ../steps/func-settings-steps.yml - parameters: - subscriptionService: '${{ parameters.subscriptionService }}' - environmentApp: '$(environmentApp)' - funcName: '$(funcName)' - appSettings: '${{ parameters.funcSettings }}' - - - job: deploy_func - dependsOn: settings_func - steps: - - template: ../steps/func-deploy-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - archiveFile: '$(archiveFileFunc)' - funcName: '$(funcName)' - subscriptionId: '$(subscriptionId)' - subscriptionService: '${{ parameters.subscriptionService }}' - - - stage: production - condition: and(eq(variables['Build.SourceBranch'], 'refs/heads/main'), ne(variables['Build.Reason'], 'PullRequest')) - variables: - - template: ../variables/production.yml - - jobs: - - job: deploy_approval - pool: server - timeoutInMinutes: 4320 - steps: - - task: ManualValidation@0 - inputs: - notifyUsers: $(deployApprovers) - instructions: "Team: $(System.TeamProject)\r\nRepo: $(Build.Repository.Name)\r\nRequested by: $(Build.RequestedFor)\r\nBuild: $(Build.BuildNumber)\r\nUrl: $(Build.BuildUri)\r\nA new deployment has been requested. Please approve or reject the deployment." - onTimeout: reject - - - job: build_src - dependsOn: deploy_approval - steps: - - template: ../steps/dotnet-build-steps.yml - parameters: - dotnetVersion: '$(dotnetVersion)' - srcPath: '$(srcPath)' - srcProject: '$(apiProject)' - - - - job: test_unit - dependsOn: build_src - steps: - - template: ../steps/dotnet-test-steps.yml - parameters: - dotnetVersion: '$(dotnetVersion)' - testPath: '$(unitPath)' - testProject: '$(unitProject)' - restorePath: '$(srcPath)' - appcsEnvironmentVariable: '$(appcsEnvironmentVariable)' - appcsConnection: '$(appcsConnection)' - environmentApp: '$(environmentApp)' - - - job: build_func - dependsOn: test_unit - steps: - - template: ../steps/func-build-steps.yml - parameters: - archiveFile: '$(archiveFileFunc)' - dotnetVersion: '$(dotnetVersion)' - scriptsPath: '$(scriptspath)' - funcPath: '$(funcPath)' - tempPath: '$(tempPath)' - - - job: settings_func - dependsOn: build_func - steps: - - template: ../steps/func-settings-steps.yml - parameters: - subscriptionService: '${{ parameters.subscriptionService }}' - environmentApp: '$(environmentApp)' - funcName: '$(funcName)' - appSettings: '${{ parameters.funcSettings }}' - - - job: deploy_func - dependsOn: settings_func - steps: - - template: ../steps/func-deploy-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - archiveFile: '$(archiveFileFunc)' - funcName: '$(funcName)' - subscriptionId: '$(subscriptionId)' - subscriptionService: '${{ parameters.subscriptionService }}' \ No newline at end of file diff --git a/.azuredevops/pipelines/COMPANY-PRODUCT-infrastructure-bicep.yml b/.azuredevops/pipelines/COMPANY-PRODUCT-infrastructure-bicep.yml deleted file mode 100644 index 1f59207..0000000 --- a/.azuredevops/pipelines/COMPANY-PRODUCT-infrastructure-bicep.yml +++ /dev/null @@ -1,144 +0,0 @@ -trigger: - batch: false - branches: - include: - - 'main' - paths: - include: - - .azure/** - exclude: - - '*' - -pr: - branches: - include: - - 'main' - -pool: - vmImage: 'ubuntu-latest' - -parameters: - - name: ForceDeployDev - type: boolean - default: false - - name: ForceDeployProd - type: boolean - default: false - - name: SubscriptionService - type: string - default: 'AZURE-DEVOPS-SERVICE-CONNECTION-NAME' - -stages: - - stage: development - condition: or(eq(lower(${{ parameters.ForceDeployDev }}), 'true'), and(eq(variables['Build.SourceBranch'], 'refs/heads/main'), ne(variables['Build.Reason'], 'PullRequest'))) - variables: - - template: ../variables/development-infrastructure.yml - - jobs: - - job: deploy_approval - pool: server - timeoutInMinutes: 4320 - steps: - - task: ManualValidation@0 - displayName: Approve $(environmentApp) Deployment - inputs: - notifyUsers: $(deployApprovers) - instructions: "Team: $(System.TeamProject)\r\nRepo: $(Build.Repository.Name)\r\nRequested by: $(Build.RequestedFor)\r\nBuild: $(Build.BuildNumber)\r\nUrl: $(Build.BuildUri)\r\nA new deployment has been requested. Please approve or reject the deployment." - onTimeout: reject - - - job: deploy_resourcegroup - dependsOn: deploy_approval - steps: - - task: AzureCLI@2 - displayName: Create Resource Group - inputs: - azureSubscription: '${{ parameters.SubscriptionService }}' - scriptType: 'bash' - scriptLocation: 'inlineScript' - inlineScript: | - az group create --name $(rgSharedName) --location $(rgSharedLocation) - az group create --name $(rgName) --location $(rgLocation) - - - job: validate_landingzone - dependsOn: deploy_resourcegroup - steps: - - task: AzureCLI@2 - displayName: Validate Landing Zone - inputs: - azureSubscription: '${{ parameters.SubscriptionService }}' - scriptType: 'bash' - scriptLocation: 'inlineScript' - inlineScript: | - az deployment group what-if --resource-group $(rgSharedName) --template-file $(sharedTemplate) --parameters $(sharedParameters) - az deployment group what-if --resource-group $(rgName) --template-file $(landingZoneTemplate) --parameters $(landingZoneParameters) - - - job: deploy_landingzone - dependsOn: validate_landingzone - steps: - - task: AzureCLI@2 - displayName: Deploy Landing Zone - inputs: - azureSubscription: '${{ parameters.SubscriptionService }}' - scriptType: 'bash' - scriptLocation: 'inlineScript' - inlineScript: | - az deployment group create --resource-group $(rgSharedName) --template-file $(sharedTemplate) --parameters $(sharedParameters) - az deployment group create --resource-group $(rgName) --template-file $(landingZoneTemplate) --parameters $(landingZoneParameters) - az keyvault set-policy --name '$(kvName)' --object-id $(serviceConnectionIdentityObjectId) --secret-permissions get list - - - stage: production - condition: or(eq(lower(${{ parameters.ForceDeployProd }}), 'true'), and(and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main')), ne(variables['Build.Reason'], 'PullRequest'))) - variables: - - template: ../variables/production-infrastructure.yml - - jobs: - - job: deploy_approval - pool: server - timeoutInMinutes: 4320 - steps: - - task: ManualValidation@0 - displayName: Approve $(environmentApp) Deployment - inputs: - notifyUsers: $(deployApprovers) - instructions: "Team: $(System.TeamProject)\r\nRepo: $(Build.Repository.Name)\r\nRequested by: $(Build.RequestedFor)\r\nBuild: $(Build.BuildNumber)\r\nUrl: $(Build.BuildUri)\r\nA new deployment has been requested. Please approve or reject the deployment." - onTimeout: reject - - - job: deploy_resourcegroup - dependsOn: deploy_approval - steps: - - task: AzureCLI@2 - displayName: Create Resource Group - inputs: - azureSubscription: '${{ parameters.SubscriptionService }}' - scriptType: 'bash' - scriptLocation: 'inlineScript' - inlineScript: | - az group create --name $(rgSharedName) --location $(rgSharedLocation) - az group create --name $(rgName) --location $(rgLocation) - - - job: validate_landingzone - dependsOn: deploy_resourcegroup - steps: - - task: AzureCLI@2 - displayName: Validate Landing Zone - inputs: - azureSubscription: '${{ parameters.SubscriptionService }}' - scriptType: 'bash' - scriptLocation: 'inlineScript' - inlineScript: | - az deployment group what-if --resource-group $(rgSharedName) --template-file $(sharedTemplate) --parameters $(sharedParameters) - az deployment group what-if --resource-group $(rgName) --template-file $(landingZoneTemplate) --parameters $(landingZoneParameters) - - - job: deploy_landingzone - dependsOn: validate_landingzone - steps: - - task: AzureCLI@2 - displayName: Deploy Landing Zone - inputs: - azureSubscription: '${{ parameters.SubscriptionService }}' - scriptType: 'bash' - scriptLocation: 'inlineScript' - inlineScript: | - az deployment group create --resource-group $(rgSharedName) --template-file $(sharedTemplate) --parameters $(sharedParameters) - az deployment group create --resource-group $(rgName) --template-file $(landingZoneTemplate) --parameters $(landingZoneParameters) - az keyvault set-policy --name '$(kvName)' --object-id $(serviceConnectionIdentityObjectId) --secret-permissions get list diff --git a/.azuredevops/pipelines/COMPANY-PRODUCT-infrastructure-template.yml b/.azuredevops/pipelines/COMPANY-PRODUCT-infrastructure-template.yml deleted file mode 100644 index a3f68b9..0000000 --- a/.azuredevops/pipelines/COMPANY-PRODUCT-infrastructure-template.yml +++ /dev/null @@ -1,154 +0,0 @@ -trigger: - batch: true - branches: - include: - - '*' - paths: - include: - - .azure/* - exclude: - - '*' - -pr: - branches: - exclude: - - '*' - -pool: - vmImage: 'ubuntu-latest' - -parameters: - - name: releaseToDev - type: boolean - default: false - - name: releaseToQA - type: boolean - default: false - - name: releaseToStaging - type: boolean - default: false - - name: releaseToProd - type: boolean - default: false - - name: subscriptionService - type: string - default: "AZURE-DEVOPS-SERVICE-CONNECTION-NAME" - -variables: - - template: ../variables/common.yml - -stages: - - stage: development - condition: or(eq(lower(${{ parameters.releaseToDev }}), 'true'), and(or(eq(variables['Build.SourceBranch'], 'refs/heads/develop'), eq(variables['Build.SourceBranch'], 'refs/heads/main')), ne(variables['Build.Reason'], 'PullRequest'))) - variables: - - template: ../variables/development.yml - - jobs: - - job: deploy_landing_zone - steps: - - template: ../steps/landingzone-infrastructure-steps.yml - parameters: - armPath: '$(armPath)' - subscriptionId: '$(subscriptionId)' - subscriptionService: '${{ parameters.subscriptionService }}' - rgName: '$(rgName)' - rgLocation: '$(rgLocation)' - appiName: '$(appiName)' - kvName: '$(kvName)' - stName: '$(stName)' - workSubscriptionId: '$(subscriptionId)' - workName: '$(workName)' - workResourceGroupName: '$(workResourceGroupName)' - workLocation: '$(workLocation)' - - - task: AzureCLI@2 - inputs: - azureSubscription: '${{ parameters.subscriptionService }}' - scriptType: bash - scriptLocation: inlineScript - inlineScript: | - az keyvault set-policy --name '$(kvName)' --object-id $(objectId) --secret-permissions get list - - - job: deploy_func_and_plan - dependsOn: deploy_landing_zone - steps: - - template: ../steps/func-infrastructure-plan-vnet-steps.yml - parameters: - armPath: '$(armPath)' - subscriptionId: '$(subscriptionId)' - subscriptionService: '${{ parameters.subscriptionService }}' - rgName: '$(rgName)' - rgLocation: '$(rgLocation)' - environmentApp: '$(environmentApp)' - appiKey: '$(appiKey)' - appiConnection: '$(appiConnection)' - funcName: '$(funcName)' - planSku: '$(funcPlan)' - stName: '$(stName)' - vnetResourceGroupName: '$(vnetResourceGroupName)' - vnetName: '$(vnetName)' - snetName: '$(snetName)' - alwaysOn: 'true' - - - stage: production - condition: or(eq(lower(${{ parameters.releaseToProd }}), 'true'), and(eq(variables['Build.SourceBranch'], 'refs/heads/main'), ne(variables['Build.Reason'], 'PullRequest'))) - variables: - - template: ../variables/production.yml - - jobs: - - job: deploy_approval - pool: server - timeoutInMinutes: 4320 - steps: - - task: ManualValidation@0 - inputs: - notifyUsers: $(deployApprovers) - instructions: "Team: $(System.TeamProject)\r\nRepo: $(Build.Repository.Name)\r\nRequested by: $(Build.RequestedFor)\r\nBuild: $(Build.BuildNumber)\r\nUrl: $(Build.BuildUri)\r\nA new deployment has been requested. Please approve or reject the deployment." - onTimeout: reject - - - job: deploy_landing_zone - dependsOn: deploy_approval - steps: - - template: ../steps/landingzone-infrastructure-steps.yml - parameters: - armPath: '$(armPath)' - subscriptionId: '$(subscriptionId)' - subscriptionService: '${{ parameters.subscriptionService }}' - rgName: '$(rgName)' - rgLocation: '$(rgLocation)' - appiName: '$(appiName)' - kvName: '$(kvName)' - stName: '$(stName)' - workSubscriptionId: '$(subscriptionId)' - workName: '$(workName)' - workResourceGroupName: '$(workResourceGroupName)' - workLocation: '$(workLocation)' - - - task: AzureCLI@2 - inputs: - azureSubscription: '${{ parameters.subscriptionService }}' - scriptType: bash - scriptLocation: inlineScript - inlineScript: | - az keyvault set-policy --name '$(kvName)' --object-id $(objectId) --secret-permissions get list - - - job: deploy_func_and_plan - dependsOn: deploy_landing_zone - steps: - - template: ../steps/func-infrastructure-plan-vnet-steps.yml - parameters: - armPath: '$(armPath)' - subscriptionId: '$(subscriptionId)' - subscriptionService: '${{ parameters.subscriptionService }}' - rgName: '$(rgName)' - rgLocation: '$(rgLocation)' - environmentApp: '$(environmentApp)' - appiKey: '$(appiKey)' - appiConnection: '$(appiConnection)' - funcName: '$(funcName)' - planSku: '$(funcPlan)' - stName: '$(stName)' - vnetResourceGroupName: '$(vnetResourceGroupName)' - vnetName: '$(vnetName)' - snetName: '$(snetName)' - alwaysOn: 'true' \ No newline at end of file diff --git a/.azuredevops/pipelines/COMPANY-PRODUCT-landingzone.yml b/.azuredevops/pipelines/COMPANY-PRODUCT-landingzone.yml deleted file mode 100644 index c010b34..0000000 --- a/.azuredevops/pipelines/COMPANY-PRODUCT-landingzone.yml +++ /dev/null @@ -1,85 +0,0 @@ -trigger: - batch: true - branches: - include: - - '*' - paths: - include: - - .azure/* - -pr: - branches: - exclude: - - '*' - -pool: - vmImage: 'ubuntu-latest' - -parameters: - - name: subscriptionService - type: string - default: "AZURE_DEVOPS_SERVICE_CONNECTION" - -variables: - - template: ../variables/common.yml - -stages: - - stage: development - condition: eq(variables['Build.SourceBranch'], 'refs/heads/develop') - variables: - - template: ../variables/development.yml - - jobs: - - job: deploy_landing_zone - steps: - - template: ../steps/landingzone-infrastructure-steps.yml - parameters: - armPath: '$(armPath)' - subscriptionId: '$(subscriptionId)' - subscriptionService: '${{ parameters.subscriptionService }}' - rgName: '$(rgName)' - rgLocation: '$(rgLocation)' - appiName: '$(appiName)' - kvName: '$(kvName)' - stName: '$(stName)' - workLocation: '$(rgLocation)' - workName: '$(workName)' - workResourceGroupName: '$(workResourceGroupName)' - workSubscriptionId: '$(subscriptionId)' - - task: AzureCLI@2 - inputs: - azureSubscription: '${{ parameters.subscriptionService }}' - scriptType: bash - scriptLocation: inlineScript - inlineScript: | - az keyvault set-policy --name '$(kvName)' --object-id $(objectId) --secret-permissions get list - - - stage: production - condition: eq(variables['Build.SourceBranch'], 'refs/heads/main') - variables: - - template: ../variables/production.yml - - jobs: - - job: deploy_landing_zone - steps: - - template: ../steps/landingzone-infrastructure-steps.yml - parameters: - armPath: '$(armPath)' - subscriptionId: '$(subscriptionId)' - subscriptionService: '${{ parameters.subscriptionService }}' - rgName: '$(rgName)' - rgLocation: '$(rgLocation)' - appiName: '$(appiName)' - kvName: '$(kvName)' - stName: '$(stName)' - workLocation: '$(rgLocation)' - workName: '$(workName)' - workResourceGroupName: '$(workResourceGroupName)' - workSubscriptionId: '$(subscriptionId)' - - task: AzureCLI@2 - inputs: - azureSubscription: '${{ parameters.subscriptionService }}' - scriptType: bash - scriptLocation: inlineScript - inlineScript: | - az keyvault set-policy --name '$(kvName)' --object-id $(objectId) --secret-permissions get list \ No newline at end of file diff --git a/.azuredevops/pipelines/COMPANY-PRODUCT-nuget.yml b/.azuredevops/pipelines/COMPANY-PRODUCT-nuget.yml deleted file mode 100644 index add59bb..0000000 --- a/.azuredevops/pipelines/COMPANY-PRODUCT-nuget.yml +++ /dev/null @@ -1,115 +0,0 @@ -trigger: - batch: true - branches: - include: - - '*' - paths: - include: - - src/* - -pr: - branches: - exclude: - - '*' - -pool: - vmImage: 'ubuntu-latest' - -parameters: - - name: nugetService - type: string - default: "COMPANY-nuget-001" - - name: nugetId - type: string - default: "$(nugetId)" - - name: versionToReplace - type: string - default: "1.0.0" - -variables: - - template: ../variables/common.yml - -stages: - - stage: development - condition: eq(variables['Build.SourceBranch'], 'refs/heads/develop') - variables: - - template: ../variables/development.yml - - jobs: - - job: build_src - steps: - - template: ../steps/dotnet-build-steps.yml - parameters: - scriptsPath: '$(scriptsPath)' - srcPath: '$(srcPath)' - - - job: test_src - dependsOn: build_src - steps: - - template: ../steps/dotnet-test-steps.yml - parameters: - testPath: '$(srcPath)' - arguments: '--filter TestCategory!=Integration' - appcsEnvironmentVariable: '${{ parameters.appcsEnvironmentVariable }}' - appcsConnection: '${{ parameters.appcsConnection }}' - environmentApp: '$(environmentApp)' - - - job: pack_src - dependsOn: test_src - steps: - - template: ../steps/dotnet-pack-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - scriptsPath: '$(scriptsPath)' - srcPath: '$(srcPath)' - versionToReplace: '${{ parameters.versionToReplace }}' - - - job: deploy_nuget - dependsOn: pack_src - steps: - - template: ../steps/nuget-deploy-internal-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - nugetId: '${{ parameters.nugetId }}' - - - stage: production - condition: eq(variables['Build.SourceBranch'], 'refs/heads/main') - variables: - - template: ../variables/production.yml - - jobs: - - job: build_src - steps: - - template: ../steps/dotnet-build-steps.yml - parameters: - scriptsPath: '$(scriptsPath)' - srcPath: '$(srcPath)' - - - job: test_src - dependsOn: build_src - steps: - - template: ../steps/dotnet-test-steps.yml - parameters: - testPath: '$(srcPath)' - arguments: '--filter TestCategory!=Integration' - appcsEnvironmentVariable: '${{ parameters.appcsEnvironmentVariable }}' - appcsConnection: '${{ parameters.appcsConnection }}' - environmentApp: '$(environmentApp)' - - - job: pack_src - dependsOn: test_src - steps: - - template: ../steps/dotnet-pack-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - scriptsPath: '$(scriptsPath)' - srcPath: '$(srcPath)' - versionToReplace: '${{ parameters.versionToReplace }}' - - - job: deploy_nuget - dependsOn: pack_src - steps: - - template: ../steps/nuget-deploy-external-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - nugetService: '${{ parameters.nugetService }}' \ No newline at end of file diff --git a/.azuredevops/pipelines/COMPANY-PRODUCT-tests.yml b/.azuredevops/pipelines/COMPANY-PRODUCT-tests.yml deleted file mode 100644 index aff944c..0000000 --- a/.azuredevops/pipelines/COMPANY-PRODUCT-tests.yml +++ /dev/null @@ -1,42 +0,0 @@ -trigger: - batch: true - branches: - include: - - '*' - paths: - include: - - tests/* - -pr: - branches: - exclude: - - '*' - -pool: - vmImage: 'ubuntu-latest' - -variables: - - template: ../variables/development.yml - - name: buildPlatform - value: 'Any CPU' - - name: buildConfiguration - value: 'Release' - -stages: - - stage: development - condition: eq(variables['Build.SourceBranch'], 'refs/heads/develop') - variables: - - template: ../variables/development.yml - - jobs: - - job: test_integration - steps: - - template: ../steps/integration-test-steps.yml - parameters: - dotnetVersion: '$(dotnetVersion)' - integrationPath: '$(integrationPath)' - appcsEnvironmentVariable: '$(appcsEnvironmentVariable)' - appcsConnection: '$(appcsConnection)' - stEnvironmentVariable: '$(stEnvironmentVariable)' - stConnection: '$(stConnection)' - environmentApp: '$(environmentApp)' diff --git a/.azuredevops/pipelines/COMPANY-PRODUCT-webjob.yml b/.azuredevops/pipelines/COMPANY-PRODUCT-webjob.yml deleted file mode 100644 index cddba05..0000000 --- a/.azuredevops/pipelines/COMPANY-PRODUCT-webjob.yml +++ /dev/null @@ -1,149 +0,0 @@ -trigger: - batch: true - branches: - include: - - '*' - paths: - include: - - src/* - -pr: - branches: - exclude: - - '*' - -pool: - vmImage: 'ubuntu-latest' - -parameters: - - name: workerSettings - type: object - default: - - name: 'APPINSIGHTS_INSTRUMENTATIONKEY' - value: '$(appiKey)' - slotSetting: false - - name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' - value: '$(appiConnection)' - slotSetting: false - - name: 'ASPNETCORE_ENVIRONMENT' - value: '$(environmentApp)' - slotSetting: false - - name: 'AzureWebJobsDashboard' - value: '$(stConnection)' - slotSetting: false - - name: '$(appcsEnvironmentVariable)' - value: '$(appcsConnection)' - slotSetting: false - - name: '$(stEnvironmentVariable)' - value: '$(stConnection)' - slotSetting: false - -variables: - - template: ../variables/development.yml - - name: buildPlatform - value: 'Any CPU' - - name: buildConfiguration - value: 'Release' - -stages: - - stage: development - condition: eq(variables['Build.SourceBranch'], 'refs/heads/develop') - variables: - - template: ../variables/development.yml - - jobs: - - job: build_webjob - steps: - - template: ../steps/webjob-build-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - archiveFile: '$(archiveFile)' - dotnetVersion: '$(dotnetVersion)' - srcPath: '$(webjobPath)' - webjobName: '$(webjobName)' - cronSchedule: '$(webjobSchedule)' - - - job: test_webjob - dependsOn: build_webjob - steps: - - template: ../steps/dotnet-test-steps.yml - parameters: - dotnetVersion: '$(dotnetVersion)' - unitPath: $(webjobPath) - environmentApp: '$(environmentApp)' - appcsEnvironmentVariable: '$(appcsEnvironmentVariable)' - appcsConnection: '$(appcsConnection)' - stEnvironmentVariable: '$(stEnvironmentVariable)' - stConnection: '$(stConnection)' - - - job: settings_webjob - dependsOn: build_webjob - steps: - - template: ../steps/webjob-settings-steps.yml - parameters: - subscriptionName: '$(subscriptionName)' - environmentApp: '$(environmentApp)' - webjobName: '$(webjobName)' - appSettings: '${{ parameters.workerSettings }}' - - - job: deploy_webjob - dependsOn: settings_webjob - steps: - - template: ../steps/webjob-deploy-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - archiveFile: '$(archiveFile)' - webjobName: '$(webjobName)' - subscriptionId: '$(subscriptionId)' - subscriptionName: '$(subscriptionName)' - - - stage: production - condition: eq(variables['Build.SourceBranch'], 'refs/heads/main') - variables: - - template: ../variables/production.yml - - jobs: - - job: build_webjob - steps: - - template: ../steps/webjob-build-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - archiveFile: '$(archiveFile)' - dotnetVersion: '$(dotnetVersion)' - srcPath: '$(webjobPath)' - webjobName: '$(webjobName)' - cronSchedule: '$(webjobSchedule)' - - - job: test_webjob - dependsOn: build_webjob - steps: - - template: ../steps/dotnet-test-steps.yml - parameters: - dotnetVersion: '$(dotnetVersion)' - unitPath: $(webjobPath) - environmentApp: '$(environmentApp)' - appcsEnvironmentVariable: '$(appcsEnvironmentVariable)' - appcsConnection: '$(appcsConnection)' - stEnvironmentVariable: '$(stEnvironmentVariable)' - stConnection: '$(stConnection)' - - - job: settings_webjob - dependsOn: build_webjob - steps: - - template: ../steps/webjob-settings-steps.yml - parameters: - subscriptionName: '$(subscriptionName)' - environmentApp: '$(environmentApp)' - webjobName: '$(webjobName)' - appSettings: '${{ parameters.workerSettings }}' - - - job: deploy_webjob - dependsOn: settings_webjob - steps: - - template: ../steps/webjob-deploy-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - archiveFile: '$(archiveFile)' - webjobName: '$(webjobName)' - subscriptionId: '$(subscriptionId)' - subscriptionName: '$(subscriptionName)' \ No newline at end of file diff --git a/.azuredevops/pipelines/COMPANY-PRODUCT-worker.yml b/.azuredevops/pipelines/COMPANY-PRODUCT-worker.yml deleted file mode 100644 index e0fbd84..0000000 --- a/.azuredevops/pipelines/COMPANY-PRODUCT-worker.yml +++ /dev/null @@ -1,174 +0,0 @@ -trigger: - batch: true - branches: - include: - - '*' - paths: - include: - - src/* - -pr: - branches: - exclude: - - '*' - -pool: - vmImage: 'ubuntu-latest' - -parameters: - - name: appcsEnvironmentVariable - type: string - default: "AZURE_APP_CONFIGURATION_CONNECTION" - - name: appcsConnection - type: string - default: "$(appcsConnection)" # from secrets (library) - - name: subscriptionService - type: string - default: "AZURE_DEVOPS_SERVICE_CONNECTION" - -variables: - - template: ../variables/common.yml - -stages: - - stage: development - condition: eq(variables['Build.SourceBranch'], 'refs/heads/develop') - variables: - - template: ../variables/development.yml - - jobs: - - job: build_src - steps: - - template: ../steps/dotnet-build-steps.yml - parameters: - dotnetVersion: '$(dotnetVersion)' - srcPath: '$(srcPath)' - - - job: test_src - dependsOn: build_src - steps: - - template: ../steps/dotnet-test-steps.yml - parameters: - dotnetVersion: '$(dotnetVersion)' - testPath: '$(srcPath)' - appcsEnvironmentVariable: '$(appcsEnvironmentVariable)' - appcsConnection: '$(appcsConnection)' - environmentApp: '$(environmentApp)' - - - job: build_func - dependsOn: test_src - steps: - - template: ../steps/func-build-steps.yml - parameters: - archiveFile: '$(funcArchiveFile)' - dotnetVersion: '$(dotnetVersion)' - funcPath: '$(funcPath)' - tempPath: '$(tempPath)' - - - job: deploy_func - dependsOn: build_func - steps: - - template: ../steps/func-deploy-steps.yml - parameters: - appSettings: '-$(appcsEnvironmentVariable) $(appcsConnection) -$(stEnvironmentVariable) $(stConnection) -APPINSIGHTS_INSTRUMENTATIONKEY $(appiKey) -APPLICATIONINSIGHTS_CONNECTION_STRING $(appiConnection) -AZURE_FUNCTIONS_ENVIRONMENT $(environmentApp) -ASPNETCORE_ENVIRONMENT $(environmentApp) ' - artifactsPath: '$(artifactsPath)' - archiveFile: '$(funcArchiveFile)' - funcName: '$(funcName)' - subscriptionId: '$(subscriptionId)' - subscriptionService: '${{ parameters.subscriptionService }}' - - - job: build_webjob - dependsOn: test_src - steps: - - template: ../steps/webjob-build-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - archiveFile: '$(webjobArchiveFile)' - dotnetVersion: '$(dotnetVersion)' - srcPath: '$(srcPath)' - webjobProject: '$(webjobProject)' - webjobName: '$(webjobName)' - cronSchedule: '$(webjobSchedule)' - - - job: deploy_webjob - dependsOn: build_webjob - steps: - - template: ../steps/web-deploy-steps.yml - parameters: - appSettings: '-$(appcsEnvironmentVariable) $(appcsConnection) -$(stEnvironmentVariable) $(stConnection) -AzureWebJobsDashboard $(stConnection) -APPINSIGHTS_INSTRUMENTATIONKEY $(appiKey) -APPLICATIONINSIGHTS_CONNECTION_STRING $(appiConnection) -AZURE_FUNCTIONS_ENVIRONMENT $(environmentApp) -ASPNETCORE_ENVIRONMENT $(environmentApp) ' - artifactsPath: '$(artifactsPath)' - archiveFile: '$(webjobArchiveFile)' - webName: '$(webjobName)' - subscriptionId: '$(subscriptionId)' - subscriptionService: '${{ parameters.subscriptionService }}' - - - stage: production - condition: eq(variables['Build.SourceBranch'], 'refs/heads/main') - variables: - - template: ../variables/production.yml - - jobs: - - job: build_src - steps: - - template: ../steps/dotnet-build-steps.yml - parameters: - dotnetVersion: '$(dotnetVersion)' - srcPath: '$(srcPath)' - - - job: test_src - dependsOn: build_src - steps: - - template: ../steps/dotnet-test-steps.yml - parameters: - dotnetVersion: '$(dotnetVersion)' - testPath: '$(srcPath)' - appcsEnvironmentVariable: '$(appcsEnvironmentVariable)' - appcsConnection: '$(appcsConnection)' - environmentApp: '$(environmentApp)' - - - job: build_func - dependsOn: test_src - steps: - - template: ../steps/func-build-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - archiveFile: '$(funcArchiveFile)' - dotnetVersion: '$(dotnetVersion)' - funcPath: '$(funcPath)' - tempPath: '$(tempPath)' - - - job: deploy_func - dependsOn: build_func - steps: - - template: ../steps/func-deploy-steps.yml - parameters: - appSettings: '-$(appcsEnvironmentVariable) $(appcsConnection) -$(stEnvironmentVariable) $(stConnection) -APPINSIGHTS_INSTRUMENTATIONKEY $(appiKey) -APPLICATIONINSIGHTS_CONNECTION_STRING $(appiConnection) -AZURE_FUNCTIONS_ENVIRONMENT $(environmentApp) -ASPNETCORE_ENVIRONMENT $(environmentApp) ' - artifactsPath: '$(artifactsPath)' - archiveFile: '$(funcArchiveFile)' - funcName: '$(funcName)' - subscriptionId: '$(subscriptionId)' - subscriptionService: '${{ parameters.subscriptionService }}' - - - job: build_webjob - dependsOn: test_src - steps: - - template: ../steps/webjob-build-steps.yml - parameters: - artifactsPath: '$(artifactsPath)' - archiveFile: '$(webjobArchiveFile)' - dotnetVersion: '$(dotnetVersion)' - srcPath: '$(srcPath)' - webjobProject: '$(webjobProject)' - webjobName: '$(webjobName)' - cronSchedule: '$(webjobSchedule)' - - - job: deploy_webjob - dependsOn: build_webjob - steps: - - template: ../steps/web-deploy-steps.yml - parameters: - appSettings: '-$(appcsEnvironmentVariable) $(appcsConnection) -$(stEnvironmentVariable) $(stConnection) -AzureWebJobsDashboard $(stConnection) -APPINSIGHTS_INSTRUMENTATIONKEY $(appiKey) -APPLICATIONINSIGHTS_CONNECTION_STRING $(appiConnection) -AZURE_FUNCTIONS_ENVIRONMENT $(environmentApp) -ASPNETCORE_ENVIRONMENT $(environmentApp) ' - artifactsPath: '$(artifactsPath)' - archiveFile: '$(webjobArchiveFile)' - webName: '$(webjobName)' - subscriptionId: '$(subscriptionId)' - subscriptionService: '${{ parameters.subscriptionService }}' \ No newline at end of file diff --git a/.azuredevops/scripts/Set-Version.ps1 b/.azuredevops/scripts/Set-Version.ps1 deleted file mode 100644 index 37d3e95..0000000 --- a/.azuredevops/scripts/Set-Version.ps1 +++ /dev/null @@ -1,82 +0,0 @@ -#----------------------------------------------------------------------- -# Set-Version [-Path []] [-VersionToReplace []] [-Type []] -# -# Example: .\Set-Version -Path \\source\path -Major 1 -#----------------------------------------------------------------------- - -# *** -# *** Parameters -# *** -param -( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string] $Path=$(throw '-Path is a required parameter. i.e. $(Build.SourcesDirectory)'), - [Version] $VersionToReplace='1.0.0', - [String] $Major='-1', - [String] $Minor='-1', - [String] $Revision='-1', - [String] $Build='-1', - [String] $Patch='-1', - [String] $PreRelease='-1', - [String] $CommitHash='-1' -) - -# *** -# *** Initialize -# *** -if ($IsWindows) { Set-ExecutionPolicy Unrestricted -Scope Process -Force } -$VerbosePreference = 'SilentlyContinue' #'Continue' -[String]$ThisScript = $MyInvocation.MyCommand.Path -[String]$ThisDir = Split-Path $ThisScript -[DateTime]$Now = Get-Date -Write-Debug "*****************************" -Write-Debug "*** Starting: $ThisScript on $Now" -Write-Debug "*****************************" -# Imports -Import-Module "$ThisDir/System.psm1" - -# *** -# *** Validate and cleanse -# *** -If($IsWindows){ - $Path = Set-Unc -Path $Path -} - -# *** -# *** Locals -# *** - -# *** -# *** Execute -# *** -$Major = $Major.Replace('-1', $VersionToReplace.ToString().Substring(0,1)) # Static 1, 2, 3 -$Minor = $Minor.Replace('-1', (Get-Date -UFormat '%Y').ToString().Substring(2,2)) # Year YYYY 2023 -$Revision = $Revision.Replace('-1', (Get-Date -UFormat '%j').ToString()) # DayOfYear D[DD]1-365 -$Build = $Build.Replace('-1', (Get-Date -UFormat '%H%M').ToString()) # HrMin 1937 -$Patch = $Patch.Replace('-1', (Get-Date -UFormat '%m').ToString()) # Month mm -$PreRelease = $PreRelease.Replace('-1', '') # -alpha -$CommitHash = $CommitHash.Replace('-1', '') # +204ff0a - - -# Version Formats -$FileVersion = "$Major.$Minor.$Revision.$Build" # Ref: https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/versioning -$AssemblyVersion = "$Major.$Minor.0.0" -$InformationalVersion = "$Major.$Minor.$Revision$PreRelease$CommitHash" -$SemanticVersion = "$Major.$Minor.$Patch$PreRelease" -Write-Debug "FileVersion: $FileVersion SemanticVersion: $SemanticVersion AssemblyVersion: $AssemblyVersion InformationalVersion: $InformationalVersion" - -# Package.json version -Update-LineByContains -Path $Path -Contains 'version' -Line """version"": ""$FileVersion""," -Include package.json -# OpenApiConfigurationOptions.cs version -Update-LineByContains -Path $Path -Contains 'Version' -Line "Version = ""$AssemblyVersion""," -Include OpenApiConfigurationOptions.cs -# *.csproj C# Project files -Update-ContentsByTag -Path $Path -Value $SemanticVersion -Open '' -Close '' -Include *.csproj -# *.nuspec NuGet packages -Update-ContentsByTag -Path $Path -Value $SemanticVersion -Open '' -Close '' -Include *.nuspec -# Assembly.cs C# assembly manifest -Update-LineByContains -Path $Path -Contains "FileVersion(" -Line "[assembly: FileVersion(""$FileVersion"")]" -Include AssemblyInfo.cs -Update-LineByContains -Path $Path -Contains "AssemblyVersion(" -Line "[assembly: AssemblyVersion(""$AssemblyVersion"")]" -Include AssemblyInfo.cs -# *.vsixmanifest VSIX Visual Studio Templates -Update-TextByContains -Path $Path -Contains "]] -# -# Example: .\Add-Prefix -String ello -Add H -# Result: Hello -#----------------------------------------------------------------------- -function Add-Prefix -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$String = $(throw '-String is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Add = $(throw '-Add is a required parameter.') - ) - Write-Verbose "Add-Prefix -String $String -Add $Add" - [string]$ReturnValue = $String - if (-not (Compare-IsFirst -String $String -BeginsWith $Add)) - { - $ReturnValue = ($Add + $ReturnValue) - Write-Verbose "[Success] 1 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - else - { - Write-Verbose "[OK] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -String $String already has prefix of -Add $Add" - } - return $ReturnValue -} -export-modulemember -function Add-Prefix - -#----------------------------------------------------------------------- -# Add-Suffix [-String []] -# -# Example: .\Add-Suffix -String Hell -Add o -# Result: Hello -#----------------------------------------------------------------------- -function Add-Suffix -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$String = $(throw '-String is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Add = $(throw '-Add is a required parameter.') - ) - Write-Verbose "Add-Suffix -String $String -Add $Add" - [string]$ReturnValue = $String - if (-not (Compare-IsLast -String $String -EndsWith $Add)) - { - $ReturnValue = ($String + $Add) - Write-Verbose "[Success] 1 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - else - { - Write-Verbose "[OK] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -String $String already has suffix of -Add $Add" - } - - return $ReturnValue -} -export-modulemember -function Add-Suffix - -#----------------------------------------------------------------------- -# Compare-IsFirst [-String []] -# -# Example: .\Compare-IsFirst -String Hell -EndsWith H -# Result: false -# Example: .\Compare-IsFirst -String Hello -Add H -# Result: Hello -#----------------------------------------------------------------------- -function Compare-IsFirst -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$String = $(throw '-String is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$BeginsWith = $(throw '-BeginsWith is a required parameter.') - ) - - Write-Verbose "Compare-IsFirst -String $String -EndsWith $EndsWith" - [Boolean]$ReturnValue = $false - if($BeginsWith.Length -lt $String.Length) - { - $StringBeginning = $String.SubString(0, $BeginsWith.Length).ToLower() - if ($StringBeginning.ToLower().Equals($BeginsWith.ToLower())) - { - $ReturnValue = $true - } - Write-Verbose "[Success] 1 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - else - { - Write-Verbose "[OK] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - return $ReturnValue -} -export-modulemember -function Compare-IsFirst - -#----------------------------------------------------------------------- -# Compare-IsLast [-String []] -# -# Example: .\Compare-IsLast -String Hell -EndsWith H -# Result: false -# Example: .\Compare-IsLast -String Hello -Add H -# Result: Hello -#----------------------------------------------------------------------- -function Compare-IsLast -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$String = $(throw '-String is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$EndsWith = $(throw '-EndsWith is a required parameter.') - ) - Write-Verbose "Compare-IsLast -String $String -EndsWith $EndsWith" - [Boolean]$ReturnValue = $false - if($EndsWith.Length -lt $String.Length) - { - $StringEnding = $String.SubString(($String.Length - $EndsWith.Length), $EndsWith.Length) - if ($StringEnding.ToLower().Equals($EndsWith.ToLower())) - { - $ReturnValue = $true - } - Write-Verbose "[Success] 1 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - else - { - Write-Verbose "[OK] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - - return $ReturnValue -} -export-modulemember -function Compare-IsLast - -#----------------------------------------------------------------------- -# Compress-Path [-Path []] [-File []] -# -# Example: .\Compress-Path \\source\path \\destination\path\file.zip -#----------------------------------------------------------------------- -function Compress-Path -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path=$(throw '-Path is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$File=$(throw '-File is a required parameter.') - ) - Write-Verbose "Compress-Path -Path $Path -File $File" - New-Path -Path $Path - Remove-File $File - [Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem") - [System.IO.Compression.ZipFile]::CreateFromDirectory($Path, $File) -} -export-modulemember -function Compress-Path - -#----------------------------------------------------------------------- -# Convert-PathSafe [-Path []] -# -# Example: .\Convert-PathSafe -Path \\source\path -#----------------------------------------------------------------------- -function Convert-PathSafe -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.') - ) - Write-Verbose "Convert-PathSafe -Path $Path" - $Path = $Path.Trim() - $ReturnValue = $Path - $Path = Set-Unc -Path $Path - if(Test-Path -Path $Path) - { - $ReturnValue = Convert-Path -Path $Path - if (-not ($ReturnValue)) - { - $ReturnValue = $Path - Write-Verbose "[Warning] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path didnt convert." - } - } - else - { - Write-Verbose "[Error] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path does not exist." - } - return $ReturnValue -} -export-modulemember -function Convert-PathSafe - -#----------------------------------------------------------------------- -# Copy-Backup [-Path []] [-Destination []] -# -# Example: .\Copy-Backup -Path \\source\path -Destination \\destination\path -#----------------------------------------------------------------------- -function Copy-Backup -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Destination = $(throw '-Destination is a required parameter.') - ) - Write-Verbose "Copy-Backup -Path $Path -Destination $Destination" - $Path = Remove-Suffix -String $Path -Remove "\" - New-Path -Path $Destination - [String]$BackupPath=[string]::Format("{0}\{1}", $Destination, (Get-Date).ToString("yyyy-MM-dd")) - if($Path) - { - if(-not (Test-Path -Path $BackupPath -PathType Container)){ - New-Path -Path $BackupPath - } - Copy-Recurse -Path $Path -Destination $BackupPath - Write-Verbose "[Success] 1 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path to -Destination $BackupPath" - } - else - { - Write-Verbose "[Error] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path does not exist." - } -} -export-modulemember -function Copy-Backup - -#----------------------------------------------------------------------- -# Copy-File [-Path []] [-Destination []] -# -# Example: .\Copy-File -Path \\source\path\File.name -Destination \\destination\path -#----------------------------------------------------------------------- -function Copy-File -{ - param ( - [Parameter(Mandatory = $True)] - [string]$Path = $(throw '-Path is a required parameter.'), - [Parameter(Mandatory = $True)] - [string]$Destination = $(throw '-Destination is a required parameter.'), - [string[]]$Include = "*.*", - [string[]]$Exclude = "", - [bool]$Overwrite = $true - ) - Write-Verbose "Copy-File -Path $Path -Destination $Destination -Overwrite $Overwrite" - $Destination = Set-Unc -Path $Destination - if(Test-File -Path $Path) - { - New-Path -Path $Destination - $DestinationAbsolute = $Destination - if(Test-Folder -Path $Destination) - { - $DestinationAbsolute = Convert-PathSafe -Path $Destination - } - $DestinationPathFile = $DestinationAbsolute - $FolderArray = $Path.Split('\') - if($FolderArray.Count -gt 0) - { - $DestinationPathFile = Join-Path $DestinationAbsolute $FolderArray[$FolderArray.Count-1] - } - if((-not (Test-Path $DestinationPathFile -PathType Leaf)) -or ($Overwrite -eq $true)) - { - try{ - Copy-Item -Path $Path -Destination $DestinationAbsolute -Include $Include -Exclude $Exclude -Force - } - catch{ - Write-Verbose "[Error] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path does not exist." - } - } - Write-Verbose "[Success] 1 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path to -Destination $DestinationAbsolute" - } - else - { - Write-Verbose "[Error] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path does not exist." - } -} -export-modulemember -function Copy-File - -#----------------------------------------------------------------------- -# Copy-Recurse [-Source []] [-Destination []] -# [-Include [] [-Exclude []] -# -# Example: .\Copy-Recurse \\source\path \\destination\path -#----------------------------------------------------------------------- -function Copy-Recurse -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Source is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Destination = $(throw '-Destination is a required parameter.'), - [string[]]$Include = "*.*", - [string[]]$Exclude = "", - [Int32]$First = 1000, - [bool]$Overwrite=$True, - [bool]$Clean = $False - ) - Write-Verbose "Copy-Recurse -Path $Path -Destination $Destination -Include $Include -Exclude $Exclude -First $ -Overwrite $Overwrite -Clean $Clean" - $Affected = 0 - $Path = Set-Unc -Path $Path - if (Test-Path $Path) - { - $PathAbsolute = Convert-PathSafe -Path $Path - # Optionally Clean - if($Clean -eq $True) { Remove-Path -Path $Destination } - New-Path -Path $Destination - $DestinationAbsolute = $Destination - if(Test-Path $Destination) { $DestinationAbsolute=Convert-PathSafe -Path $Destination } - $Items = Get-ChildItem -Path $PathAbsolute -Recurse -Include $Include -Exclude $Exclude | where { ! $_.PSIsContainer } - ForEach ($Item in $Items) { - $PathArray = $PathAbsolute.Split('\') - $Folder = $DestinationAbsolute - for ($count=1; $count -lt $PathArray.length-1; $count++) { - $Subfolder = $PathArray[$count] - $Folder = Join-Path $Folder $Subfolder - if (($Folder.Length > 0) -and (-not (Test-Path $Folder))) { - Write-Verbose "New-Item -ItemType directory -Force -Path $Folder" - New-Item -ItemType directory -Force -Path $Folder - } - } - $DirName = $Item.DirectoryName - $Position = $DirName.IndexOf($PathAbsolute) - $PathSegment = $DirName.SubString($Position + $PathAbsolute.Length) - $NewPath = Join-Path $DestinationAbsolute $PathSegment - Copy-File -Path $Item.FullName -Destination $NewPath -Overwrite $Overwrite - $Affected = $Affected + 1 - } - Write-Verbose "[Success] $Affected items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - else - { - Write-Verbose "[Error] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path does not exist." - } -} -export-modulemember -function Copy-Recurse - -#----------------------------------------------------------------------- -# Expand-File [-Path []] [-File []] -# -# Example: .\Expand-File \\source\path\file.zip \\destination\path -#----------------------------------------------------------------------- -function Expand-File -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path=$(throw '-Path is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$File=$(throw '-File is a required parameter.') - ) - Write-Verbose "Expand-Zip -Path $Path -File $File" - New-Path -Path $Path - [Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem") - [System.IO.Compression.ZipFile]::ExtractToDirectory($File, $Path) -} -export-modulemember -function Expand-File - -#----------------------------------------------------------------------- -# Find-File [-Path []] [-File []] -# -# Example: .\Find-File \\source\path\file.zip \\destination\path -#----------------------------------------------------------------------- -function Find-File -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path=$(throw '-Path is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$File=$(throw '-File is a required parameter.'), - [Int32]$First=1 - ) - Write-Verbose "Find-Zip -Path $Path -File $File" - Get-Childitem -Path $Path -Include $File -Recurse| select -First $First -} -export-modulemember -function Find-File - -#----------------------------------------------------------------------- -# Get-AssemblyStrongName -# -# Example: Get-AssemblyStrongName -#----------------------------------------------------------------------- -function Get-AssemblyStrongName($assemblyPath) -{ - [System.Reflection.AssemblyName]::GetAssemblyName($assemblyPath).FullName -} -export-modulemember -function Get-AssemblyStrongName - -#----------------------------------------------------------------------- -# Get-CurrentLine -# -# Example: Get-CurrentLine -#----------------------------------------------------------------------- -function Get-CurrentLine { - $MyInvocation.ScriptLineNumber -} -export-modulemember -function Get-CurrentLine - -#----------------------------------------------------------------------- -# Get-CurrentFile -# -# Example: Get-CurrentFile -#----------------------------------------------------------------------- -function Get-CurrentFile { - $MyInvocation.ScriptName -} -export-modulemember -function Get-CurrentFile - -#----------------------------------------------------------------------- -# Get-FilesByString -# -# Example: Get-FilesByString -#----------------------------------------------------------------------- -function Get-FilesByString { - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.'), - [string]$String = $(throw '-String is a required parameter.'), - [string[]]$Include = ("*.*"), - [string[]]$Exclude = "" - ) - Write-Verbose "Get-FilesByString -Path $Path -String $String -Include $Include -Exclude $Exclude" - $Path = Set-Unc -Path $Path - - $ReturnData = Get-Childitem -Path $Path -Include $Include -Exclude $Exclude -Recurse | Select-String -pattern $String | group path | select name - - return $ReturnData -} -export-modulemember -function Get-FilesByString - -#----------------------------------------------------------------------- -# Get-SystemFolders -# -# Example: Get-SystemFolder -#----------------------------------------------------------------------- -function Get-SystemFolders -{ - param ( - ) - Write-Verbose "Get-SystemFolders" - $SpecialFolders = @{} - $names = [Environment+SpecialFolder]::GetNames([Environment+SpecialFolder]) - foreach($name in $names) - { - if($path = [Environment]::GetFolderPath($name)){ - $SpecialFolders[$name] = $path - } - else - { - Write-Verbose "[Warning] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - } - return $SpecialFolders -} -export-modulemember -function Get-SystemFolders - -#----------------------------------------------------------------------- -# Get-SystemFolder [-Name []] -# Keys: Desktop,Programs,MyDocuments,Personal,Favorites,Startup,Recent,SendTo,StartMenu,MyMusic,MyVideos,DesktopDirectory,NetworkShortcuts,Fonts -# Templates,CommonStartMenu,CommonPrograms,CommonStartup,CommonDesktopDirectory,ApplicationData,PrinterShortcuts,LocalApplicationData,InternetCache -# Cookies,History,CommonApplicationData,Windows,System,ProgramFiles,MyPictures,UserProfile,SystemX86,ProgramFilesX86 -# CommonProgramFiles,CommonProgramFilesX86,CommonTemplates,CommonDocuments,CommonAdminTools,AdminTools,CommonMusic,CommonPictures,CommonVideos,ResourcesCDBurning -# Example: Get-SystemFolder -Name 'UserProfile' -#----------------------------------------------------------------------- -function Get-SystemFolder -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [String]$Name=$(throw '-Folder is a required parameter.') - ) - Write-Verbose "Get-SystemFolder -Folder $Folder" - if($path = [Environment]::GetFolderPath($name)){ - $Folder = $path - } - else - { - Write-Verbose "[Warning] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - return $Folder -} -export-modulemember -function Get-SystemFolder - -#----------------------------------------------------------------------- -# Move-Path [-Path []] [-Destination []] -# [-Exclude []] -# -# Example: .\Move-Path -Path \\source\path -#----------------------------------------------------------------------- -function Move-Path -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Destination = $(throw '-Destination is a required parameter.'), - [string]$Exclude = "" - ) - Write-Verbose "Move-Path -Path $Path -Destination $Destination" - $Path = Remove-Suffix -String $Path -Remove "\" - if (test-folder -Path $Path) - { - Remove-Path -Destination $Destination - New-Path -Destination $Destination - Copy-Recurse -Path $Path -Destination $Destination -Exclude $Exclude - Remove-Path -Destination $Path - Write-Verbose "[Success] 1 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path moved to -Destination $Destination" - } - else - { - Write-Verbose "[Error] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path does not exist." - } -} -export-modulemember -function Move-Path - -#----------------------------------------------------------------------- -# New-Path [-Path []] -# -# Example: .\New-Path \\source\path -#----------------------------------------------------------------------- -function New-Path -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.'), - [bool]$Clean=$false - ) - Write-Verbose "New-Path -Path $Path" - [String]$Folder = "" - $Path = Remove-Suffix -String $Path -Remove "\" - if ($Clean) {Remove-Path -Path $Path} - if (-not (test-path $Path)) { - if (Test-Unc $Path) - { - $PathArray = $Path.Split('\') - foreach($item in $PathArray) - { - if($item.Length -gt 0) - { - if($Folder.Length -lt 1) - { - $Folder = "\\$item" - } - else - { - $Folder = "$Folder\$item" - if (-not (Test-Path $Folder)) { - New-Item -ItemType directory -Path $Folder -Force - } - } - } - } - } - else - { - New-Item -ItemType directory -Path $Path -Force - } - } -} -export-modulemember -function New-Path - -#----------------------------------------------------------------------- -# Redo-Path [-Path []] [-Destination []] -# [-Exclude []] -# -# Example: .\Redo-Path -Path \\source\path -#----------------------------------------------------------------------- -function Redo-Path -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Destination = $(throw '-Destination is a required parameter.'), - [string]$Exclude = "" - ) - Write-Verbose "Redo-Path -Path $Path -Destination $Destination" - $Path = Remove-Suffix -String $Path -Remove "\" - Remove-Path -Destination $Path - New-Path -Destination $Path - Copy-Recurse -Path $Path -Destination $Destination -Exclude $Exclude - Write-Verbose "[Success] 1 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path to -Destination $Destination" -} -export-modulemember -function Redo-Path - -#----------------------------------------------------------------------- -# Remove-File [-File []] -# -# Example: .\Remove-File \\source\path\file.txt -#----------------------------------------------------------------------- -function Remove-File -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path=$(throw '-Path is a required parameter.') - ) - Write-Verbose "Remove-File -Path $Path" - if (Test-File -Path $Path) { - Remove-Item -Path $Path -Force - Write-Verbose "[Success] 1 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path removed." - } - else - { - Write-Verbose "[Error] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path does not exist." - } -} -export-modulemember -function Remove-File - -#----------------------------------------------------------------------- -# Remove-Path [-Path []] -# [-Include [] [-Exclude []] -# -# Example: .\Remove-Path -Path \\source\path -#----------------------------------------------------------------------- -function Remove-Path -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.'), - [string[]]$Include = "*.*", - [string[]]$Exclude = "", - [Int16]$Retention = 1, - [Int32]$First = 1000 - ) - Write-Verbose "Remove-Path -Path $Path -Include $Include -Exclude $Exclude -Retention $Retention -First $First" - $Path = Remove-Suffix -String $Path -Remove "\" - if (test-folder -Path $Path) { - $ErrorActionPreferenceBackup = $ErrorActionPreference - $ErrorActionPreference = 'SilentlyContinue' - Get-ChildItem -Path $Path -Include $Include -Exclude $Exclude -Recurse | Where-Object {($_.PSIsContainer) -and ($_.lastwritetime -le (get-date).addDays(($Retention*-1)))} | select -First $First | Remove-Item -Force -Recurse - Remove-Item $Path -Recurse -Force - $ErrorActionPreference = $ErrorActionPreferenceBackup - Write-Verbose "[Success] 1 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path removed." - } - else - { - Write-Verbose "[Warning] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } -} -export-modulemember -function Remove-Path - -#----------------------------------------------------------------------- -# Remove-Subfolders [-Path []] -# [-Include [] [-Exclude []] -# -# Example: .\Remove-Subfolders -Path \\source\path -#----------------------------------------------------------------------- -function Remove-Subfolders -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Subfolder = $(throw '-Subfolder is a required parameter.'), - [Int32]$First = 1000 - ) - Write-Verbose "Remove-Subfolders -Path $Path -Subfolder $Subfolder -First $First" - $Path = Set-Unc -Path $Path - - if (test-folder -Path $Path) { - $Affected = 0 - if(Test-Unc -Path $Path) - { - $Folders=Get-ChildItem $Path -Recurse | Where-Object {($_.Name -EQ $Subfolder) -and ($_.PSIsContainer)} | select -First $First - foreach ($Folder in $Folders) { - if ($Folder.FullName) - { - [String]$FolderToRemove=Add-Suffix -String $Folder.FullName -Add "\" - Remove-Path -Path $FolderToRemove - $Affected = $Affected + 1 - } - } - } - else - { - $Remove = Add-Suffix -String $Path -Add '\' - $Remove = Add-Suffix -String $Remove -Add $Subfolder - Remove-Path -Path $Remove - $Affected = 1 - } - Write-Verbose "[Success] $Affected items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path -Subfolder $Subfolder removed." - } - else - { - Write-Verbose "[Warning] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path does not exist." - } -} -export-modulemember -function Remove-Subfolders - -#----------------------------------------------------------------------- -# Remove-Recurse [-Source []] [-Destination []] -# [-Include [] [-Exclude []] -# -# Example: .\Remove-Recurse \\source\path \\destination\path -#----------------------------------------------------------------------- -function Remove-Recurse -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Source is a required parameter.'), - [string[]]$Include = "*.*", - [string[]]$Exclude = "", - [Int32]$First = 1000 - ) - Write-Verbose "Remove-Recurse -Path $Path -Include $Include -Exclude $Exclude" - $Path = Remove-Suffix -String $Path -Remove "\" - if (Test-Path $Path) - { - $PathAbsolute = Convert-PathSafe -Path $Path - $Items = Get-ChildItem -Path $PathAbsolute -Recurse -Include $Include -Exclude $Exclude | where { ! $_.PSIsContainer } - $Affected = 0 - ForEach ($Item in $Items) { - Remove-File -Path $Item - $Affected = $Affected + 1 - } - Write-Verbose "[Success] $Affected items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path." - } - else - { - Write-Verbose "[Warning] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path does not exist." - } -} -export-modulemember -function Remove-Recurse - -#----------------------------------------------------------------------- -# Remove-Prefix [-String []] -# -# Example: .\Remove-Prefix -String Hell -Remove o -# Result: Hello -#----------------------------------------------------------------------- -function Remove-Prefix -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$String = $(throw '-String is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Remove = $(throw '-Remove is a required parameter.') - ) - Write-Verbose "Remove-Prefix -String $String -Remove $Remove" - [string]$ReturnValue = $String - if (Compare-IsFirst -String $String -BeginsWith $Remove) - { - $ReturnValue = $String.Substring($Remove.Length, $String.Length - $Remove.Length) - Write-Verbose "[Success] 1 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - else - { - Write-Verbose "[OK] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -String $String already has prefix of -Remove $Remove" - } - - return $ReturnValue -} -export-modulemember -function Remove-Prefix - -#----------------------------------------------------------------------- -# Remove-Suffix [-String []] -# -# Example: .\Remove-Suffix -String Hell -Remove o -# Result: Hello -#----------------------------------------------------------------------- -function Remove-Suffix -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$String = $(throw '-String is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Remove = $(throw '-Remove is a required parameter.') - ) - Write-Verbose "Remove-Suffix -String $String -Remove $Remove" - [string]$ReturnValue = $String - if($String) - { - if (Compare-IsLast -String $String -EndsWith $Remove) - { - $ReturnValue = $ReturnValue.Substring(0, $String.Length - $Remove.Length) - } - Write-Verbose "[Success] 1 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - else - { - Write-Verbose "[OK] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -String $String already has suffix of -Remove $Remove" - } - - return $ReturnValue -} -export-modulemember -function Remove-Suffix - -#----------------------------------------------------------------------- -# Remove-Element [-Path []] -# -# Example: .\Remove-Element -Value "" -# -XPath "//msb:None/msb:Generator" -# -Namespace @{msb = "http://schemas.microsoft.com/developer/msbuild/2003"} -# -# Called: $XMLValue = [xml](Get-Content $path) -# $Namespace = @{msb = 'http://schemas.microsoft.com/developer/msbuild/2003'} -# Remove-Element $XMLValue -XPath '//msb:None/msb:Generator' -Namespace $Namespace -# $proj.Save($path) -#----------------------------------------------------------------------- -function Remove-Element -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [xml]$Value=$(throw '-Value is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [String]$XPath=$(throw '-Value is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [String]$Namespace=$(throw '-Value is a required parameter.') - ) - Write-Verbose ".\Remove-Element -Value $Value -XPath $XPath -Namespace $Namespace -SingleNode" - $nodes = @(Select-Xml $XPath $Value -Namespace $Namespace | Foreach {$_.Node}) - if (!$nodes) { Write-Verbose "RemoveElement: XPath $XPath not found" } - if ($singleNode -and ($nodes.Count -gt 1)) { - throw "XPath $XPath found multiple nodes" - } - $Count = 0 - foreach ($node in $nodes) - { - $parentNode = $node.ParentNode - [void]$parentNode.RemoveChild($node) - $Count = $Count + 1 - } - Write-Verbose "[Success] $Count items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." -} -export-modulemember -function Remove-Element - -#----------------------------------------------------------------------- -# Remove-ContentsByTagContains [-Path []] -# [-Open [] [-Close []] -# -# Example: .\Remove-ContentsByTagContains \\source\path \\destination\path -# GlobalSection(TeamFoundationVersionControl) = preSolution -# EndGlobalSection -#----------------------------------------------------------------------- -function Remove-ContentsByTag -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Open = $(throw '-Open is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Close = $(throw '-Close is a required parameter.'), - [string[]]$Include = "*.*", - [string[]]$Exclude = "", - [Int32]$First = 100 - ) - Write-Verbose "Remove-ContentsByTag -Path $Path -Open $Open -Close $Close -Include $Include -Exclude $Exclude -First $First" - $Path = Remove-Suffix -String $Path -Remove "\" - if (Test-Path $Path) - { - $Open = $Open.Trim() - $Close = $Close.Trim() - $Files = Get-Childitem -Path $Path -Include $Include -Exclude $Exclude -Recurse -Force | select -First $First - ForEach ($File in $Files) - { - [Int32]$OpenIndex = -1 - [Int32]$CloseIndex = -1 - $Content=Get-Content $File.PSPath - # Search for matches - For([Int32]$Count = 0; $Count -lt $Content.Length; $Count++) - { - $CurrentLine = $Content[$Count].Trim() - If(($OpenIndex -eq -1) -and ($CurrentLine -eq $Open)) - { - $OpenIndex = $Count - } - ElseIf(($OpenIndex -gt -1) -and ($CurrentLine -eq $Close)) - { - $CloseIndex = $Count - Break - } - } - # Evaluate search - If(($OpenIndex -gt -1) -and ($OpenIndex -lt $CloseIndex)) - { - # Match Found Remove block. - $NewContent = ($Content | Select -First $OpenIndex) + ($Content | select -Last ($Content.Length - $CloseIndex - 1)) - } - else - { - # No Match Found - $NewContent = $Content - } - Set-Content $File.PSPath -Value $NewContent - } - Write-Verbose "[Success] 1 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - else - { - Write-Verbose "[OK] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } -} -export-modulemember -function Remove-ContentsByTag - -#----------------------------------------------------------------------- -# Remove-ContentsByTagContains [-Path []] -# [-Open [] [-Close []] -# -# Example: .\Remove-ContentsByTagContains \\source\path \\destination\path -# GlobalSection(TeamFoundationVersionControl) = preSolution -# EndGlobalSection -#----------------------------------------------------------------------- -function Remove-ContentsByTagContains -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Open = $(throw '-Open is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Close = $(throw '-Close is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Contains = $(throw '-Contains is a required parameter.'), - [string[]]$Include = "*.*", - [string[]]$Exclude = "", - [Int32]$First = 100 - ) - Write-Verbose "Remove-ContentsByTagContains -Path $Path -Open $Open -Close $Close -Contains $Contains -Include $Include -Exclude $Exclude -First $First" - $Path = Remove-Suffix -String $Path -Remove "\" - if (Test-Path $Path) - { - $Open = $Open.Trim() - $Close = $Close.Trim() - $Contains = $Contains.Trim() - - $Files = Get-Childitem -Path $Path -Include $Include -Exclude $Exclude -Recurse -Force | select -First $First - ForEach ($File in $Files) - { - [Int32]$OpenIndex = -1 - [Int32]$ContainsIndex = -1 - [Int32]$CloseIndex = -1 - $Content=Get-Content $File.PSPath - # Search for matches - For([Int32]$Count = 0; $Count -lt $Content.Length; $Count++) - { - $CurrentLine = $Content[$Count].Trim() - If ($CurrentLine -like "*$Open*") - { - If($OpenIndex -gt -1) - { - # Fail: Block did not contain -Content and/or -Open was found before -Close. Reset for next open tag match. - $ContainsIndex = -1 - $CloseIndex = -1 - } - $OpenIndex = $Count - }ElseIf($OpenIndex -gt -1) - { - If($CurrentLine -like "*$Contains*") - { - $ContainsIndex = $Count - }ElseIf(($ContainsIndex -gt -1) -and ($CurrentLine -like "*$Close*")) - { - # Success, block starts with -Open, ends with -Close and includes -Contains - $CloseIndex = $Count - Break - } - } - } - # Any matches? - If(($OpenIndex -gt -1) -and ($ContainsIndex -gt $OpenIndex) -and ($CloseIndex -gt $ContainsIndex)) - { - If($CloseIndex -eq ($OpenIndex + 2)) - { - # Match Found with single element. Remove Block. - $NewContent = ($Content | Select -First $OpenIndex) + ($Content | select -Last ($Content.Length - $CloseIndex - 1)) - } - Else - { - # Match Found with multiple elements. Remove Line Only. - $NewContent = ($Content | Select -First $ContainsIndex) + ($Content | select -Last ($Content.Length - $ContainsIndex -1)) - } - } - else - { - # No Match Found - $NewContent = $Content - } - Set-Content $File.PSPath -Value $NewContent - } - Write-Verbose "[Success] 1 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - else - { - Write-Verbose "[OK] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } -} -export-modulemember -function Remove-ContentsByTagContains - -#----------------------------------------------------------------------- -# Rename-File [-File []] -# -# Example: .\Rename-File -Path ($StagingZipPath + 'root.vstbak') -NewName root.vstemplate -Force -#----------------------------------------------------------------------- -function Rename-File -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path=$(throw '-Path is a required parameter.'), - [string]$NewName=$(throw '-NewName is a required parameter.') - ) - Write-Verbose "Rename-File -Path $Path -NewName $NewName" - if (Test-File -Path $Path) { - Rename-Item -Path $Path -NewName $NewName -Force - Write-Verbose "[Success] 1 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path removed." - } - else - { - Write-Verbose "[Error] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path does not exist." - } -} -export-modulemember -function Rename-File - -#----------------------------------------------------------------------- -# Set-ReadOnly [-Path []] [-ReadOnly []] -# -# Example: .\Set-ReadOnly -Path \\source\path\File.name -ReadOnly $False -#----------------------------------------------------------------------- -function Set-ReadOnly -{ - param ( - [Parameter(Mandatory = $True)] - [string]$Path = $(throw '-Path is a required parameter.'), - [bool]$ReadOnly = $True, - [string]$ErrorPreference = 'SilentlyContinue' - ) - Write-Verbose "Set-ReadOnly -Path $Path -ReadOnly $ReadOnly -ErrorPreference $ErrorPreference" - $Path = Remove-Suffix -String $Path -Remove "\" - if(test-path $Path) - { - $PathAbsolute = Convert-PathSafe -Path $Path - if (Test-Path $PathAbsolute -PathType Leaf) - { - $ErrorActionPreferenceBackup = $ErrorActionPreference - $ErrorActionPreference = $ErrorPreference - Set-ItemProperty $PathAbsolute -name IsReadOnly -value $ReadOnly -Force - $ErrorActionPreference = $ErrorActionPreferenceBackup - } - Write-Verbose "[Success] 1 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path set." - } - else - { - Write-Verbose "[Error] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path does not exist." - } -} -export-modulemember -function Set-ReadOnly - -#----------------------------------------------------------------------- -# Set-SystemFolderDrives -# -# Example: Set-SystemFolderDrives -#----------------------------------------------------------------------- -function Set-SystemFolderDrives -{ - param ( - ) - Write-Verbose "Set-SystemFolderDrives" - - $SpecialFolders = @{} - $names = [Environment+SpecialFolder]::GetNames([Environment+SpecialFolder]) - foreach($name in $names) - { - if($path = [Environment]::GetFolderPath($name)){ - $SpecialFolders[$name] = $path - New-PSDrive -Name $name -PSProvider FileSystem -Root $path - } - } - # #TBD: Find the 10 Largest Files in the Documents Folder - # gci Personal: -Recurse -Force -ea SilentlyContinue | - # Sort-Object -Property Length -Descending | - # Select-Object -First 10 | - # Format-Table -AutoSize -Wrap -Property ` - # Length,LastWriteTime,FullName - return $SpecialFolders -} -export-modulemember -function Set-SystemFolderDrives - -#----------------------------------------------------------------------- -# Test-File [-Path []] -# -# Example: .\Test-File -Path \\source\path -#----------------------------------------------------------------------- -function Test-File -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.') - ) - Write-Verbose "Test-File -Path $Path" - [bool]$ReturnValue = $false - if(Test-Path -Path $Path -PathType Leaf) - { - $ReturnValue = $true - } - else - { - Write-Verbose "[Warning] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path does not exist or is not a File." - } - return $ReturnValue -} -export-modulemember -function Test-File - -#----------------------------------------------------------------------- -# Test-Folder [-Path []] -# -# -# Example: .\Test-Folder -Path \\source\path -#----------------------------------------------------------------------- -function Test-Folder -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.') - ) - Write-Verbose "Test-Folder -Path $Path" - [bool]$ReturnValue = $false - if(test-path -Path $Path -PathType Container) - { - $ReturnValue = $true - } - else - { - Write-Verbose "[Warning] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path does not exist or is not a Folder." - } - return $ReturnValue -} -export-modulemember -function Test-Folder - -#----------------------------------------------------------------------- -# Test-PathEmpty [-Path []] -# -# Example: .\Test-PathEmpty -Path \\source\path -#----------------------------------------------------------------------- -function Test-PathEmpty -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.') - ) - Write-Verbose "Test-PathEmpty -Path $Path" - [bool]$ReturnValue = $false - if((Get-ChildItem $Path -force | Select-Object -First 1 | Measure-Object).Count -eq 0) - { - $ReturnValue = $true - } - else - { - Write-Verbose "[Error] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path does not exist." - } - return $ReturnValue -} -export-modulemember -function Test-PathEmpty - -#----------------------------------------------------------------------- -# Set-Unc [-Path []] -# -# Example: .\Set-Unc -Path \\source\path -#----------------------------------------------------------------------- -function Set-Unc -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.') - ) - Write-Verbose "Set-Unc -Path $Path" - $Path = $Path.Trim() - $Path = Remove-Suffix -String $Path -Remove '\' - if(-not ($Path.Contains(':\') -or $Path.Contains('.\') -or (Compare-IsFirst -String $Path -BeginsWith '\'))) - { - $ReturnValue = Add-Prefix -String $Path -Add '\\' - } - else - { - $ReturnValue = $Path - Write-Verbose "[OK] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine). -Path $Path already a UNC, drive letter, absolute or relative path." - } - return $ReturnValue -} -export-modulemember -function Set-Unc - -#----------------------------------------------------------------------- -# Test-Unc [-Path []] -# -# -# Example: .\Test-Unc -Path \\source\path -#----------------------------------------------------------------------- -function Test-Unc -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.') - ) - Write-Verbose "Test-Unc -Path $Path" - [bool]$ReturnValue = $false - if(($Path.Contains('\\')) -and (-not ($Path.Contains(':\')))) - { - $ReturnValue = $true - } - else - { - Write-Verbose "[OK] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - return $ReturnValue -} -export-modulemember -function Test-Unc - -#----------------------------------------------------------------------- -# Update-LineByContains [-Path []] -# [-Contains [] [-Close []] -# -# Example: .\Update-LineByContains -Path \\source\path -Include AssemblyInfo.cs -Contains 'AssemblyVersion(' -Line '[assembly: AssemblyVersion("5.20.07")]' -#----------------------------------------------------------------------- -function Update-LineByContains -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Contains = $(throw '-Contains is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Line = $(throw '-Line is a required parameter.'), - [string[]]$Include = "*.*", - [string[]]$Exclude = "", - [Int32]$First = 100 - ) - Write-Verbose "Update-LineByContains -Path $Path -Contains $Contains -Line $Line -Include $Include -Exclude $Exclude -First $First" - $Path = Remove-Suffix -String $Path -Remove "\" - if (Test-Path $Path) - { - $Contains = $Contains.Trim() - $Count = 0 - $Files = Get-Childitem -Path $Path -Include $Include -Exclude $Exclude -Recurse -Force | select -First $First - ForEach ($File in $Files) - { - [Int32]$ContainsIndex = -1 - $Affected = 0 - $Content=Get-Content $File.PSPath - # Search for matches - For([Int32]$Count = 0; $Count -lt $Content.Length; $Count++) - { - $CurrentLine = $Content[$Count].Trim() - If(($ContainsIndex -eq -1) -and ($CurrentLine -eq $Contains)) - { - $ContainsIndex = $Count - Break - } - } - # Evaluate search - If($ContainsIndex -gt -1) - { - # Select before line, add -Line, select after line - $NewContent = (($Content | Select -First $ContainsIndex) + ($Line + [Environment]::NewLine) + ($Content | select -Last ($Content.Length - $ContainsIndex -1))) - } - else - { - # No Match Found - $NewContent = $Content - } - Set-Content $File.PSPath -Value $NewContent - $Affected = $Count - } - Write-Verbose "[Success] $Count items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - else - { - Write-Verbose "[OK] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } -} -export-modulemember -function Update-LineByContains - -#----------------------------------------------------------------------- -# Update-ContentsByTag [-Path []] -# [-Open [] [-Close []] -# -# Example: .\Update-ContentsByTag -Path $Path -Include *.sln -Open "GlobalSection(TeamFoundationVersionControl) = preSolution" -Close "EndGlobalSection" -#----------------------------------------------------------------------- -function Update-ContentsByTag -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Open = $(throw '-Open is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Close = $(throw '-Close is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Value = $(throw '-Value is a required parameter.'), - [string[]]$Include = "*.*", - [string[]]$Exclude = "", - [Int32]$First = 100 - ) - Write-Verbose "Update-ContentsByTag -Path $Path -Open $Open -Close $Close -Include $Include -Exclude $Exclude -First $First" - $Path = Remove-Suffix -String $Path -Remove "\" - [String]$PaddingLeft = ' ' - if (Test-Path $Path) - { - $Open = $Open.Trim() - $Close = $Close.Trim() - $Affected = 0 - $Files = Get-Childitem -Path $Path -Include $Include -Exclude $Exclude -Recurse -Force | select -First $First - ForEach ($File in $Files) - { - [Int32]$OpenIndex = -1 - [Int32]$CloseIndex = -1 - [String]$NewValue = "" - $Content=Get-Content $File.PSPath - # Search for matches - For([Int32]$Count = 0; $Count -lt $Content.Length; $Count++) - { - $CurrentLine = $Content[$Count].Trim() - If(($OpenIndex -eq -1) -and ($CurrentLine -like "*$Open*")) - { - $OpenIndex = $Count - } - If(($OpenIndex -gt -1) -and ($CurrentLine -like "*$Close*")) - { - $CloseIndex = $Count - Break - } - } - # Evaluate search - If(($OpenIndex -gt -1) -and ($OpenIndex -le $CloseIndex)) - { - if($OpenIndex -eq $CloseIndex) - { - # Open/Close on same line, rebuild the line with new contents - $NewValue = ($Open + $Value + $Close) - } - else - { - $NewValue = $Value - } - # Update content - $NewContent = ($Content | Select -First ($OpenIndex)) + ($PaddingLeft + $NewValue) + ($Content | select -Last ($Content.Length - $CloseIndex - 1)) - $Affected = 1 - } - else - { - # No Match Found - $NewContent = $Content - } - Set-Content $File.PSPath -Value $NewContent - } - Write-Verbose "[Success] $Affected items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - else - { - Write-Verbose "[OK] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } -} -export-modulemember -function Update-ContentsByTag - -#----------------------------------------------------------------------- -# Update-Text [-Path []] -# [-Include [] [-Exclude []] -# -# Example: .\Update-Text -Path \\source\path -Include *.cs -Old "Use gotos" -New "Point at people who use gotos" -#----------------------------------------------------------------------- -function Update-Text -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Old = $(throw '-Old is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$New = $(throw '-New is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string[]]$Include = $(throw '-Include is a required parameter.'), - [string[]]$Exclude = "", - [Int32]$First = 100 - ) - Write-Verbose "Update-Text -Path $Path -Old $Old -New $New -Include $Include -Exclude $Exclude -First $First" - $Path = Remove-Suffix -String $Path -Remove "\" - $Count = 0 - if (Test-Path $Path) - { - $ConfigFiles=Get-Childitem $Path -Include $Include -Exclude $Exclude -Recurse -Force | select -First $First - foreach ($Item in $ConfigFiles) - { - Set-ReadOnly -Path $Item.PSPath -ReadOnly $false - (Get-Content $Item.PSPath) | - Foreach-Object {$_.Replace($Old, $New) - } | - Set-Content $Item.PSPath -force - $Count = $Count + 1 - } - Write-Verbose "[Success] $Count items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - else - { - Write-Verbose "[OK] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } -} -export-modulemember -function Update-Text - -#----------------------------------------------------------------------- -# Update-TextByContains [-Path []] -# [-Contains [] [-Close []] -# -# Example: .\Update-TextByContains -Path \\source\path -Include AssemblyInfo.cs -Contains 'AssemblyVersion(' -Line '[assembly: AssemblyVersion("5.20.07")]' -#----------------------------------------------------------------------- -function Update-TextByContains -{ - param ( - [string]$Path = $(throw '-Path is a required parameter.'), - [string]$Contains = $(throw '-Contains is a required parameter.'), - [string]$Old = $(throw '-Old is a required parameter.'), - [string]$New = $(throw '-New is a required parameter.'), - [string[]]$Include = "*.*", - [string[]]$Exclude = "", - [Int32]$First = 100 - ) - Write-Verbose "Update-TextByContains -Path $Path -Contains $Contains -Old $Old -New $New -Include $Include -Exclude $Exclude -First $First" - $Path = Remove-Suffix -String $Path -Remove "\" - if (Test-Path $Path) - { - $Contains = $Contains.Trim() - $Count = 0 - $Files = Get-Childitem -Path $Path -Include $Include -Exclude $Exclude -Recurse -Force | select -First $First - ForEach ($File in $Files) - { - [Int32]$FoundIndex = -1 - [String]$FoundLine = '' - $Affected = 0 - $Content=Get-Content $File.PSPath - # Search for matches - For([Int32]$Count = 0; $Count -lt $Content.Length; $Count++) - { - $CurrentLine = $Content[$Count].Trim() - If(($FoundIndex -eq -1) -and ($CurrentLine.ToLowerInvariant().Contains($Contains.ToLowerInvariant()))) - { - $FoundIndex = $Count - $FoundLine = $CurrentLine - Break - } - } - # Evaluate search - If($FoundIndex -gt -1) - { - # Replace text inside of line - $NewLine = $FoundLine.Replace($Old, $New) - # Select before line, add $NewLine, select after line - $NewContent = (($Content | Select -First $FoundIndex) + ($NewLine + [Environment]::NewLine) + ($Content | select -Last ($Content.Length - $FoundIndex -1))) - } - else - { - # No Match Found - $NewContent = $Content - } - Set-Content $File.PSPath -Value $NewContent - $Affected = $Count - } - Write-Verbose "[Success] $Count items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - else - { - Write-Verbose "[OK] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } -} -export-modulemember -function Update-TextByContains - -#----------------------------------------------------------------------- -# Update-TextByTable [-Path []] [-Replace []] -# [-Include [] [-Exclude []] -# -# Example: .\Update-TextByTable -Path \\source\path -Include *.cs -# -Replace @{'Old1' = 'New1' -# 'Old2' = 'New2' -# 'Old3' = 'New3'} -#----------------------------------------------------------------------- -function Update-TextByTable -{ - param ( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string]$Path = $(throw '-Path is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [hashtable]$Replace = $(throw '-Replace is a required parameter.'), - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string[]]$Include = $(throw '-Include is a required parameter.'), - [string[]]$Exclude = "", - [Int32]$First = 100 - ) - Write-Verbose "Update-Text -Path $Path -Old $Old -New $New -Include $Include -Exclude $Exclude -First $First" - $Path = Remove-Suffix -String $Path -Remove "\" - if (Test-Path $Path) - { - Write-Verbose "Get-Childitem $Path -Include $Include -Exclude $Exclude -Recurse -Force | select -First $First" - $ConfigFiles=Get-Childitem $Path -Include $Include -Exclude $Exclude -Recurse -Force | select -First $First - Write-Verbose "ConfigFiles: $ConfigFiles" - $Count = 0 - foreach ($Item in $ConfigFiles) - { - Write-Verbose "Get-Content $Item.PSPath" - $fileLines = Get-Content $Item.PSPath - Write-Verbose "fileLines: $fileLines" - if($fileLines) - { - foreach($replaceItem in $Replace.GetEnumerator()) { - Write-Verbose "$fileLines.Replace($replaceItem.Key, $replaceItem.Value)" - $fileLines = $fileLines.Replace($replaceItem.Key, $replaceItem.Value) - } - Write-Verbose "Set-Content -Path $Item.PSPath -Value $fileLines -force" - Set-Content -Path $Item.PSPath -Value $fileLines -force - } - $Count = $Count + 1 - } - Write-Verbose "[Success] $Count items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } - else - { - Write-Verbose "[OK] 0 items affected. $(Get-CurrentFile) at $(Get-CurrentLine)." - } -} -export-modulemember -function Update-TextByTable \ No newline at end of file diff --git a/.azuredevops/steps/api-deploy-steps.yml b/.azuredevops/steps/api-deploy-steps.yml deleted file mode 100644 index e53e07c..0000000 --- a/.azuredevops/steps/api-deploy-steps.yml +++ /dev/null @@ -1,35 +0,0 @@ -parameters: - - name: subscriptionId - type: string - default: "00000000-0000-0000-0000-000000000000" - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - - name: artifactsPath - type: string - default: "." - - name: archiveFile - type: string - default: "*.zip" - - name: apiName - type: string - default: "web-PRODUCT-ENVIRONMENT-001" - -steps: - - task: DownloadBuildArtifacts@0 - inputs: - buildType: 'current' - downloadType: 'single' - artifactName: 'drop' - downloadPath: '${{ parameters.artifactsPath }}' - - - task: AzureRmWebAppDeployment@4 - displayName: 'deploy ${{ parameters.apiName }}' - inputs: - ConnectionType: 'AzureRM' - ConnectedServiceName: ${{ parameters.subscriptionService }} - subscriptionName: ${{ parameters.subscriptionId }} - appType: 'webApp' - WebAppName: '${{ parameters.apiName }}' - RemoveAdditionalFilesFlag: true - packageForLinux: '${{ parameters.artifactsPath }}/**/${{ parameters.archiveFile }}' \ No newline at end of file diff --git a/.azuredevops/steps/api-infrastructure-steps.yml b/.azuredevops/steps/api-infrastructure-steps.yml deleted file mode 100644 index d91313a..0000000 --- a/.azuredevops/steps/api-infrastructure-steps.yml +++ /dev/null @@ -1,95 +0,0 @@ -parameters: - - name: armPath - type: string - default: "." - - name: subscriptionId - type: string - default: "00000000-0000-0000-0000-000000000000" - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - - name: rgName - type: string - default: 'rg-PRODUCT-001' - - name: rgLocation - type: string - default: "West US 3" - - name: environmentApp - type: string - default: 'Development' - - name: planName - type: string - default: "plan-api-PRODUCT-ENVIRONMENT-001" - - name: planResourceGroupName - type: string - default: "plan-api-PRODUCT-ENVIRONMENT-001" - - name: planSku - type: string - default: "F1" - - name: planCapacity - type: string - default: "0" - - name: apiName - type: string - default: "api-PRODUCT-ENVIRONMENT-001" - -steps: - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.planName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.planResourceGroupName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/plan-appplan.json" - csmParametersFile: "${{ parameters.armPath }}/plan-appplan.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.planName }}" -sku "${{ parameters.planSku }}" -location "${{ parameters.rgLocation }}" -skuCapacity "${{ parameters.planCapacity }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.planName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.planResourceGroupName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/plan-appplan.json" - csmParametersFile: "${{ parameters.armPath }}/plan-appplan.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.planName }}" -sku "${{ parameters.planSku }}" -location "${{ parameters.rgLocation }}" -skuCapacity "${{ parameters.planCapacity }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.apiName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/api-apiapp.json" - csmParametersFile: "${{ parameters.armPath }}/api-apiapp.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.apiName }}" -location "${{ parameters.rgLocation }}" -planName "${{ parameters.planName }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.apiName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/api-apiapp.json" - csmParametersFile: "${{ parameters.armPath }}/api-apiapp.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.apiName }}" -location "${{ parameters.rgLocation }}" -planName "${{ parameters.planName }}" \ No newline at end of file diff --git a/.azuredevops/steps/api-settings-steps.yml b/.azuredevops/steps/api-settings-steps.yml deleted file mode 100644 index 9216ab1..0000000 --- a/.azuredevops/steps/api-settings-steps.yml +++ /dev/null @@ -1,45 +0,0 @@ -parameters: - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - - name: environmentApp - type: string - default: 'Development' - - name: apiName - type: string - default: "api-PRODUCT-ENVIRONMENT-001" - - name: appSettings - type: object - default: - - name: 'APPINSIGHTS_INSTRUMENTATIONKEY' - value: '$(appiKey)' - slotSetting: false - - name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' - value: '$(appiConnection)' - slotSetting: false - - name: 'ASPNETCORE_ENVIRONMENT' - value: '$(environmentApp)' - slotSetting: false - - name: '$(appcsEnvironmentVariable)' - value: '$(appcsConnection)' - slotSetting: false - - name: '$(stEnvironmentVariable)' - value: '$(stConnection)' - slotSetting: false - -steps: - - ${{ each appSetting in parameters.appSettings }}: - - task: AzureAppServiceSettings@1 - displayName: 'set_appsettings ${{ appSetting.name }}' - inputs: - azureSubscription: ${{ parameters.subscriptionService }} - appName: ${{ parameters.apiName }} - appSettings: | - [ - { - "name": "${{ appSetting.name }}", - "value": "${{ appSetting.value }}", - "slotSetting": "${{ appSetting.slotSetting }}" - } - ] - slotName: ${{ parameters.slotName }} \ No newline at end of file diff --git a/.azuredevops/steps/appcs-infrastructure-steps.yml b/.azuredevops/steps/appcs-infrastructure-steps.yml deleted file mode 100644 index 528791d..0000000 --- a/.azuredevops/steps/appcs-infrastructure-steps.yml +++ /dev/null @@ -1,53 +0,0 @@ -parameters: -- name: armPath - type: string - default: "." -- name: subscriptionId - type: string - default: "00000000-0000-0000-0000-000000000000" -- name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' -- name: rgName - type: string - default: 'rg-PRODUCT-001' -- name: rgLocation - type: string - default: "West US 3" -- name: appcsName - type: string - default: "appcs-PRODUCT-ENVIRONMENT-001" -- name: appcsSku - type: string - default: "free" #standard - -steps: - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.appcsName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/appcs-appconfigurationstore.json" - csmParametersFile: "${{ parameters.armPath }}/appcs-appconfigurationstore.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.appcsName }}" -sku "${{ parameters.appcsSku }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.appcsName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/appcs-appconfigurationstore.json" - csmParametersFile: "${{ parameters.armPath }}/appcs-appconfigurationstore.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.appcsName }}" -sku "${{ parameters.appcsSku }}" diff --git a/.azuredevops/steps/appcs-settings-steps.yml b/.azuredevops/steps/appcs-settings-steps.yml deleted file mode 100644 index d647120..0000000 --- a/.azuredevops/steps/appcs-settings-steps.yml +++ /dev/null @@ -1,56 +0,0 @@ -parameters: - - name: armPath - type: string - default: "." - - name: subscriptionId - type: string - default: "00000000-0000-0000-0000-000000000000" - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - - name: rgName - type: string - default: 'rg-PRODUCT-001' - - name: rgLocation - type: string - default: "West US 3" - - name: appcsName - type: string - default: "appcs-PRODUCT-ENVIRONMENT-001" - - name: appcsKeys - type: object - default: [ "Shared:Sentinel", "Shared:Sentinel$Development" ] - - name: appcsValues - type: object - default: [ "1", "1" ] - -steps: - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.appcsName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/appcs-appconfigurationsetting.json" - csmParametersFile: "${{ parameters.armPath }}/appcs-appconfigurationsetting.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.appcsName }}" -appcsKeys ${{ convertToJson(parameters.appcsKeys) }} -appcsValues ${{ convertToJson(parameters.appcsValues) }} - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Configure ${{ parameters.appcsName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/appcs-appconfigurationsetting.json" - csmParametersFile: "${{ parameters.armPath }}/appcs-appconfigurationsetting.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.appcsName }}" -appcsKeys ${{ convertToJson(parameters.appcsKeys) }} -appcsValues ${{ convertToJson(parameters.appcsValues) }} diff --git a/.azuredevops/steps/appsetting-deploy-steps.yml b/.azuredevops/steps/appsetting-deploy-steps.yml deleted file mode 100644 index 26ce84b..0000000 --- a/.azuredevops/steps/appsetting-deploy-steps.yml +++ /dev/null @@ -1,35 +0,0 @@ -parameters: -- name: appServiceName - type: string - default: "api/web-PRODUCT-ENVIRONMENT-001" -- name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' -- name: environmentApp - type: string - default: 'development' -- name: slotName - type: string - default: 'production' -- name: appSetting - type: object - default: - name: 'ASPNETCORE_ENVIRONMENT' - value: 'Development' - slotSetting: false - -steps: - - task: AzureAppServiceSettings@1 - displayName: 'App Settings ${{ parameters.appServiceName }}' - inputs: - azureSubscription: ${{ parameters.subscriptionService }} - appName: ${{ parameters.appServiceName }} - appSettings: | - [ - { - "name": "${{ parameters.appSetting.name }}", - "value": "${{ parameters.appSetting.value }}", - "slotSetting": "${{ parameters.appSetting.slotSetting }}" - } - ] - slotName: ${{ parameters.slotName }} \ No newline at end of file diff --git a/.azuredevops/steps/appsettings-deploy-steps.yml b/.azuredevops/steps/appsettings-deploy-steps.yml deleted file mode 100644 index 969b784..0000000 --- a/.azuredevops/steps/appsettings-deploy-steps.yml +++ /dev/null @@ -1,36 +0,0 @@ -parameters: -- name: appServiceName - type: string - default: "web-PRODUCT-ENVIRONMENT-001" -- name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' -- name: environmentApp - type: string - default: 'development' -- name: slotName - type: string - default: 'development' -- name: appSettings - type: object - default: - - name: 'Shared:Sentinel' - value: '1' - slotSetting: false - -steps: - - ${{ each appSetting in parameters.appSettings }}: - - task: AzureAppServiceSettings@1 - displayName: 'App Settings ${{ parameters.appServiceName }}' - inputs: - azureSubscription: ${{ parameters.subscriptionService }} - appName: ${{ parameters.appServiceName }} - appSettings: | - [ - { - "name": "${{ parameters.appSetting.name }}", - "value": "${{ parameters.appSetting.value }}", - "slotSetting": "${{ parameters.appSetting.slotSetting }}" - } - ] - slotName: ${{ parameters.slotName }} \ No newline at end of file diff --git a/.azuredevops/steps/blob-deploy-steps.yml b/.azuredevops/steps/blob-deploy-steps.yml deleted file mode 100644 index a168710..0000000 --- a/.azuredevops/steps/blob-deploy-steps.yml +++ /dev/null @@ -1,30 +0,0 @@ -parameters: -- name: arguments - type: string - default: '--overwrite true' -- name: containerName - type: string -- name: filePath - type: string - default: '.' -- name: fileMask - type: string - default: '*' -- name: stName - type: string -- name: subscriptionId - type: string -- name: subscriptionService - type: string - -steps: - - task: AzureFileCopy@4 - displayName: 'copy files to ${{ parameters.containerName }}' - inputs: - sourcePath: '${{ parameters.filePath }}/${{ parameters.fileMask }}' - azureSubscription: '${{ parameters.subscriptionId }}' - connectedServiceNameARM: '${{ parameters.subscriptionService }}' - destination: 'azureBlob' - storage: '${{ parameters.stName }}' - containerName: '${{ parameters.containerName }}' - additionalArgumentsForBlobCopy: '${{ parameters.arguments }}' \ No newline at end of file diff --git a/.azuredevops/steps/cog-deploy-steps.yml b/.azuredevops/steps/cog-deploy-steps.yml deleted file mode 100644 index 35f209b..0000000 --- a/.azuredevops/steps/cog-deploy-steps.yml +++ /dev/null @@ -1,62 +0,0 @@ -parameters: -- name: armPath - type: string - default: "." -- name: subscriptionId - type: string - default: "00000000-0000-0000-0000-000000000000" -- name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' -- name: rgName - type: string - default: 'rg-PRODUCT-001' -- name: rgLocation - type: string - default: "West US 3" -- name: environmentApp - type: string - default: 'Development' -- name: cogName - type: string - default: "cog-PRODUCT-ENVIRONMENT-001" -- name: cogSku - type: string - default: "S0" #standard -- name: cogtextName - type: string - default: "cogtext-PRODUCT-ENVIRONMENT-001" -- name: cogtextSku - type: string - default: "F0" #standard - -steps: - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.cogName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/cog-cognitiveservices.json" - csmParametersFile: "${{ parameters.armPath }}/cog-cognitiveservices.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.cogName }}" -sku "${{ parameters.cogSku }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.cogtextName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/cogtext-textanalytics.json" - csmParametersFile: "${{ parameters.armPath }}/cogtext-textanalytics.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.cogtextName }}" -sku "${{ parameters.cogtextSku }}" \ No newline at end of file diff --git a/.azuredevops/steps/dotnet-archive-steps.yml b/.azuredevops/steps/dotnet-archive-steps.yml deleted file mode 100644 index 41e6800..0000000 --- a/.azuredevops/steps/dotnet-archive-steps.yml +++ /dev/null @@ -1,90 +0,0 @@ -parameters: - - name: artifactsPath - type: string - default: '.' - - name: archiveFile - type: string - default: '*.zip' - - name: srcPath - type: string - default: '.' - - name: srcProject - type: string - default: '*.csproj' - - name: nugetPathFile - type: string - default: ".nuget/NuGet.config" - - name: buildConfiguration - type: string - default: "Release" - - name: tempPath - type: string - default: './ymltemp' - - name: scriptsPath - type: string - default: "../scripts" - - name: versionToReplace - type: string - default: "1.0.0" - - name: dotnetVersion - type: string - default: "7.x" - -steps: - - task: DeleteFiles@1 - inputs: - SourceFolder: '${{ parameters.tempPath }}' - Contents: '**/*' - RemoveDotFiles: true - - - task: UseDotNet@2 - displayName: 'use .net ${{ parameters.dotnetVersion }}' - inputs: - packageType: 'sdk' - version: ${{ parameters.dotnetVersion }} - includePreviewVersions: true - - - task: PowerShell@2 - displayName: 'Set-Version.ps1' - inputs: - filePath: '${{ parameters.scriptsPath }}/Set-Version.ps1' - arguments: '-Path ${{ parameters.srcPath }} -VersionToReplace ${{ parameters.versionToReplace }}' - workingDirectory: '${{ parameters.scriptsPath }}' - - - task: DotNetCoreCLI@2 - displayName: 'dotnet restore' - inputs: - command: 'restore' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcProject }} - feedsToUse: config - nugetConfigPath: ${{ parameters.nugetPathFile }} - - - task: DotNetCoreCLI@2 - displayName: 'dotnet build' - inputs: - command: 'build' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcProject }} - arguments: --output ${{ parameters.tempPath }} --configuration ${{ parameters.buildConfiguration }} - - - task: ArchiveFiles@2 - displayName: 'Archive ${{ parameters.archiveFile }}' - inputs: - rootFolderOrFile: '${{ parameters.tempPath }}' - includeRootFolder: false - archiveType: zip - archiveFile: '${{ parameters.artifactsPath }}/${{ parameters.archiveFile }}' - replaceExistingArchive: true - - - task: DeleteFiles@1 - inputs: - SourceFolder: '${{ parameters.tempPath }}' - Contents: '**/*' - RemoveDotFiles: true - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: drop' - inputs: - pathToPublish: '${{ parameters.artifactsPath }}' - artifactName: drop \ No newline at end of file diff --git a/.azuredevops/steps/dotnet-build-steps.yml b/.azuredevops/steps/dotnet-build-steps.yml deleted file mode 100644 index 53be05f..0000000 --- a/.azuredevops/steps/dotnet-build-steps.yml +++ /dev/null @@ -1,44 +0,0 @@ -parameters: - - name: srcPath - type: string - default: "." - - name: srcProject - type: string - default: "*.csproj" - - name: nugetPathFile - type: string - default: ".nuget/NuGet.config" - - name: buildConfiguration - type: string - default: "Release" - - name: scriptsPath - type: string - default: "../scripts" - - name: dotnetVersion - type: string - default: "7.x" - -steps: - - task: UseDotNet@2 - displayName: 'use .net ${{ parameters.dotnetVersion }}' - inputs: - packageType: 'sdk' - version: ${{ parameters.dotnetVersion }} - includePreviewVersions: true - - - task: DotNetCoreCLI@2 - displayName: 'dotnet restore' - inputs: - command: 'restore' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcProject }} - feedsToUse: config - nugetConfigPath: ${{ parameters.nugetPathFile }} - - - task: DotNetCoreCLI@2 - displayName: 'dotnet build' - inputs: - command: 'build' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcProject }} - arguments: '--configuration ${{ parameters.buildConfiguration }}' diff --git a/.azuredevops/steps/dotnet-output-steps.yml b/.azuredevops/steps/dotnet-output-steps.yml deleted file mode 100644 index 9c089be..0000000 --- a/.azuredevops/steps/dotnet-output-steps.yml +++ /dev/null @@ -1,69 +0,0 @@ -parameters: - - name: artifactsPath - type: string - default: '.' - - name: archiveFile - type: string - default: '*.zip' - - name: srcPath - type: string - default: '.' - - name: srcProject - type: string - default: '*.csproj' - - name: nugetPathFile - type: string - default: ".nuget/NuGet.config" - - name: buildConfiguration - type: string - default: "Release" - - name: outputPath - type: string - default: '$(Build.ArtifactStagingDirectory)' - - name: scriptsPath - type: string - default: "../scripts" - - name: versionToReplace - type: string - default: "1.0.0" - - name: dotnetVersion - type: string - default: "7.x" - -steps: - - task: UseDotNet@2 - displayName: 'use .net ${{ parameters.dotnetVersion }}' - inputs: - packageType: 'sdk' - version: ${{ parameters.dotnetVersion }} - includePreviewVersions: true - - - task: PowerShell@2 - displayName: 'Set-Version.ps1' - inputs: - filePath: '${{ parameters.scriptsPath }}/Set-Version.ps1' - arguments: '-Path ${{ parameters.srcPath }} -VersionToReplace ${{ parameters.versionToReplace }}' - workingDirectory: '${{ parameters.scriptsPath }}' - - - task: DotNetCoreCLI@2 - displayName: 'dotnet restore' - inputs: - command: 'restore' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcProject }} - feedsToUse: config - nugetConfigPath: ${{ parameters.nugetPathFile }} - - - task: DotNetCoreCLI@2 - displayName: 'dotnet build' - inputs: - command: 'build' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcProject }} - arguments: --output ${{ parameters.outputPath }} --configuration ${{ parameters.buildConfiguration }} - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: drop' - inputs: - pathToPublish: '${{ parameters.artifactsPath }}' - artifactName: drop \ No newline at end of file diff --git a/.azuredevops/steps/dotnet-pack-steps.yml b/.azuredevops/steps/dotnet-pack-steps.yml deleted file mode 100644 index a60e779..0000000 --- a/.azuredevops/steps/dotnet-pack-steps.yml +++ /dev/null @@ -1,71 +0,0 @@ -parameters: - - name: artifactsPath - type: string - default: "." - - name: buildConfiguration - type: string - default: "Release" - - name: nugetPathFile - type: string - default: ".nuget/NuGet.config" - - name: srcPath - type: string - default: "." - - name: srcProject - type: string - default: "*.csproj" - - name: scriptsPath - type: string - default: "../scripts" - - name: versionToReplace - type: string - default: "1.0.0" - - name: dotnetVersion - type: string - default: "7.x" - -steps: - - task: UseDotNet@2 - displayName: 'use .net ${{ parameters.dotnetVersion }}' - inputs: - packageType: 'sdk' - version: ${{ parameters.dotnetVersion }} - includePreviewVersions: true - - - task: PowerShell@2 - displayName: 'Set-Version.ps1' - inputs: - filePath: '${{ parameters.scriptsPath }}/Set-Version.ps1' - arguments: '-Path ${{ parameters.srcPath }} -VersionToReplace ${{ parameters.versionToReplace }}' - workingDirectory: '${{ parameters.scriptsPath }}' - - - task: DotNetCoreCLI@2 - displayName: 'dotnet restore' - inputs: - command: 'restore' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcProject }} - feedsToUse: config - nugetConfigPath: ${{ parameters.nugetPathFile }} - - - task: DotNetCoreCLI@2 - displayName: 'dotnet build' - inputs: - command: 'build' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcProject }} - arguments: '--configuration ${{ parameters.buildConfiguration }}' - - - task: DotNetCoreCLI@2 - displayName: 'dotnet pack' - inputs: - command: pack - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcProject }} - arguments: '--output ${{ parameters.artifactsPath }}' - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: drop' - inputs: - pathToPublish: '${{ parameters.artifactsPath }}' - artifactName: drop \ No newline at end of file diff --git a/.azuredevops/steps/dotnet-publish-steps.yml b/.azuredevops/steps/dotnet-publish-steps.yml deleted file mode 100644 index 381937a..0000000 --- a/.azuredevops/steps/dotnet-publish-steps.yml +++ /dev/null @@ -1,71 +0,0 @@ -parameters: - - name: artifactsPath - type: string - default: "." - - name: artifactName - type: string - default: "drop" - - name: nugetPathFile - type: string - default: ".nuget/NuGet.config" - - name: restorePath - type: string - default: "." - - name: restoreProject - type: string - default: "*.csproj" - - name: srcPath - type: string - default: "." - - name: srcProject - type: string - default: "*.csproj" - - name: scriptsPath - type: string - default: "../scripts" - - name: versionToReplace - type: string - default: "1.0.0" - - name: dotnetVersion - type: string - default: "7.x" - - -steps: - - task: UseDotNet@2 - displayName: 'use .net ${{ parameters.dotnetVersion }}' - inputs: - packageType: 'sdk' - version: ${{ parameters.dotnetVersion }} - includePreviewVersions: true - - - task: PowerShell@2 - displayName: 'Set-Version.ps1' - inputs: - filePath: '${{ parameters.scriptsPath }}/Set-Version.ps1' - arguments: '-Path ${{ parameters.srcPath }} -VersionToReplace ${{ parameters.versionToReplace }}' - workingDirectory: '${{ parameters.scriptsPath }}' - - - task: DotNetCoreCLI@2 - displayName: 'dotnet restore' - inputs: - command: 'restore' - projects: | - ${{ parameters.restorePath }}/**/${{ parameters.restoreProject }} - feedsToUse: config - nugetConfigPath: ${{ parameters.nugetPathFile }} - - - task: DotNetCoreCLI@2 - displayName: 'dotnet publish' - inputs: - command: publish - publishWebProjects: false - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcProject }} - arguments: '--output ${{ parameters.artifactsPath }}' - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: ${{ parameters.artifactName }}' - inputs: - pathToPublish: '${{ parameters.artifactsPath }}' - artifactName: '${{ parameters.artifactName }}' \ No newline at end of file diff --git a/.azuredevops/steps/dotnet-test-steps.yml b/.azuredevops/steps/dotnet-test-steps.yml deleted file mode 100644 index 18906e4..0000000 --- a/.azuredevops/steps/dotnet-test-steps.yml +++ /dev/null @@ -1,71 +0,0 @@ -parameters: - - name: environmentApp - type: string - default: "rg-PRODUCT-001" - - name: testPath - type: string - default: "." - - name: testProject - type: string - default: "*.csproj" - - name: restorePath - type: string - default: "." - - name: restoreProject - type: string - default: "*.csproj" - - name: nugetPathFile - type: string - default: ".nuget/NuGet.config" - - name: appcsConnection - type: string - default: "" - - name: appcsEnvironmentVariable - type: string - default: "AZURE_APP_CONFIGURATION_CONNECTION" - - name: stConnection - type: string - default: "" - - name: stEnvironmentVariable - type: string - default: "AZURE_STORAGE_CONNECTION" - - name: arguments - type: string - default: "" - - name: dotnetVersion - type: string - default: "7.x" - - name: continueOnFail - type: boolean - default: false - -steps: - - task: UseDotNet@2 - displayName: 'use .net ${{ parameters.dotnetVersion }}' - inputs: - packageType: 'sdk' - version: ${{ parameters.dotnetVersion }} - includePreviewVersions: true - - - task: DotNetCoreCLI@2 - displayName: 'dotnet restore' - inputs: - command: 'restore' - projects: | - ${{ parameters.restorePath }}/**/${{ parameters.restoreProject }} - feedsToUse: config - nugetConfigPath: ${{ parameters.nugetPathFile }} - - - task: DotNetCoreCLI@2 - displayName: 'dotnet test' - continueOnError: ${{ parameters.continueOnFail }} - inputs: - command: test - projects: | - ${{ parameters.testPath }}/**/${{ parameters.testProject }} - arguments: ${{ parameters.arguments }} - env: - ${{ parameters.appcsEnvironmentVariable }}: ${{ parameters.appcsConnection }} - ${{ parameters.stEnvironmentVariable }}: ${{ parameters.stConnection }} - ASPNETCORE_ENVIRONMENT: ${{ parameters.environmentApp }} - AZURE_FUNCTIONS_ENVIRONMENT: ${{ parameters.environmentApp }} \ No newline at end of file diff --git a/.azuredevops/steps/ef-deploy-steps.yml b/.azuredevops/steps/ef-deploy-steps.yml deleted file mode 100644 index d8f76f6..0000000 --- a/.azuredevops/steps/ef-deploy-steps.yml +++ /dev/null @@ -1,106 +0,0 @@ -parameters: - - name: artifactsPath - type: string - default: "." - - name: buildConfiguration - type: string - default: "Release" - - name: dbContext - type: string - - name: dotnetVersion - type: string - default: "7.x" - - name: dotnetEfVersion - type: string - default: "7.0.14" - - name: nugetPathFile - type: string - default: ".nuget/NuGet.config" - - name: srcPath - type: string - default: "." - - name: srcProjectMask - type: string - default: "*.csproj" - - name: scriptsPath - type: string - default: "../scripts" - - name: sqldbFile - type: string - default: "migration.sql" - - name: sqldbName - type: string - default: "sqldb-PRODUCT-ENVIRONMENT-001" - - name: sqlName - type: string - default: "sql-PRODUCT-ENVIRONMENT-001" - - name: sqlUser - type: string - default: "LocalAdmin" - - name: sqlPassword - type: string - - name: efStartupPath - type: string - default: "." - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - - name: efTargetPath - type: string - default: "." - -steps: - - task: UseDotNet@2 - displayName: 'use .net ${{ parameters.dotnetVersion }}' - inputs: - packageType: 'sdk' - version: ${{ parameters.dotnetVersion }} - includePreviewVersions: true - - - task: DotNetCoreCLI@2 - displayName: 'dotnet restore' - inputs: - command: 'restore' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcProjectMask }} - feedsToUse: config - nugetConfigPath: ${{ parameters.nugetPathFile }} - - - task: DotNetCoreCLI@2 - displayName: 'dotnet build' - inputs: - command: 'build' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcProjectMask }} - arguments: '--configuration ${{ parameters.buildConfiguration }}' - - - task: DotNetCoreCLI@2 - displayName: 'dotnet tool install dotnet-ef' - inputs: - command: custom - custom: tool - arguments: 'install --global dotnet-ef --version ${{ parameters.dotnetEfVersion }}' - - - task: DotNetCoreCLI@2 - displayName: 'dotnet ef migrations script' - inputs: - command: custom - custom: ef - arguments: 'migrations script -i -o ${{ parameters.artifactsPath }}/${{ parameters.sqldbFile }} -p ${{ parameters.efTargetPath }} -s ${{ parameters.efStartupPath }} -c ${{ parameters.dbContext }}' - - - task: SqlAzureDacpacDeployment@1 - displayName: 'deploy SQL script' - inputs: - azureSubscription: '${{ parameters.subscriptionService }}' - deployType: 'SqlTask' - ServerName: '${{ parameters.sqlName }}.database.windows.net' - DatabaseName: '${{ parameters.sqldbName }}' - SqlUsername: '${{ parameters.sqlUser }}' - SqlPassword: '${{ parameters.sqlPassword }}' - SqlFile: '${{ parameters.artifactsPath }}/${{ parameters.sqldbFile }}' - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: drop' - inputs: - pathToPublish: '${{ parameters.artifactsPath }}' - artifactName: drop \ No newline at end of file diff --git a/.azuredevops/steps/ef-publish-steps.yml b/.azuredevops/steps/ef-publish-steps.yml deleted file mode 100644 index 359e95a..0000000 --- a/.azuredevops/steps/ef-publish-steps.yml +++ /dev/null @@ -1,69 +0,0 @@ -parameters: - - name: artifactsPath - type: string - default: "." - - name: buildConfiguration - type: string - default: "Release" - - name: dbContext - type: string - - name: dotnetVersion - type: string - default: "7.x" - - name: nugetPathFile - type: string - default: ".nuget/NuGet.config" - - name: sqldbFile - type: string - default: "migration.sql" - - name: srcPath - type: string - default: "." - - name: srcProject - type: string - default: "*.csproj" - - name: efStartupPath - type: string - default: "." - - name: efTargetPath - type: string - default: "." - -steps: - - task: UseDotNet@2 - displayName: 'use .net ${{ parameters.dotnetVersion }}' - inputs: - packageType: 'sdk' - version: ${{ parameters.dotnetVersion }} - includePreviewVersions: true - - - task: DotNetCoreCLI@2 - displayName: 'dotnet restore' - inputs: - command: 'restore' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcProject }} - feedsToUse: config - nugetConfigPath: ${{ parameters.nugetPathFile }} - - - task: DotNetCoreCLI@2 - displayName: 'dotnet build' - inputs: - command: 'build' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcProject }} - arguments: '--configuration ${{ parameters.buildConfiguration }}' - - - task: DotNetCoreCLI@2 - displayName: 'dotnet tool install dotnet-ef' - inputs: - command: custom - custom: tool - arguments: 'install --global dotnet-ef' - - - task: DotNetCoreCLI@2 - displayName: 'dotnet ef migrations script' - inputs: - command: custom - custom: ef - arguments: 'migrations script -i -o ${{ parameters.sqldbFile }} -p ${{ parameters.efTargetPath }} -s ${{ parameters.efStartupPath }} -c ${{ parameters.dbContext }}' \ No newline at end of file diff --git a/.azuredevops/steps/func-build-steps.yml b/.azuredevops/steps/func-build-steps.yml deleted file mode 100644 index 3a2369b..0000000 --- a/.azuredevops/steps/func-build-steps.yml +++ /dev/null @@ -1,90 +0,0 @@ -parameters: - - name: artifactsPath - type: string - default: '.' - - name: archiveFile - type: string - default: '*.zip' - - name: funcPath - type: string - default: '.' - - name: funcProject - type: string - default: '*.csproj' - - name: nugetPathFile - type: string - default: ".nuget/NuGet.config" - - name: buildConfiguration - type: string - default: "Release" - - name: tempPath - type: string - default: './ymltemp' - - name: scriptsPath - type: string - default: "../scripts" - - name: versionToReplace - type: string - default: "1.0.0" - - name: dotnetVersion - type: string - default: "7.x" - -steps: - - task: DeleteFiles@1 - inputs: - SourceFolder: '${{ parameters.tempPath }}' - Contents: '**/*' - RemoveDotFiles: true - - - task: UseDotNet@2 - displayName: 'use .net ${{ parameters.dotnetVersion }}' - inputs: - packageType: 'sdk' - version: ${{ parameters.dotnetVersion }} - includePreviewVersions: true - - - task: PowerShell@2 - displayName: 'Set-Version.ps1' - inputs: - filePath: '${{ parameters.scriptsPath }}/Set-Version.ps1' - arguments: '-Path ${{ parameters.funcPath }} -VersionToReplace ${{ parameters.versionToReplace }}' - workingDirectory: '${{ parameters.scriptsPath }}' - - - task: DotNetCoreCLI@2 - displayName: 'dotnet restore' - inputs: - command: 'restore' - projects: | - ${{ parameters.funcPath }}/**/${{ parameters.funcProject }} - feedsToUse: config - nugetConfigPath: ${{ parameters.nugetPathFile }} - - - task: DotNetCoreCLI@2 - displayName: 'dotnet build' - inputs: - command: 'build' - projects: | - ${{ parameters.funcPath }}/**/${{ parameters.funcProject }} - arguments: --output ${{ parameters.tempPath }} --configuration ${{ parameters.buildConfiguration }} - - - task: ArchiveFiles@2 - displayName: 'Archive ${{ parameters.archiveFile }}' - inputs: - rootFolderOrFile: '${{ parameters.tempPath }}' - includeRootFolder: false - archiveType: zip - archiveFile: '${{ parameters.artifactsPath }}/${{ parameters.archiveFile }}' - replaceExistingArchive: true - - - task: DeleteFiles@1 - inputs: - SourceFolder: '${{ parameters.tempPath }}' - Contents: '**/*' - RemoveDotFiles: true - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: drop' - inputs: - pathToPublish: '${{ parameters.artifactsPath }}' - artifactName: drop \ No newline at end of file diff --git a/.azuredevops/steps/func-deploy-steps.yml b/.azuredevops/steps/func-deploy-steps.yml deleted file mode 100644 index 2678c85..0000000 --- a/.azuredevops/steps/func-deploy-steps.yml +++ /dev/null @@ -1,35 +0,0 @@ -parameters: - - name: artifactsPath - type: string - default: '.' - - name: archiveFile - type: string - default: '*.zip' - - name: funcName - type: string - default: 'func-PRODUCT-ENVIRONMENT-001' - - name: subscriptionId - type: string - default: "00000000-0000-0000-0000-000000000000" - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - -steps: - - task: DownloadBuildArtifacts@0 - inputs: - buildType: 'current' - downloadType: 'single' - artifactName: 'drop' - downloadPath: '${{ parameters.artifactsPath }}' - - - task: AzureRmWebAppDeployment@4 - displayName: 'Deploying ${{ parameters.funcName }}' - inputs: - ConnectionType: 'AzureRM' - ConnectedServiceName: ${{ parameters.subscriptionService }} - subscriptionName: ${{ parameters.subscriptionId }} - appType: 'webApp' - WebAppName: '${{ parameters.funcName }}' - RemoveAdditionalFilesFlag: true - packageForLinux: '${{ parameters.artifactsPath }}/**/${{ parameters.archiveFile }}' diff --git a/.azuredevops/steps/func-infrastructure-plan-steps.yml b/.azuredevops/steps/func-infrastructure-plan-steps.yml deleted file mode 100644 index 01903f2..0000000 --- a/.azuredevops/steps/func-infrastructure-plan-steps.yml +++ /dev/null @@ -1,73 +0,0 @@ -parameters: - - name: armPath - type: string - default: "." - - name: subscriptionId - type: string - - name: subscriptionService - type: string - - name: rgName - type: string - - name: rgLocation - type: string - default: "West US 3" - - name: environmentApp - type: string - default: 'Development' - - name: appiKey - type: string - - name: appiConnection - type: string - - name: funcName - type: string - - name: funcRuntime - type: string - default: "dotnet-isolated" - - name: funcVersion - type: "number" - default: 4 - - name: alwaysOn - type: string - default: "false" - - name: planSku - type: string - default: "Y1" - - name: planCapacity - type: string - default: "0" - - name: planTier - type: string - default: "dynamic" - - name: stName - type: string - -steps: - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.funcName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/func-functionsappplan.json" - csmParametersFile: "${{ parameters.armPath }}/func-functionsappplan.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.funcName }}" -funcVersion "${{ parameters.funcVersion }}" -stName "${{ parameters.stName }}" -appiKey "${{ parameters.appiKey }}" -appiConnection "${{ parameters.appiConnection }}" -funcRuntime "${{ parameters.funcRuntime }}" -environmentApp "${{ parameters.environmentApp }}" -sku "${{ parameters.planSku }}" -alwaysOn "${{ parameters.alwaysOn }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.funcName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/func-functionsappplan.json" - csmParametersFile: "${{ parameters.armPath }}/func-functionsappplan.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.funcName }}" -funcVersion "${{ parameters.funcVersion }}" -stName "${{ parameters.stName }}" -appiKey "${{ parameters.appiKey }}" -appiConnection "${{ parameters.appiConnection }}" -funcRuntime "${{ parameters.funcRuntime }}" -environmentApp "${{ parameters.environmentApp }}" -sku "${{ parameters.planSku }}" -alwaysOn "${{ parameters.alwaysOn }}" diff --git a/.azuredevops/steps/func-infrastructure-plan-vnet-steps.yml b/.azuredevops/steps/func-infrastructure-plan-vnet-steps.yml deleted file mode 100644 index d2d99b4..0000000 --- a/.azuredevops/steps/func-infrastructure-plan-vnet-steps.yml +++ /dev/null @@ -1,120 +0,0 @@ -parameters: - - name: armPath - type: string - default: "." - - name: subscriptionId - type: string - default: "00000000-0000-0000-0000-000000000000" - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - - name: rgName - type: string - default: 'rg-PRODUCT-001' - - name: rgLocation - type: string - default: "West US 3" - - name: environmentApp - type: string - default: 'Development' - - name: appiKey - type: string - - name: appiConnection - type: string - - name: funcName - type: string - default: "func-PRODUCT-ENVIRONMENT-001" - - name: funcVersion - type: "number" - default: 4 - - name: funcRuntime - type: string - default: "dotnet-isolated" - - name: alwaysOn - type: boolean - default: false - - name: planName - type: string - default: "plan-func-PRODUCT-ENVIRONMENT-001" - - name: planSku - type: string - - name: planCapacity - type: string - default: "1" - - name: planResourceGroupSubscriptionId - type: string - - name: planResourceGroupName - type: string - - name: planResourceGroupLocation - type: string - - name: stName - type: string - default: "stPRODUCTENVIRONMENT001" - - name: vnetResourceGroupName - type: string - default: "rg-REGION-SUBSCRIPTION-ENVIRONMENT" - - name: vnetName - type: string - - name: snetName - type: string - -steps: - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.planName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.planResourceGroupSubscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.planResourceGroupName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/plan-appplan.json" - csmParametersFile: "${{ parameters.armPath }}/plan-appplan.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.planName }}" -sku "${{ parameters.planSku }}" -location "${{ parameters.rgLocation }}" -skuCapacity "${{ parameters.planCapacity }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.planName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.planResourceGroupSubscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.planResourceGroupName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/plan-appplan.json" - csmParametersFile: "${{ parameters.armPath }}/plan-appplan.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.planName }}" -sku "${{ parameters.planSku }}" -location "${{ parameters.rgLocation }}" -skuCapacity "${{ parameters.planCapacity }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.funcName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/func-functionsappvnet.json" - csmParametersFile: "${{ parameters.armPath }}/func-functionsappvnet.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.funcName }}" -location "${{ parameters.rgLocation }}" -funcVersion "${{ parameters.funcVersion }}" -stName "${{ parameters.stName }}" -appiKey "${{ parameters.appiKey }}" -appiConnection "${{ parameters.appiConnection }}" -funcRuntime "${{ parameters.funcRuntime }}" -environmentApp "${{ parameters.environmentApp }}" -planName "${{ parameters.planName }}" -planResourceGroupSubscriptionId "${{ parameters.planResourceGroupSubscriptionId }}" -planResourceGroupName "${{ parameters.planResourceGroupName }}" -vnetResourceGroupName "${{ parameters.vnetResourceGroupName }}" -vnetName "${{ parameters.vnetName }}" -subnetName "${{ parameters.snetName }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.funcName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/func-functionsappvnet.json" - csmParametersFile: "${{ parameters.armPath }}/func-functionsappvnet.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.funcName }}" -location "${{ parameters.rgLocation }}" -funcVersion "${{ parameters.funcVersion }}" -stName "${{ parameters.stName }}" -appiKey "${{ parameters.appiKey }}" -appiConnection "${{ parameters.appiConnection }}" -funcRuntime "${{ parameters.funcRuntime }}" -environmentApp "${{ parameters.environmentApp }}" -planName "${{ parameters.planName }}" -planResourceGroupSubscriptionId "${{ parameters.planResourceGroupSubscriptionId }}" -planResourceGroupName "${{ parameters.planResourceGroupName }}" -vnetResourceGroupName "${{ parameters.vnetResourceGroupName }}" -vnetName "${{ parameters.vnetName }}" -subnetName "${{ parameters.snetName }}" \ No newline at end of file diff --git a/.azuredevops/steps/func-infrastructure-steps.yml b/.azuredevops/steps/func-infrastructure-steps.yml deleted file mode 100644 index 780380b..0000000 --- a/.azuredevops/steps/func-infrastructure-steps.yml +++ /dev/null @@ -1,107 +0,0 @@ -parameters: - - name: armPath - type: string - default: "." - - name: subscriptionId - type: string - default: "00000000-0000-0000-0000-000000000000" - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - - name: rgName - type: string - default: 'rg-PRODUCT-001' - - name: rgLocation - type: string - default: "West US 3" - - name: environmentApp - type: string - default: 'Development' - - name: appiKey - type: string - - name: appiConnection - type: string - - name: funcName - type: string - default: "func-PRODUCT-ENVIRONMENT-001" - - name: funcVersion - type: "number" - default: 4 - - name: funcRuntime - type: string - default: "dotnet-isolated" - - name: alwaysOn - type: boolean - default: false - - name: planName - type: string - default: "plan-func-PRODUCT-ENVIRONMENT-001" - - name: planSku - type: string - - name: planCapacity - type: string - default: "1" - - name: stName - type: string - default: "stPRODUCTENVIRONMENT001" - -steps: - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.planName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/plan-appplan.json" - csmParametersFile: "${{ parameters.armPath }}/plan-appplan.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.planName }}" -sku "${{ parameters.planSku }}" -location "${{ parameters.rgLocation }}" -skuCapacity "${{ parameters.planCapacity }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.planName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/plan-appplan.json" - csmParametersFile: "${{ parameters.armPath }}/plan-appplan.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.planName }}" -sku "${{ parameters.planSku }}" -location "${{ parameters.rgLocation }}" -skuCapacity "${{ parameters.planCapacity }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.funcName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/func-functionsapp.json" - csmParametersFile: "${{ parameters.armPath }}/func-functionsapp.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.funcName }}" -location "${{ parameters.rgLocation }}" -funcVersion "${{ parameters.funcVersion }}" -stName "${{ parameters.stName }}" -appiKey "${{ parameters.appiKey }}" -appiConnection "${{ parameters.appiConnection }}" -funcRuntime "${{ parameters.funcRuntime }}" -environmentApp "${{ parameters.environmentApp }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.funcName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/func-functionsapp.json" - csmParametersFile: "${{ parameters.armPath }}/func-functionsapp.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.funcName }}" -location "${{ parameters.rgLocation }}" -funcVersion "${{ parameters.funcVersion }}" -stName "${{ parameters.stName }}" -appiKey "${{ parameters.appiKey }}" -appiConnection "${{ parameters.appiConnection }}" -funcRuntime "${{ parameters.funcRuntime }}" -environmentApp "${{ parameters.environmentApp }}" diff --git a/.azuredevops/steps/func-settings-steps.yml b/.azuredevops/steps/func-settings-steps.yml deleted file mode 100644 index 4e02ad1..0000000 --- a/.azuredevops/steps/func-settings-steps.yml +++ /dev/null @@ -1,51 +0,0 @@ -parameters: - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - - name: environmentApp - type: string - default: 'Development' - - name: funcName - type: string - default: "func-PRODUCT-ENVIRONMENT-001" - - name: appSettings - type: object - default: - - name: 'APPINSIGHTS_INSTRUMENTATIONKEY' - value: '$(appiKey)' - slotSetting: false - - name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' - value: '$(appiConnection)' - slotSetting: false - - name: 'AZURE_FUNCTIONS_ENVIRONMENT' - value: '$(environmentApp)' - slotSetting: false - - name: 'FUNCTIONS_EXTENSION_VERSION' - value: '~$(funcVersion)' - slotSetting: false - - name: 'FUNCTIONS_WORKER_RUNTIME' - value: '$(funcRuntime)' - slotSetting: false - - name: '$(appcsEnvironmentVariable)' - value: '$(appcsConnection)' - slotSetting: false - - name: '$(stEnvironmentVariable)' - value: '$(stConnection)' - slotSetting: false - -steps: - - ${{ each appSetting in parameters.appSettings }}: - - task: AzureAppServiceSettings@1 - displayName: 'set_appsettings ${{ appSetting.name }}' - inputs: - azureSubscription: ${{ parameters.subscriptionService }} - appName: ${{ parameters.funcName }} - appSettings: | - [ - { - "name": "${{ appSetting.name }}", - "value": "${{ appSetting.value }}", - "slotSetting": "${{ appSetting.slotSetting }}" - } - ] - slotName: ${{ parameters.slotName }} \ No newline at end of file diff --git a/.azuredevops/steps/hcm-infrastructure-steps.yml b/.azuredevops/steps/hcm-infrastructure-steps.yml deleted file mode 100644 index 672ba0a..0000000 --- a/.azuredevops/steps/hcm-infrastructure-steps.yml +++ /dev/null @@ -1,53 +0,0 @@ -parameters: -- name: armPath - type: string - default: "." -- name: subscriptionId - type: string - default: "00000000-0000-0000-0000-000000000000" -- name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' -- name: rgName - type: string - default: 'rg-PRODUCT-001' -- name: rgLocation - type: string - default: "West US 3" -- name: hcnName - type: string - default: "hcn-PRODUCT-ENVIRONMENT-001" -- name: hcnRelayName - type: string - default: "" - -steps: - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.hcnName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/hcn-hybridconnection.json" - csmParametersFile: "${{ parameters.armPath }}/hcn-hybridconnection.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.hcnName }}" -relayName "${{ parameters.hcnRelayName }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.hcnName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/hcn-hybridconnection.json" - csmParametersFile: "${{ parameters.armPath }}/hcn-hybridconnection.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.hcnName }}" -relayName "${{ parameters.hcnRelayName }}" diff --git a/.azuredevops/steps/integration-test-steps.yml b/.azuredevops/steps/integration-test-steps.yml deleted file mode 100644 index b058f92..0000000 --- a/.azuredevops/steps/integration-test-steps.yml +++ /dev/null @@ -1,46 +0,0 @@ -parameters: - - name: environmentApp - type: string - default: "rg-PRODUCT-001" - - name: integrationPath - type: string - default: "." - - name: integrationProject - type: string - default: "*.csproj" - - name: appcsConnection - type: string - default: "" - - name: appcsEnvironmentVariable - type: string - default: "AZURE_APP_CONFIGURATION_CONNECTION" - - name: stConnection - type: string - default: "" - - name: stEnvironmentVariable - type: string - default: "AZURE_STORAGE_CONNECTION" - - name: dotnetVersion - type: string - default: "7.x" - - -steps: - - task: UseDotNet@2 - displayName: 'use .net ${{ parameters.dotnetVersion }}' - inputs: - packageType: 'sdk' - version: ${{ parameters.dotnetVersion }} - includePreviewVersions: true - - - task: DotNetCoreCLI@2 - displayName: 'dotnet test' - inputs: - command: test - projects: | - ${{ parameters.integrationPath }}/**/${{ parameters.integrationProject }} - env: - ${{ parameters.appcsEnvironmentVariable }}: ${{ parameters.appcsConnection }} - ${{ parameters.stEnvironmentVariable }}: ${{ parameters.stConnection }} - ASPNETCORE_ENVIRONMENT: ${{ parameters.environmentApp }} - AZURE_FUNCTIONS_ENVIRONMENT: ${{ parameters.environmentApp }} \ No newline at end of file diff --git a/.azuredevops/steps/landingzone-infrastructure-steps.yml b/.azuredevops/steps/landingzone-infrastructure-steps.yml deleted file mode 100644 index 4ce08dd..0000000 --- a/.azuredevops/steps/landingzone-infrastructure-steps.yml +++ /dev/null @@ -1,147 +0,0 @@ -parameters: - - name: armPath - type: string - default: "." - - name: subscriptionId - type: string - - name: subscriptionService - type: string - - name: rgName - type: string - - name: rgLocation - type: string - - name: appiName - type: string - - name: kvName - type: string - - name: stName - type: string - - name: workLocation - type: string - - name: workName - type: string - - name: workResourceGroupName - type: string - - name: workSubscriptionId - type: string - -steps: - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.stName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/st-storageaccount.json" - csmParametersFile: "${{ parameters.armPath }}/st-storageaccount.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.stName }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.stName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/st-storageaccount.json" - csmParametersFile: "${{ parameters.armPath }}/st-storageaccount.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.stName }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.workName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.workSubscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.workResourceGroupName }}" - location: "${{ parameters.workLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/work-loganalyticsworkspace.json" - csmParametersFile: "${{ parameters.armPath }}/work-loganalyticsworkspace.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.workName }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.workName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.workSubscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.workResourceGroupName }}" - location: "${{ parameters.workLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/work-loganalyticsworkspace.json" - csmParametersFile: "${{ parameters.armPath }}/work-loganalyticsworkspace.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.workName }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.appiName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/appi-applicationinsights.json" - csmParametersFile: "${{ parameters.armPath }}/appi-applicationinsights.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.appiName }}" -workName "${{ parameters.workName }}" -workResourceGroupName "${{ parameters.workResourceGroupName }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.appiName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/appi-applicationinsights.json" - csmParametersFile: "${{ parameters.armPath }}/appi-applicationinsights.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.appiName }}" -workName "${{ parameters.workName }}" -workResourceGroupName "${{ parameters.workResourceGroupName }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.kvName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/kv-keyvault.json" - csmParametersFile: "${{ parameters.armPath }}/kv-keyvault.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.kvName }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.kvName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/kv-keyvault.json" - csmParametersFile: "${{ parameters.armPath }}/kv-keyvault.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.kvName }}" \ No newline at end of file diff --git a/.azuredevops/steps/logic-infrastructure-steps.yml b/.azuredevops/steps/logic-infrastructure-steps.yml deleted file mode 100644 index 9e68aac..0000000 --- a/.azuredevops/steps/logic-infrastructure-steps.yml +++ /dev/null @@ -1,53 +0,0 @@ -parameters: - - name: subscriptionId - type: string - default: "00000000-0000-0000-0000-000000000000" - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - - name: rgName - type: string - default: 'rg-PRODUCT-001' - - name: rgLocation - type: string - default: "West US 3" - - name: logicPath - type: string - default: "." - - name: logicName - type: string - default: "logic-PRODUCT-ENVIRONMENT-001" - - name: logicParameters - type: string - default: "logic-PRODUCT-ENVIRONMENT-001" - -steps: - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.logicName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.logicPath }}/LogicApp.json" - csmParametersFile: "${{ parameters.logicPath }}/LogicApp.parameters.json" - deploymentMode: "Validation" - overrideParameters: "${{ parameters.logicParameters }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.logicName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.logicPath }}/LogicApp.json" - csmParametersFile: "${{ parameters.logicPath }}/LogicApp.parameters.json" - deploymentMode: "Incremental" - overrideParameters: "${{ parameters.logicParameters }}" \ No newline at end of file diff --git a/.azuredevops/steps/msbuild-build-steps.yml b/.azuredevops/steps/msbuild-build-steps.yml deleted file mode 100644 index a033239..0000000 --- a/.azuredevops/steps/msbuild-build-steps.yml +++ /dev/null @@ -1,51 +0,0 @@ -parameters: - - name: artifactsPath - type: string - default: "." - - name: srcPath - type: string - default: "." - - name: srcProject - type: string - default: "*.csproj" - - name: srcSolution - type: string - default: "*.sln" - - name: nugetId - type: string - default: "Nuget-Feed-GUID" - - name: nugetPathFile - type: string - default: ".nuget/NuGet.config" - - name: buildConfiguration - type: string - default: "Release" - - name: scriptsPath - type: string - default: "../scripts" - -steps: - - task: NuGetCommand@2 - displayName: 'nuget restore' - inputs: - command: 'restore' - feedsToUse: select - vstsFeed: '${{ parameters.nugetId }}' - restoreSolution: ${{ parameters.srcPath }}/**/${{ parameters.srcSolution }} - - - task: DotNetCoreCLI@2 - displayName: 'dotnet restore' - inputs: - command: 'restore' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcProject }} - feedsToUse: config - nugetConfigPath: ${{ parameters.nugetPathFile }} - - - task: DotNetCoreCLI@2 - displayName: 'dotnet build' - inputs: - command: 'build' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcProject }} - arguments: '--configuration ${{ parameters.buildConfiguration }}' diff --git a/.azuredevops/steps/npm-build-steps.yml b/.azuredevops/steps/npm-build-steps.yml deleted file mode 100644 index 69cefba..0000000 --- a/.azuredevops/steps/npm-build-steps.yml +++ /dev/null @@ -1,83 +0,0 @@ -parameters: - - name: artifactsPath - type: string - default: "." - - name: archiveFile - type: string - default: "archive.zip" - - name: srcPath - type: string - default: "." - - name: scriptsPath - type: string - default: "../scripts" - - name: tempPath - type: string - default: './sitetemp' - - name: versionToReplace - type: string - default: "1.0.0" - -steps: - - task: PowerShell@2 - displayName: 'Set-Version.ps1' - inputs: - filePath: '${{ parameters.scriptsPath }}/Set-Version.ps1' - arguments: '-Path ${{ parameters.srcPath }} -VersionToReplace ${{ parameters.versionToReplace }}' - workingDirectory: '${{ parameters.scriptsPath }}' - - - task: NodeTool@0 - displayName: Install Node.js - inputs: - versionSpec: '17.x' - - - task: Npm@1 - displayName: 'npm install' - inputs: - command: 'install' - workingDir: '${{ parameters.srcPath }}' - - - task: Npm@1 - displayName: 'npm run build' - inputs: - command: 'custom' - customCommand: 'run build' - workingDir: '${{ parameters.srcPath }}' - - - task: DeleteFiles@1 - displayName: 'delete tempPath' - inputs: - SourceFolder: '${{ parameters.tempPath }}' - Contents: '**/*' - RemoveDotFiles: true - - - task: CopyFiles@2 - displayName: 'copy wwwroot' - inputs: - SourceFolder: '${{ parameters.srcPath }}/wwwroot' - Contents: '**' - TargetFolder: '${{ parameters.tempPath }}' - CleanTargetFolder: true - OverWrite: true - - - task: ArchiveFiles@2 - displayName: 'Archive ${{ parameters.archiveFile }}' - inputs: - rootFolderOrFile: '${{ parameters.tempPath }}' - includeRootFolder: false - archiveType: zip - archiveFile: '${{ parameters.artifactsPath }}/${{ parameters.archiveFile }}' - replaceExistingArchive: true - - - task: DeleteFiles@1 - displayName: 'delete tempPath' - inputs: - SourceFolder: '${{ parameters.tempPath }}' - Contents: '**/*' - RemoveDotFiles: true - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: drop' - inputs: - pathToPublish: '${{ parameters.artifactsPath }}' - artifactName: drop \ No newline at end of file diff --git a/.azuredevops/steps/npm-test-steps.yml b/.azuredevops/steps/npm-test-steps.yml deleted file mode 100644 index 1feaa68..0000000 --- a/.azuredevops/steps/npm-test-steps.yml +++ /dev/null @@ -1,35 +0,0 @@ -parameters: - - name: artifactsPath - type: string - default: "." - - name: srcPath - type: string - default: "." - - name: scriptsPath - type: string - default: "../scripts" - -steps: - - task: NodeTool@0 - displayName: Install Node.js - inputs: - versionSpec: '17.x' - - - task: Npm@1 - displayName: 'npm install' - inputs: - command: 'install' - workingDir: '${{ parameters.srcPath }}' - - - task: Npm@1 - displayName: 'npm run test' - inputs: - command: 'custom' - customCommand: 'run test' - workingDir: '${{ parameters.srcPath }}' - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: drop' - inputs: - pathToPublish: '${{ parameters.artifactsPath }}' - artifactName: drop \ No newline at end of file diff --git a/.azuredevops/steps/nuget-deploy-external-steps.yml b/.azuredevops/steps/nuget-deploy-external-steps.yml deleted file mode 100644 index 50166be..0000000 --- a/.azuredevops/steps/nuget-deploy-external-steps.yml +++ /dev/null @@ -1,38 +0,0 @@ -parameters: - - name: artifactsPath - type: string - default: "." - - name: nugetFile - type: string - default: "*.nupkg" - - name: nugetType - type: string - default: "external" - - name: nugetService - type: string - default: "" - - name: nugetFeeds - type: string - default: "select" - -steps: - - task: DownloadBuildArtifacts@0 - inputs: - buildType: 'current' - downloadType: 'single' - artifactName: 'drop' - downloadPath: '${{ parameters.artifactsPath }}' - - - task: NuGetToolInstaller@1 - inputs: - versionSpec: '*' - checkLatest: true - - - task: NuGetCommand@2 - inputs: - command: 'push' - feedsToUse: '${{ parameters.nugetFeeds }}' - nuGetFeedType: '${{ parameters.nugetType }}' - includeNuGetOrg: true - publishFeedCredentials: '${{ parameters.nugetService }}' - packagesToPush: '${{ parameters.artifactsPath }}/**/${{ parameters.nugetFile }}' diff --git a/.azuredevops/steps/nuget-deploy-internal-steps.yml b/.azuredevops/steps/nuget-deploy-internal-steps.yml deleted file mode 100644 index f20bff5..0000000 --- a/.azuredevops/steps/nuget-deploy-internal-steps.yml +++ /dev/null @@ -1,33 +0,0 @@ -parameters: - - name: artifactsPath - type: string - default: "." - - name: nugetFile - type: string - default: "*.nupkg" - - name: nugetId - type: string - - name: nugetType - type: string - default: "internal" - - name: nugetFeeds - type: string - default: "select" - -steps: - - task: DownloadBuildArtifacts@0 - inputs: - buildType: 'current' - downloadType: 'single' - artifactName: 'drop' - downloadPath: '${{ parameters.artifactsPath }}' - - - task: NuGetCommand@2 - displayName: 'Deploying nuget' - inputs: - command: 'push' - packagesToPush: '${{ parameters.artifactsPath }}/**/${{ parameters.nugetFile }};' - feedsToUse: '${{ parameters.nugetFeeds }}' - nuGetFeedType: '${{ parameters.nugetType }}' - publishVstsFeed: '${{ parameters.nugetId }}' - allowPackageConflicts: true \ No newline at end of file diff --git a/.azuredevops/steps/nuget-pack-steps.yml b/.azuredevops/steps/nuget-pack-steps.yml deleted file mode 100644 index b5d43ae..0000000 --- a/.azuredevops/steps/nuget-pack-steps.yml +++ /dev/null @@ -1,63 +0,0 @@ -parameters: - - name: artifactsPath - type: string - default: "." - - name: buildConfiguration - type: string - default: "Release" - - name: nugetPathFile - type: string - default: ".nuget/NuGet.config" - - name: srcPath - type: string - default: "." - - name: srcNuspec - type: string - default: "*.nuspec" - - name: srcSolution - type: string - default: "*.sln" - - name: scriptsPath - type: string - default: "../scripts" - - name: versionToReplace - type: string - default: "1.0.0" - -steps: - - task: PowerShell@2 - displayName: 'Set-Version.ps1' - inputs: - filePath: '${{ parameters.scriptsPath }}/Set-Version.ps1' - arguments: '-Path ${{ parameters.srcPath }} -VersionToReplace ${{ parameters.versionToReplace }}' - workingDirectory: '${{ parameters.scriptsPath }}' - - - task: DotNetCoreCLI@2 - displayName: 'dotnet restore' - inputs: - command: 'restore' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcProject }} - feedsToUse: config - nugetConfigPath: ${{ parameters.nugetPathFile }} - - - task: DotNetCoreCLI@2 - displayName: 'dotnet build' - inputs: - command: 'build' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.srcSolution }} - arguments: '--configuration ${{ parameters.buildConfiguration }}' - -# Package a project - - task: NuGetCommand@2 - inputs: - command: 'pack' - packagesToPack: ${{ parameters.srcPath }}/**/${{ parameters.srcNuspec }} - packDestination: '${{ parameters.artifactsPath }}' - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: drop' - inputs: - pathToPublish: '${{ parameters.artifactsPath }}' - artifactName: drop \ No newline at end of file diff --git a/.azuredevops/steps/sb-infrastructure-steps.yml b/.azuredevops/steps/sb-infrastructure-steps.yml deleted file mode 100644 index db5b3f5..0000000 --- a/.azuredevops/steps/sb-infrastructure-steps.yml +++ /dev/null @@ -1,53 +0,0 @@ -parameters: - - name: armPath - type: string - default: "." - - name: subscriptionId - type: string - default: "00000000-0000-0000-0000-000000000000" - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - - name: rgName - type: string - default: 'rg-PRODUCT-001' - - name: rgLocation - type: string - default: "West US 3" - - name: sbName - type: string - default: "sb-PRODUCT-ENVIRONMENT-001" - - name: sbSku - type: string - default: "Basic" - -steps: - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.sbName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/sb-servicebus.json" - csmParametersFile: "${{ parameters.armPath }}/sb-servicebus.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.sbName }}" -sku "${{ parameters.sbSku }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.sbName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/sb-servicebus.json" - csmParametersFile: "${{ parameters.armPath }}/sb-servicebus.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.sbName }}" -sku "${{ parameters.sbSku }}" \ No newline at end of file diff --git a/.azuredevops/steps/sqldb-infrastructure-steps.yml b/.azuredevops/steps/sqldb-infrastructure-steps.yml deleted file mode 100644 index 934ccbe..0000000 --- a/.azuredevops/steps/sqldb-infrastructure-steps.yml +++ /dev/null @@ -1,67 +0,0 @@ -parameters: - - name: armPath - type: string - default: "." - - name: subscriptionId - type: string - default: "00000000-0000-0000-0000-000000000000" - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - - name: rgName - type: string - default: 'rg-PRODUCT-001' - - name: rgLocation - type: string - default: "West US 3" - - name: sqlName - type: string - default: "sql-PRODUCT-ENVIRONMENT-001" - - name: sqlUser - type: string - default: "LocalAdmin" - - name: sqlPassword - type: string - - name: sqldbName - type: string - default: "sqldb-PRODUCT-ENVIRONMENT-001" - - name: sqldbSku - type: string - default: "Basic" - - name: startIpAddress - type: string - default: "0.0.0.0" - - name: endIpAddress - type: string - default: "0.0.0.0" - -steps: - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.sqldbName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/sqldb-sqldatabase.json" - csmParametersFile: "${{ parameters.armPath }}/sqldb-sqldatabase.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.sqldbName }}" -sku "${{ parameters.sqldbSku }}" -adminLogin "${{ parameters.sqlUser }}" -adminPassword "${{ parameters.sqlPassword }}" -sqlName "${{ parameters.sqlName }}" -location "${{ parameters.rgLocation }}" -startIpAddress "${{ parameters.startIpAddress }}" -endIpAddress "${{ parameters.endIpAddress }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.sqldbName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/sqldb-sqldatabase.json" - csmParametersFile: "${{ parameters.armPath }}/sqldb-sqldatabase.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.sqldbName }}" -sku "${{ parameters.sqldbSku }}" -adminLogin "${{ parameters.sqlUser }}" -adminPassword "${{ parameters.sqlPassword }}" -sqlName "${{ parameters.sqlName }}" -location "${{ parameters.rgLocation }}" -startIpAddress "${{ parameters.startIpAddress }}" -endIpAddress "${{ parameters.endIpAddress }}" \ No newline at end of file diff --git a/.azuredevops/steps/stapp-infrastructure-steps.yml b/.azuredevops/steps/stapp-infrastructure-steps.yml deleted file mode 100644 index 7ca90be..0000000 --- a/.azuredevops/steps/stapp-infrastructure-steps.yml +++ /dev/null @@ -1,59 +0,0 @@ -parameters: - - name: armPath - type: string - default: "." - - name: subscriptionId - type: string - default: "00000000-0000-0000-0000-000000000000" - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - - name: rgName - type: string - default: 'rg-PRODUCT-001' - - name: rgLocation - type: string - default: "West US 3" - - name: environmentApp - type: string - default: 'Development' - - name: stappName - type: string - default: "stapp-PRODUCT-ENVIRONMENT-001" - - name: stappSku - type: string - default: "Free" - - name: repositoryUrl - type: string - default: "https://github.com/MYORG/MYREPO" - - name: branch - type: string - default: "main" - - name: repositoryToken - type: string - default: "GITHUB_PERSONAL_ACCESS_TOKEN" - - name: appLocation - type: string - default: "/src" - - name: apiLocation - type: string - default: "/api" - - name: appArtifactLocation - type: string - default: "build" - -steps: - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.stappName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/stapp-staticwebapp.json" - csmParametersFile: "${{ parameters.armPath }}/stapp-staticwebapp.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.stappName }}" -location "${{ parameters.rgLocation }}" -sku "${{ parameters.stappSku }}" -repositoryUrl "${{ parameters.repositoryUrl }}" -branch "${{ parameters.branch }}" -repositoryToken "${{ parameters.repositoryToken }}" -appLocation "${{ parameters.appLocation }}" -apiLocation "${{ parameters.apiLocation }}" -appArtifactLocation "${{ parameters.appArtifactLocation }}" \ No newline at end of file diff --git a/.azuredevops/steps/web-deploy-steps.yml b/.azuredevops/steps/web-deploy-steps.yml deleted file mode 100644 index a8fa1bb..0000000 --- a/.azuredevops/steps/web-deploy-steps.yml +++ /dev/null @@ -1,35 +0,0 @@ -parameters: - - name: artifactsPath - type: string - default: '.' - - name: archiveFile - type: string - default: '*.zip' - - name: webName - type: string - default: 'web-PRODUCT-ENVIRONMENT-001' - - name: subscriptionId - type: string - default: "00000000-0000-0000-0000-000000000000" - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - -steps: - - task: DownloadBuildArtifacts@0 - inputs: - buildType: 'current' - downloadType: 'single' - artifactName: 'drop' - downloadPath: '${{ parameters.artifactsPath }}' - - - task: AzureRmWebAppDeployment@4 - displayName: 'Deploying ${{ parameters.webName }}' - inputs: - ConnectionType: 'AzureRM' - ConnectedServiceName: ${{ parameters.subscriptionService }} - subscriptionName: ${{ parameters.subscriptionId }} - appType: 'webApp' - WebAppName: '${{ parameters.webName }}' - RemoveAdditionalFilesFlag: true - packageForLinux: '${{ parameters.artifactsPath }}/**/${{ parameters.archiveFile }}' diff --git a/.azuredevops/steps/web-infrastructure-steps.yml b/.azuredevops/steps/web-infrastructure-steps.yml deleted file mode 100644 index 61872f8..0000000 --- a/.azuredevops/steps/web-infrastructure-steps.yml +++ /dev/null @@ -1,89 +0,0 @@ -parameters: - - name: armPath - type: string - default: "." - - name: subscriptionId - type: string - default: "00000000-0000-0000-0000-000000000000" - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - - name: rgName - type: string - default: 'rg-PRODUCT-001' - - name: rgLocation - type: string - default: "West US 3" - - name: environmentApp - type: string - default: 'Development' - - name: planName - type: string - default: "plan-webjob-PRODUCT-ENVIRONMENT-001" - - name: planSku - type: string - default: "F1" - - name: webName - type: string - default: "webjob-PRODUCT-ENVIRONMENT-001" - -steps: - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.planName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/plan-appplan.json" - csmParametersFile: "${{ parameters.armPath }}/plan-appplan.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.planName }}" -sku "${{ parameters.planSku }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.planName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/plan-appplan.json" - csmParametersFile: "${{ parameters.armPath }}/plan-appplan.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.planName }}" -sku "${{ parameters.planSku }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.webName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/web-webapp.json" - csmParametersFile: "${{ parameters.armPath }}/web-webapp.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.webName }}" -planName "${{ parameters.planName }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.webName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/web-webapp.json" - csmParametersFile: "${{ parameters.armPath }}/web-webapp.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.webName }}" -planName "${{ parameters.planName }}" \ No newline at end of file diff --git a/.azuredevops/steps/web-settings-steps.yml b/.azuredevops/steps/web-settings-steps.yml deleted file mode 100644 index 873e557..0000000 --- a/.azuredevops/steps/web-settings-steps.yml +++ /dev/null @@ -1,48 +0,0 @@ -parameters: - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - - name: environmentApp - type: string - default: 'Development' - - name: webName - type: string - default: "web-PRODUCT-ENVIRONMENT-001" - - name: appSettings - type: object - default: - - name: 'APPINSIGHTS_INSTRUMENTATIONKEY' - value: '$(appiKey)' - slotSetting: false - - name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' - value: '$(appiConnection)' - slotSetting: false - - name: 'ASPNETCORE_ENVIRONMENT' - value: '${{ variables.environmentApp}}' - slotSetting: false - - name: 'NODE_ENVIRONMENT' - value: '${{ variables.environmentApp}}' - slotSetting: false - - name: '$(appcsEnvironmentVariable)' - value: '$(appcsConnection)' - slotSetting: false - - name: '$(stEnvironmentVariable)' - value: '$(stConnection)' - slotSetting: false - -steps: - - ${{ each appSetting in parameters.appSettings }}: - - task: AzureAppServiceSettings@1 - displayName: 'set_appsettings ${{ appSetting.name }}' - inputs: - azureSubscription: ${{ parameters.subscriptionService }} - appName: ${{ parameters.webName }} - appSettings: | - [ - { - "name": "${{ appSetting.name }}", - "value": "${{ appSetting.value }}", - "slotSetting": "${{ appSetting.slotSetting }}" - } - ] - slotName: ${{ parameters.slotName }} \ No newline at end of file diff --git a/.azuredevops/steps/webjob-build-steps.yml b/.azuredevops/steps/webjob-build-steps.yml deleted file mode 100644 index df0e27b..0000000 --- a/.azuredevops/steps/webjob-build-steps.yml +++ /dev/null @@ -1,96 +0,0 @@ -parameters: - - name: artifactsPath - type: string - default: '.' - - name: archiveFile - type: string - default: '*.zip' - - name: srcPath - type: string - default: '.' - - name: webjobProject - type: string - default: '*.csproj' - - name: webjobName - type: string - - name: nugetPathFile - type: string - default: ".nuget/NuGet.config" - - name: buildConfiguration - type: string - default: "Release" - - name: tempPath - type: string - default: './ymltemp' - - name: webjobType - type: string - default: 'triggered' #'continuous' - - name: webjobPath - type: string - default: '/App_Data/jobs' - - name: cronSchedule - type: string - default: '0 */5 * * * *' - - name: dotnetVersion - type: string - default: "7.x" - -steps: - - task: DeleteFiles@1 - inputs: - SourceFolder: '${{ parameters.tempPath }}' - Contents: '**/*' - RemoveDotFiles: true - - - task: UseDotNet@2 - displayName: 'use .net ${{ parameters.dotnetVersion }}' - inputs: - packageType: 'sdk' - version: ${{ parameters.dotnetVersion }} - includePreviewVersions: true - - - task: DotNetCoreCLI@2 - displayName: 'dotnet restore' - inputs: - command: 'restore' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.webjobProject }} - feedsToUse: config - nugetConfigPath: ${{ parameters.nugetPathFile }} - - - task: DotNetCoreCLI@2 - displayName: 'dotnet build' - inputs: - command: 'build' - projects: | - ${{ parameters.srcPath }}/**/${{ parameters.webjobProject }} - arguments: '--output ${{ parameters.tempPath }}${{ parameters.webjobPath }}/${{ parameters.webjobType }}/${{ parameters.webjobName }} --configuration ${{ parameters.buildConfiguration }}' - - - task: PowerShell@2 - displayName: 'create settings.job' - inputs: - targetType: 'inline' - script: | - New-Item '${{ parameters.tempPath }}${{ parameters.webjobPath }}/${{ parameters.webjobType }}/${{ parameters.webjobName }}/settings.job' - Set-Content '${{ parameters.tempPath }}${{ parameters.webjobPath }}/${{ parameters.webjobType }}/${{ parameters.webjobName }}/settings.job' '{"schedule":"${{ parameters.cronSchedule }}"}' - - - task: ArchiveFiles@2 - displayName: 'Archive ${{ parameters.archiveFile }}' - inputs: - rootFolderOrFile: '${{ parameters.tempPath }}' - includeRootFolder: false - archiveType: zip - archiveFile: '${{ parameters.artifactsPath }}/${{ parameters.archiveFile }}' - replaceExistingArchive: true - - - task: DeleteFiles@1 - inputs: - SourceFolder: '${{ parameters.tempPath }}' - Contents: '**/*' - RemoveDotFiles: true - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: drop' - inputs: - pathToPublish: '${{ parameters.artifactsPath }}' - artifactName: drop \ No newline at end of file diff --git a/.azuredevops/steps/webjob-deploy-steps.yml b/.azuredevops/steps/webjob-deploy-steps.yml deleted file mode 100644 index 63986a0..0000000 --- a/.azuredevops/steps/webjob-deploy-steps.yml +++ /dev/null @@ -1,35 +0,0 @@ -parameters: - - name: artifactsPath - type: string - default: '.' - - name: archiveFile - type: string - default: '*.zip' - - name: webjobName - type: string - default: 'web-PRODUCT-ENVIRONMENT-001' - - name: subscriptionId - type: string - default: "00000000-0000-0000-0000-000000000000" - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - -steps: - - task: DownloadBuildArtifacts@0 - inputs: - buildType: 'current' - downloadType: 'single' - artifactName: 'drop' - downloadPath: '${{ parameters.artifactsPath }}' - - - task: AzureRmWebAppDeployment@4 - displayName: 'Deploying ${{ parameters.webJobName }}' - inputs: - ConnectionType: 'AzureRM' - ConnectedServiceName: ${{ parameters.subscriptionService }} - subscriptionName: ${{ parameters.subscriptionId }} - appType: 'webApp' - WebAppName: '${{ parameters.webjobName }}' - RemoveAdditionalFilesFlag: true - packageForLinux: '${{ parameters.artifactsPath }}/**/${{ parameters.archiveFile }}' diff --git a/.azuredevops/steps/webjob-infrastructure-steps.yml b/.azuredevops/steps/webjob-infrastructure-steps.yml deleted file mode 100644 index e19138a..0000000 --- a/.azuredevops/steps/webjob-infrastructure-steps.yml +++ /dev/null @@ -1,89 +0,0 @@ -parameters: - - name: armPath - type: string - default: "." - - name: subscriptionId - type: string - default: "00000000-0000-0000-0000-000000000000" - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - - name: rgName - type: string - default: 'rg-PRODUCT-001' - - name: rgLocation - type: string - default: "West US 3" - - name: environmentApp - type: string - default: 'Development' - - name: planName - type: string - default: "plan-webjob-PRODUCT-ENVIRONMENT-001" - - name: planSku - type: string - default: "F1" - - name: webjobName - type: string - default: "webjob-PRODUCT-ENVIRONMENT-001" - -steps: - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.planName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/plan-appplan.json" - csmParametersFile: "${{ parameters.armPath }}/plan-appplan.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.planName }}" -sku "${{ parameters.planSku }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.planName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/plan-appplan.json" - csmParametersFile: "${{ parameters.armPath }}/plan-appplan.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.planName }}" -sku "${{ parameters.planSku }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Validate ${{ parameters.webjobName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/web-webapp.json" - csmParametersFile: "${{ parameters.armPath }}/web-webapp.parameters.json" - deploymentMode: "Validation" - overrideParameters: -name "${{ parameters.webjobName }}" -planName "${{ parameters.planName }}" - - - task: AzureResourceManagerTemplateDeployment@3 - displayName: "Deploy ${{ parameters.webjobName }}" - inputs: - deploymentScope: "Resource Group" - azureResourceManagerConnection: "${{ parameters.subscriptionService }}" - subscriptionId: "${{ parameters.subscriptionId }}" - action: "Create Or Update Resource Group" - resourceGroupName: "${{ parameters.rgName }}" - location: "${{ parameters.rgLocation }}" - templateLocation: "Linked artifact" - csmFile: "${{ parameters.armPath }}/web-webapp.json" - csmParametersFile: "${{ parameters.armPath }}/web-webapp.parameters.json" - deploymentMode: "Incremental" - overrideParameters: -name "${{ parameters.webjobName }}" -planName "${{ parameters.planName }}" \ No newline at end of file diff --git a/.azuredevops/steps/webjob-settings-steps.yml b/.azuredevops/steps/webjob-settings-steps.yml deleted file mode 100644 index 25d6891..0000000 --- a/.azuredevops/steps/webjob-settings-steps.yml +++ /dev/null @@ -1,48 +0,0 @@ -parameters: - - name: subscriptionService - type: string - default: 'COMPANY-PRODUCT-001' - - name: environmentApp - type: string - default: 'Development' - - name: webjobName - type: string - default: "webjob-PRODUCT-ENVIRONMENT-001" - - name: appSettings - type: object - default: - - name: 'APPINSIGHTS_INSTRUMENTATIONKEY' - value: '$(appiKey)' - slotSetting: false - - name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' - value: '$(appiConnection)' - slotSetting: false - - name: 'ASPNETCORE_ENVIRONMENT' - value: '${{ variables.environmentApp}}' - slotSetting: false - - name: 'AzureWebJobsDashboard' - value: '$(stConnection)' - slotSetting: false - - name: '$(appcsEnvironmentVariable)' - value: '$(appcsConnection)' - slotSetting: false - - name: '$(stEnvironmentVariable)' - value: '$(stConnection)' - slotSetting: false - -steps: - - ${{ each appSetting in parameters.appSettings }}: - - task: AzureAppServiceSettings@1 - displayName: 'set_appsettings ${{ appSetting.name }}' - inputs: - azureSubscription: ${{ parameters.subscriptionService }} - appName: ${{ parameters.webjobName }} - appSettings: | - [ - { - "name": "${{ appSetting.name }}", - "value": "${{ appSetting.value }}", - "slotSetting": "${{ appSetting.slotSetting }}" - } - ] - slotName: ${{ parameters.slotName }} diff --git a/.azuredevops/steps/webstatic-pack-steps.yml b/.azuredevops/steps/webstatic-pack-steps.yml deleted file mode 100644 index e6e7384..0000000 --- a/.azuredevops/steps/webstatic-pack-steps.yml +++ /dev/null @@ -1,62 +0,0 @@ -parameters: -- name: artifactsPath - type: string - default: "." -- name: archiveFile - type: string - default: "archive.zip" -- name: srcPath - type: string - default: "." -- name: scriptsPath - type: string - default: "../scripts" -- name: tempPath - type: string - default: './sitetemp' - -steps: - - task: DeleteFiles@1 - displayName: 'delete tempPath' - inputs: - SourceFolder: '${{ parameters.tempPath }}' - Contents: '**/*' - RemoveDotFiles: true - - - task: CopyFiles@2 - displayName: 'copy files' - inputs: - SourceFolder: '${{ parameters.srcPath }}' - Contents: '**' - TargetFolder: '${{ parameters.tempPath }}' - CleanTargetFolder: true - OverWrite: true - - - task: DeleteFiles@1 - displayName: 'delete .github files' - inputs: - SourceFolder: '${{ parameters.tempPath }}' - Contents: '.github/**/*' - RemoveDotFiles: true - - - task: ArchiveFiles@2 - displayName: 'Archive ${{ parameters.archiveFile }}' - inputs: - rootFolderOrFile: '${{ parameters.tempPath }}' - includeRootFolder: false - archiveType: zip - archiveFile: '${{ parameters.artifactsPath }}/${{ parameters.archiveFile }}' - replaceExistingArchive: true - - - task: DeleteFiles@1 - displayName: 'delete tempPath' - inputs: - SourceFolder: '${{ parameters.tempPath }}' - Contents: '**/*' - RemoveDotFiles: true - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: drop' - inputs: - pathToPublish: '${{ parameters.artifactsPath }}' - artifactName: drop \ No newline at end of file diff --git a/.azuredevops/variables/common-ci.yml b/.azuredevops/variables/common-ci.yml deleted file mode 100644 index 5e13d5c..0000000 --- a/.azuredevops/variables/common-ci.yml +++ /dev/null @@ -1,17 +0,0 @@ -variables: - - name: buildConfiguration - value: 'Release' - - name: scriptsPath - value: '$(System.DefaultWorkingDirectory)/.azure-devops/scripts' - - name: srcPath - value: '$(System.DefaultWorkingDirectory)/src' - - name: srcSolution - value: '**/HWeatTeams.sln' - - name: srcProject - value: '**/Presentation.WebApi.csproj' - - name: testProject - value: '**/Application.Specs.Unit.csproj' - - name: nugetConfig - value: '.nuget/NuGet.config' - - name: dotnetVersion - value: '7.x' \ No newline at end of file diff --git a/.azuredevops/variables/development-cd.yml b/.azuredevops/variables/development-cd.yml deleted file mode 100644 index 52deadd..0000000 --- a/.azuredevops/variables/development-cd.yml +++ /dev/null @@ -1,15 +0,0 @@ -variables: - - group: aacn-PRODUCT-dev - - name: environmentApp - value: 'Development' - - name: siteDns - value: 'FQDN' - - name: thumbprint - value: 'THUMBPRINT' - - name: productName - value: 'PRODUCT' - -# Key Vault -# sqlUser -# sqlPassword -# sqlConnection diff --git a/.azuredevops/variables/development-infrastructure.yml b/.azuredevops/variables/development-infrastructure.yml deleted file mode 100644 index 6d44cb8..0000000 --- a/.azuredevops/variables/development-infrastructure.yml +++ /dev/null @@ -1,25 +0,0 @@ -variables: - - name: environmentApp - value: 'Development' - - name: rgName - value: 'rg-PRODUCT-dev-001' - - name: rgLocation - value: 'westus2' - - name: rgSharedName - value: 'rg-SHARED-dev-001' - - name: rgSharedLocation - value: 'westus2' - - name: kvName - value: 'kv-PRODUCT-dev-001' - - name: serviceConnectionIdentityObjectId - value: '' - - name: deployApprovers - value: 'approver.email@goodtocode.com;' - - name: sharedTemplate - value: '.azure/templates/landingzone-shared.bicep' - - name: sharedParameters - value: '.azure/variables/landingzone-shared-development.bicepparam' - - name: landingZoneTemplate - value: '.azure/templates/landingzone-appservice.bicep' - - name: landingZoneParameters - value: '.azure/variables/landingzone-appservice-development.bicepparam' \ No newline at end of file diff --git a/.azuredevops/variables/production-cd.yml b/.azuredevops/variables/production-cd.yml deleted file mode 100644 index 72c654e..0000000 --- a/.azuredevops/variables/production-cd.yml +++ /dev/null @@ -1,15 +0,0 @@ -variables: - - group: aacn-PRODUCT-prod - - name: environmentApp - value: 'Production' - - name: siteDns - value: 'FQDN' - - name: thumbprint - value: 'THUMBPRINT' - - name: productName - value: 'PRODUCT' - -# Key Vault -# sqlUser -# sqlPassword -# sqlConnection diff --git a/.azuredevops/variables/production-infrastructure.yml b/.azuredevops/variables/production-infrastructure.yml deleted file mode 100644 index 47876e8..0000000 --- a/.azuredevops/variables/production-infrastructure.yml +++ /dev/null @@ -1,25 +0,0 @@ -variables: - - name: environmentApp - value: 'Production' - - name: rgName - value: 'rg-PRODUCT-prod-001' - - name: rgLocation - value: 'westus2' - - name: rgSharedName - value: 'rg-SHARED-prod-001' - - name: rgSharedLocation - value: 'westus2' - - name: kvName - value: 'kv-PRODUCT-prod-001' - - name: serviceConnectionIdentityObjectId - value: '' - - name: deployApprovers - value: 'approver.email@goodtocode.com;' - - name: sharedTemplate - value: '.azure/templates/landingzone-shared.bicep' - - name: sharedParameters - value: '.azure/variables/landingzone-shared-production.bicepparam' - - name: landingZoneTemplate - value: '.azure/templates/landingzone-appservice.bicep' - - name: landingZoneParameters - value: '.azure/variables/landingzone-appservice-production.bicepparam' \ No newline at end of file diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..8901bc9 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,43 @@ +# Copilot Instructions for Semantic Kernel Quick-Start + +## Project Overview +- **Semantic Kernel Quick-Start** is a C# solution for AI-powered monitoring, classification, and mitigation of digital assets. +- Built on a clean architecture: ASP.NET Core Web API backend, Blazor WebAssembly frontend, SQL Server storage, and Microsoft Semantic Kernel for AI/LLM integration. +- Infrastructure is managed via Azure Bicep and deployed using GitHub Actions. + +## Key Architectural Patterns +- **Frontend:** `src/Presentation.Blazor/` (Blazor WebAssembly) +- **Backend:** `src/Presentation.WebApi/` (ASP.NET Core Web API) +- **Core Logic:** `src/Core.Application/`, `src/Core.Domain/` +- **AI Integration:** `src/Infrastructure.SemanticKernel/` (Semantic Kernel plugins, prompt orchestration) +- **Persistence:** `src/Infrastructure.SqlServer/` (SQL Server, migrations) +- **IaC:** `data/` (SQL), Azure Bicep in deployment scripts + +## Developer Workflows +- **Build:** Use `build.cmd` or `build.sh` in `src/` to build the solution. +- **Test:** Integration specs in `src/gherkin Tests.Specs.Integration/` and `src/Tests.Specs.Integration/`. +- **Run:** Launch via Visual Studio or `dotnet run` from solution root. +- **CI/CD:** Managed by GitHub Actions (`.github/workflows/`). +- **IaC Deploy:** See `can-digital-insights-iac.yml` for infrastructure deployment. + +## Naming & Conventions +- **C# code:** PascalCase for types/methods/properties, _camelCase for private fields, camelCase for locals. +- **Database:** PascalCase plural for tables, PascalCase for columns, `Id` for PK, `RelatedEntityId` for FK. +- **API:** Kebab-case, plural nouns for endpoints (e.g., `/api/digital-agents`). +- **Files/Folders:** C# files match main class, folders use PascalCase, config/docs use lowercase-hyphens. +- See `docs/naming-conventions.md` for details. + +## Integration & Extensibility +- **Semantic Kernel:** Add plugins in `src/Infrastructure.SemanticKernel/Plugins/`. +- **External Integrations:** Use `src/Core.Application/Common/` and `src/Infrastructure.SemanticKernel/` for connectors. +- **RBAC & Security:** Enforced in API layer, see `ConfigureServicesAuth.cs`. + +## References +- [README.md](../README.md): Project overview and getting started + +## Examples +- To add a new agent plugin: create in `src/Infrastructure.SemanticKernel/Plugins/`, register in `ConfigureServices.cs`. +- To add a new API endpoint: implement in `src/Presentation.WebApi/`, follow API naming conventions. + +--- +For further details, always check the referenced docs and existing code patterns in the relevant folders. \ No newline at end of file diff --git a/.github/scripts/Get-AzureAd.ps1 b/.github/scripts/Get-AzureAd.ps1 deleted file mode 100644 index 00240f3..0000000 --- a/.github/scripts/Get-AzureAd.ps1 +++ /dev/null @@ -1,50 +0,0 @@ -#################################################################################### -# To execute -# 1. Run Powershell as ADMINistrator -# 2. In powershell, set security polilcy for this script: -# Set-ExecutionPolicy Unrestricted -Scope Process -Force -# 3. Change directory to the script folder: -# CD C:\Temp (wherever your script is) -# 4. In powershell, run script: -# .\Get-AzureAd.ps1 -#################################################################################### - -param ( -) - -#################################################################################### -Set-ExecutionPolicy Unrestricted -Scope Process -Force -$VerbosePreference = 'SilentlyContinue' # 'SilentlyContinue' # 'Continue' -[String]$ThisScript = $MyInvocation.MyCommand.Path -[String]$ThisDir = Split-Path $ThisScript -Set-Location $ThisDir # Ensure our location is correct, so we can use relative paths -Write-Host "*****************************" -Write-Host "*** Starting: $ThisScript On: $(Get-Date)" -Write-Host "*****************************" -#################################################################################### -# *** -# *** Imports -# *** -Write-Host "*** Imports ***" -Import-Module "./System.psm1" -Install-Module -Name Az -AllowClobber -Scope CurrentUser # All -Install-Module -Name AzureAD -AllowClobber -Scope CurrentUser -#Install-Module -Name Az.Accounts -AllowClobber -Scope CurrentUser -#Install-Module -Name Az.Billing -AllowClobber -Scope CurrentUser -#Install-Module -Name Az.Resources -AllowClobber -Scope CurrentUser - -# *** -# *** Auth -# *** -Write-Host "*** Auth ***" -Connect-AzureAD -TenantId "00000000-0000-0000-0000-000000000000" - -# Find service principal object ID -Write-Host "*** Get-AzureADServicePrincipal ***" -$oid = $(Get-AzureADServicePrincipal -Filter "AppId eq '00000000-0000-0000-0000-000000000000'").ObjectId -Write-host "$oid" -#$(Get-AzureADServicePrincipal -Filter "DisplayName eq 'testapp'").ObjectId -# Find user object ID -#$(Get-AzureADUser -Filter "UserPrincipalName eq 'myuser@contoso.com'").ObjectId -# Find a security group object ID -#$(Get-AzureADGroup -Filter "DisplayName eq 'mygroup'").ObjectId \ No newline at end of file diff --git a/.github/scripts/Install-AzureCli.ps1 b/.github/scripts/Install-AzureCli.ps1 deleted file mode 100644 index 039eb74..0000000 --- a/.github/scripts/Install-AzureCli.ps1 +++ /dev/null @@ -1 +0,0 @@ -Invoke-WebRequest -Uri https://aka.ms/installazurecliwindows -OutFile .\AzureCLI.msi; Start-Process msiexec.exe -Wait -ArgumentList '/I AzureCLI.msi /quiet' \ No newline at end of file diff --git a/.github/scripts/New-AzResourceGroupDeployment.ps1 b/.github/scripts/New-AzResourceGroupDeployment.ps1 deleted file mode 100644 index 4ecf8f3..0000000 --- a/.github/scripts/New-AzResourceGroupDeployment.ps1 +++ /dev/null @@ -1,7 +0,0 @@ -$rgName='rg-PRODUCT-ENVIRONMENT-001' -$rgLocation='westus2' -# rg- -New-AzResourceGroup -Name $rgName -Location $rgLocation - -# apim- -New-AzResourceGroupDeployment -ResourceGroupName $rgName -TemplateFile ./bicep/apim-apimanagement.bicep -publisherEmail "" -publisherName "" \ No newline at end of file diff --git a/.github/scripts/Remove-LogicApp.ps1 b/.github/scripts/Remove-LogicApp.ps1 deleted file mode 100644 index 852efd1..0000000 --- a/.github/scripts/Remove-LogicApp.ps1 +++ /dev/null @@ -1,50 +0,0 @@ -#----------------------------------------------------------------------- -# Remove-StorageAccount [-Path []] [-VersionToReplace []] -# -# Example: .\Remove-StorageAccount -TenantId -SubscriptionId -ResourceGroup -StorageAccount -#----------------------------------------------------------------------- - -# *** -# *** Parameters -# *** -param -( - [string] $TenantId=$(throw '-TenantId is a required parameter. (00000000-0000-0000-0000-000000000000)'), - [string] $SubscriptionId=$(throw '-TenantId is a required parameter. (00000000-0000-0000-0000-000000000000)'), - [string] $ResourceGroup=$(throw '-ResourceGroup is a required parameter. (rg-PRODUCT-ENVIRONMENT-001)'), - [string] $Name=$(throw '-Name is a required parameter. (logic-PRODUCT-ENVIRONMENT-001)') -) - -# *** -# *** Initialize -# *** -if ($IsWindows) { Set-ExecutionPolicy Unrestricted -Scope Process -Force } -$VerbosePreference = 'SilentlyContinue' #'Continue' -[String]$ThisScript = $MyInvocation.MyCommand.Path -[String]$ThisDir = Split-Path $ThisScript -[DateTime]$Now = Get-Date -Set-Location $ThisDir # Ensure our location is correct, so we can use relative paths -Write-Host "*****************************" -Write-Host "*** Starting: $ThisScript on $Now" -Write-Host "*****************************" -# Imports -Import-Module "./System.psm1" -Install-Module -Name Az.Accounts -AllowClobber -Scope CurrentUser -Install-Module -Name Az.LogicApp -AllowClobber -Scope CurrentUser - -# *** -# *** Locals -# *** - -# *** -# *** Auth -# *** -Write-Host "*** Auth ***" - -Connect-AzAccount -Tenant $TenantId -Subscription $SubscriptionId - -# *** -# *** Execute -# *** -Get-AzLogicApp -ResourceGroupName $ResourceGroup -Name $Name -Remove-AzLogicApp -ResourceGroupName $ResourceGroup -Name $Name \ No newline at end of file diff --git a/.github/scripts/Remove-SQLServer.ps1 b/.github/scripts/Remove-SQLServer.ps1 deleted file mode 100644 index 224a0aa..0000000 --- a/.github/scripts/Remove-SQLServer.ps1 +++ /dev/null @@ -1,50 +0,0 @@ -#----------------------------------------------------------------------- -# Remove-SQLServer [-Path []] [-VersionToReplace []] -# -# Example: .\Remove-StorageAccount -TenantId -SubscriptionId -ResourceGroup -StorageAccount -#----------------------------------------------------------------------- - -# *** -# *** Parameters -# *** -param -( - [string] $TenantId=$(throw '-TenantId is a required parameter. (00000000-0000-0000-0000-000000000000)'), - [string] $SubscriptionId=$(throw '-TenantId is a required parameter. (00000000-0000-0000-0000-000000000000)'), - [string] $ResourceGroup=$(throw '-ResourceGroup is a required parameter. (rg-PRODUCT-ENVIRONMENT-001)'), - [string] $SQLServer=$(throw '-SQLServer is a required parameter. (sql-PRODUCT-ENVIRONMENT-001)') -) - -# *** -# *** Initialize -# *** -if ($IsWindows) { Set-ExecutionPolicy Unrestricted -Scope Process -Force } -$VerbosePreference = 'SilentlyContinue' #'Continue' -[String]$ThisScript = $MyInvocation.MyCommand.Path -[String]$ThisDir = Split-Path $ThisScript -[DateTime]$Now = Get-Date -Set-Location $ThisDir # Ensure our location is correct, so we can use relative paths -Write-Host "*****************************" -Write-Host "*** Starting: $ThisScript on $Now" -Write-Host "*****************************" -# Imports -Import-Module "./System.psm1" -Install-Module -Name Az.Accounts -AllowClobber -Scope CurrentUser -Install-Module -Name Az.Sql -AllowClobber -Scope CurrentUser - -# *** -# *** Locals -# *** - -# *** -# *** Auth -# *** -Write-Host "*** Auth ***" - -Connect-AzAccount -Tenant $TenantId -Subscription $SubscriptionId - -# *** -# *** Execute -# *** -Get-AzSqlServer -ResourceGroupName $ResourceGroup -Remove-AzSqlServer -ResourceGroupName $ResourceGroup -AccountName $StorageAccount \ No newline at end of file diff --git a/.github/scripts/Remove-StorageAccount.ps1 b/.github/scripts/Remove-StorageAccount.ps1 deleted file mode 100644 index 549a7c6..0000000 --- a/.github/scripts/Remove-StorageAccount.ps1 +++ /dev/null @@ -1,50 +0,0 @@ -#----------------------------------------------------------------------- -# Remove-StorageAccount [-Path []] [-VersionToReplace []] -# -# Example: .\Remove-StorageAccount -TenantId -SubscriptionId -ResourceGroup -StorageAccount -#----------------------------------------------------------------------- - -# *** -# *** Parameters -# *** -param -( - [string] $TenantId=$(throw '-TenantId is a required parameter. (00000000-0000-0000-0000-000000000000)'), - [string] $SubscriptionId=$(throw '-TenantId is a required parameter. (00000000-0000-0000-0000-000000000000)'), - [string] $ResourceGroup=$(throw '-ResourceGroup is a required parameter. (rg-PRODUCT-ENVIRONMENT-001)'), - [string] $StorageAccount=$(throw '-StorageAccount is a required parameter. (stPRODUCTENVIRONMENT001)') -) - -# *** -# *** Initialize -# *** -if ($IsWindows) { Set-ExecutionPolicy Unrestricted -Scope Process -Force } -$VerbosePreference = 'SilentlyContinue' #'Continue' -[String]$ThisScript = $MyInvocation.MyCommand.Path -[String]$ThisDir = Split-Path $ThisScript -[DateTime]$Now = Get-Date -Set-Location $ThisDir # Ensure our location is correct, so we can use relative paths -Write-Host "*****************************" -Write-Host "*** Starting: $ThisScript on $Now" -Write-Host "*****************************" -# Imports -Import-Module "./System.psm1" -Install-Module -Name Az.Accounts -AllowClobber -Scope CurrentUser -Install-Module -Name Az.Resources -AllowClobber -Scope CurrentUser -Install-Module -Name Az.Storage -AllowClobber -Scope CurrentUser - -# *** -# *** Locals -# *** - -# *** -# *** Auth -# *** -Write-Host "*** Auth ***" - -Connect-AzAccount -Tenant $TenantId -Subscription $SubscriptionId - -# *** -# *** Execute -# *** -Remove-AzStorageAccount -ResourceGroupName $ResourceGroup -AccountName $StorageAccount \ No newline at end of file diff --git a/.github/scripts/Set-ApiConnectorAuth.ps1 b/.github/scripts/Set-ApiConnectorAuth.ps1 deleted file mode 100644 index 4f735a3..0000000 --- a/.github/scripts/Set-ApiConnectorAuth.ps1 +++ /dev/null @@ -1,79 +0,0 @@ -# From: https://raw.githubusercontent.com/logicappsio/LogicAppConnectionAuth/master/LogicAppConnectionAuth.ps1 -# MIT License - -Param( - [string] $ResourceGroupName = 'YourRG', - [string] $ResourceLocation = 'eastus | westus | etc.', - [string] $api = 'office365 | dropbox | dynamicscrmonline | etc.', - [string] $ConnectionName = 'YourConnectionName', - [string] $subscriptionId = '00000000-0000-0000-0000-000000000000', - [bool] $createConnection = $true -) - #region mini window, made by Scripting Guy Blog - Function Show-OAuthWindow { - Add-Type -AssemblyName System.Windows.Forms - - $form = New-Object -TypeName System.Windows.Forms.Form -Property @{Width=600;Height=800} - $web = New-Object -TypeName System.Windows.Forms.WebBrowser -Property @{Width=580;Height=780;Url=($url -f ($Scope -join "%20")) } - $DocComp = { - $Global:uri = $web.Url.AbsoluteUri - if ($Global:Uri -match "error=[^&]*|code=[^&]*") {$form.Close() } - } - $web.ScriptErrorsSuppressed = $true - $web.Add_DocumentCompleted($DocComp) - $form.Controls.Add($web) - $form.Add_Shown({$form.Activate()}) - $form.ShowDialog() | Out-Null - } - #endregion - -#login to get an access code - -Login-AzureRmAccount - -#select the subscription - -$subscription = Select-AzureRmSubscription -SubscriptionId $subscriptionId - -#if the connection wasn't alrady created via a deployment -if($createConnection) -{ - $connection = New-AzureRmResource -Properties @{"api" = @{"id" = "subscriptions/" + $subscriptionId + "/providers/Microsoft.Web/locations/" + $ResourceLocation + "/managedApis/" + $api}; "displayName" = $ConnectionName; } -ResourceName $ConnectionName -ResourceType "Microsoft.Web/connections" -ResourceGroupName $ResourceGroupName -Location $ResourceLocation -Force -} -#else (meaning the conneciton was created via a deployment) - get the connection -else{ -$connection = Get-AzureRmResource -ResourceType "Microsoft.Web/connections" -ResourceGroupName $ResourceGroupName -ResourceName $ConnectionName -} -Write-Host "connection status: " $connection.Properties.Statuses[0] - -$parameters = @{ - "parameters" = ,@{ - "parameterName"= "token"; - "redirectUrl"= "https://ema1.exp.azure.com/ema/default/authredirect" - } -} - -#get the links needed for consent -$consentResponse = Invoke-AzureRmResourceAction -Action "listConsentLinks" -ResourceId $connection.ResourceId -Parameters $parameters -Force - -$url = $consentResponse.Value.Link - -#prompt user to login and grab the code after auth -Show-OAuthWindow -URL $url - -$regex = '(code=)(.*)$' - $code = ($uri | Select-string -pattern $regex).Matches[0].Groups[2].Value - Write-output "Received an accessCode: $code" - -if (-Not [string]::IsNullOrEmpty($code)) { - $parameters = @{ } - $parameters.Add("code", $code) - # NOTE: errors ignored as this appears to error due to a null response - - #confirm the consent code - Invoke-AzureRmResourceAction -Action "confirmConsentCode" -ResourceId $connection.ResourceId -Parameters $parameters -Force -ErrorAction Ignore -} - -#retrieve the connection -$connection = Get-AzureRmResource -ResourceType "Microsoft.Web/connections" -ResourceGroupName $ResourceGroupName -ResourceName $ConnectionName -Write-Host "connection status now: " $connection.Properties.Statuses[0] \ No newline at end of file diff --git a/.github/scripts/Set-Version.ps1 b/.github/scripts/Set-Version.ps1 deleted file mode 100644 index f7c6f73..0000000 --- a/.github/scripts/Set-Version.ps1 +++ /dev/null @@ -1,82 +0,0 @@ -#----------------------------------------------------------------------- -# Set-Version [-Path []] [-VersionToReplace []] [-Type []] -# -# Example: .\Set-Version -Path \\source\path -Major 1 -#----------------------------------------------------------------------- - -# *** -# *** Parameters -# *** -param -( - [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] - [string] $Path=$(throw '-Path is a required parameter. i.e. $(Build.SourcesDirectory)'), - [Version] $VersionToReplace='1.0.0', - [String] $Major='-1', - [String] $Minor='-1', - [String] $Revision='-1', - [String] $Build='-1', - [String] $Patch='-1', - [String] $PreRelease='-1', - [String] $CommitHash='-1' -) - -# *** -# *** Initialize -# *** -if ($IsWindows) { Set-ExecutionPolicy Unrestricted -Scope Process -Force } -$VerbosePreference = 'SilentlyContinue' #'Continue' -[String]$ThisScript = $MyInvocation.MyCommand.Path -[String]$ThisDir = Split-Path $ThisScript -[DateTime]$Now = Get-Date -Write-Debug "*****************************" -Write-Debug "*** Starting: $ThisScript on $Now" -Write-Debug "*****************************" -# Imports -Import-Module "$ThisDir/System.psm1" - -# *** -# *** Validate and cleanse -# *** -If($IsWindows){ - $Path = Set-Unc -Path $Path -} - -# *** -# *** Locals -# *** - -# *** -# *** Execute -# *** -$Major = $Major.Replace('-1', $VersionToReplace.ToString().Substring(0,1)) # Static 1, 2, 3 -$Minor = $Minor.Replace('-1', (Get-Date -UFormat '%Y').ToString().Substring(2,2)) # Year YYYY 2023 -$Revision = $Revision.Replace('-1', (Get-Date -UFormat '%j').ToString()) # DayOfYear D[DD]1-365 -$Build = $Build.Replace('-1', (Get-Date -UFormat '%H%M').ToString()) # HrMin 1937 -$Patch = $Patch.Replace('-1', (Get-Date -UFormat '%m').ToString()) # Month mm -$PreRelease = $PreRelease.Replace('-1', '') # -alpha -$CommitHash = $CommitHash.Replace('-1', '') # +204ff0a - - -# Version Formats -$FileVersion = "$Major.$Minor.$Revision.$Build" # Ref: https://learn.microsoft.com/en-us/dotnet/standard/library-guidance/versioning -$AssemblyVersion = "$Major.$Minor.0.0" -$InformationalVersion = "$Major.$Minor.$Revision$PreRelease$CommitHash" -$SemanticVersion = "$Major.$Minor.$Patch$PreRelease" -Write-Debug "FileVersion: $FileVersion SemanticVersion: $SemanticVersion AssemblyVersion: $AssemblyVersion InformationalVersion: $InformationalVersion" - -# Package.json version -Update-LineByContains -Path $Path -Contains 'version' -Line """version"": ""$FileVersion""," -Include package.json -# OpenApiConfigurationOptions.cs version -Update-LineByContains -Path $Path -Contains 'Version' -Line "Version = ""$AssemblyVersion""," -Include OpenApiConfigurationOptions.cs -# *.csproj C# Project files -Update-ContentsByTag -Path $Path -Value $FileVersion -Open '' -Close '' -Include *.csproj -# *.nuspec NuGet packages -Update-ContentsByTag -Path $Path -Value $SemanticVersion -Open '' -Close '' -Include *.nuspec -# Assembly.cs C# assembly manifest -Update-LineByContains -Path $Path -Contains "FileVersion(" -Line "[assembly: FileVersion(""$FileVersion"")]" -Include AssemblyInfo.cs -Update-LineByContains -Path $Path -Contains "AssemblyVersion(" -Line "[assembly: AssemblyVersion(""$AssemblyVersion"")]" -Include AssemblyInfo.cs -# *.vsixmanifest VSIX Visual Studio Templates -Update-TextByContains -Path $Path -Contains " +.EXAMPLE +pwsh ./Remove-NugetListing.ps1 -PackageName My.Package -ApiKey -Source "https://my.custom.nuget/feed/v3/index.json" +#> + +param( + [Parameter(Mandatory)] + [string]$PackageName, + + [Parameter(Mandatory)] + [string]$ApiKey, + + [string]$Source = "https://api.nuget.org/v3/index.json" +) + +$ErrorActionPreference = 'Stop' + +# Build the flat container API URL +$flatApiUrl = "https://api.nuget.org/v3-flatcontainer/$($PackageName.ToLowerInvariant())/index.json" + +Write-Host "Querying all versions for package: $PackageName" -ForegroundColor Cyan + +try { + $response = Invoke-RestMethod -Uri $flatApiUrl + $versions = $response.versions +} catch { + Write-Error "Failed to query package versions. Ensure the package exists and the name is correct. $_" + exit 1 +} + +if (-not $versions) { + Write-Warning "No versions found for package $PackageName." + exit 0 +} + +foreach ($version in $versions) { + Write-Host "Deleting $PackageName $version from source $Source..." -ForegroundColor Yellow + try { + dotnet nuget delete $PackageName $version --source $Source --non-interactive --api-key $ApiKey --no-service-endpoint + Write-Host "Successfully deleted $PackageName $version" -ForegroundColor Green + } catch { + Write-Warning "Failed to delete $PackageName $version" + } +} + +Write-Host "Completed deleting all versions of $PackageName." -ForegroundColor Cyan \ No newline at end of file diff --git a/.github/scripts/ci/Get-Version.ps1 b/.github/scripts/ci/Get-Version.ps1 new file mode 100644 index 0000000..bc17636 --- /dev/null +++ b/.github/scripts/ci/Get-Version.ps1 @@ -0,0 +1,94 @@ +#----------------------------------------------------------------------- +# Get-Version [-VersionToReplace ] [-Major ] [-Minor ] [-Revision ] [-Build ] [-Patch ] [-PreRelease ] [-CommitHash ] +# +# 1. Default (auto date/time for revision/build/patch): +# .\Get-Version.ps1 +# # Defaults: Major=1, Minor=0, Revision=(day of year), Build=(hour+minute), Patch=(month) +# +# 2. Set explicit major/minor (auto rest): +# .\Get-Version.ps1 -Major 2 -Minor 5 +# # Defaults: Revision=(day of year), Build=(hour+minute), Patch=(month) +# +# 3. Full explicit version (no auto): +# .\Get-Version.ps1 -Major 1 -Minor 2 -Revision 3 -Build 4 -Patch 5 +# # No defaults: all values are explicit +# +# 4. Pre-release and commit hash: +# .\Get-Version.ps1 -Major 1 -Minor 0 -Patch 1 -PreRelease -beta -CommitHash +abc123 +# # Defaults: Revision=(day of year), Build=(hour+minute) +#----------------------------------------------------------------------- + +# *** +# *** Parameters +# *** +param( + [string] $VersionToReplace = '1.0.0', + [string] $Major = '-1', + [string] $Minor = '-1', + [string] $Revision = '-1', + [string] $Build = '-1', + [string] $Patch = '-1', + [string] $PreRelease = '-1', + [string] $CommitHash = '-1' +) + +# *** +# *** Initialize +# *** + + +# *** +# *** Locals +# *** + +# *** +# *** Execute +# *** + +# Calculate version parts with defaults and protect against blank/null +function Use-ValueOrDefault { + param($Value, $Default) + if ([string]::IsNullOrWhiteSpace($Value) -or $Value -eq '-1') { return $Default } + return $Value +} + +$Major = Use-ValueOrDefault $Major (($VersionToReplace -split '\.')[0]) +if ([string]::IsNullOrWhiteSpace($Major)) { $Major = '0' } + +$Minor = Use-ValueOrDefault $Minor (($VersionToReplace -split '\.')[1]) +if ([string]::IsNullOrWhiteSpace($Minor)) { $Minor = '0' } + +$Revision = Use-ValueOrDefault $Revision ((Get-Date -UFormat '%j').ToString()) +if ([string]::IsNullOrWhiteSpace($Revision)) { $Revision = '0' } + +$Build = Use-ValueOrDefault $Build ((Get-Date -UFormat '%H%M').ToString()) +if ([string]::IsNullOrWhiteSpace($Build)) { $Build = '0' } + +$Patch = Use-ValueOrDefault $Patch ((Get-Date -UFormat '%m').ToString()) +if ([string]::IsNullOrWhiteSpace($Patch)) { $Patch = '0' } + +$PreRelease = Use-ValueOrDefault $PreRelease '' +$CommitHash = Use-ValueOrDefault $CommitHash '' + + +# Remove leading zeros for all numeric identifiers +$vMajor = [int]$Major +$vMinor = [int]$Minor +$vRevision = [int]$Revision +$vBuild = [int]$Build +$vPatch = [int]$Patch + +# Version Formats +$FileVersion = "$vMajor.$vMinor.$vRevision.$vBuild" # e.g. 1.0.0.0 +$AssemblyVersion = "$vMajor.$vMinor.0.0" +$InformationalVersion = "$vMajor.$vMinor.$vRevision$PreRelease$CommitHash" +$SemanticVersion = "$vMajor.$vMinor.$vPatch$PreRelease" + +$result = [PSCustomObject]@{ + FileVersion = $FileVersion + AssemblyVersion = $AssemblyVersion + InformationalVersion = $InformationalVersion + SemanticVersion = $SemanticVersion +} + +$result | ConvertTo-Json -Compress diff --git a/.github/scripts/ci/Set-Version.ps1 b/.github/scripts/ci/Set-Version.ps1 new file mode 100644 index 0000000..482f15c --- /dev/null +++ b/.github/scripts/ci/Set-Version.ps1 @@ -0,0 +1,101 @@ +#----------------------------------------------------------------------- +# Set-Version [-Path []] [-VersionToReplace []] [-Type []] +# +# Example: .\Set-Version -Path \\source\path -Major 1 +#----------------------------------------------------------------------- + +# *** +# *** Parameters +# *** +param +( + [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] + [string] $Path=$(throw '-Path is a required parameter. i.e. $(Build.SourcesDirectory)'), + [string] $VersionToReplace='1.0.0', + [string] $Major='-1', + [string] $Minor='-1', + [string] $Revision='-1', + [string] $Build='-1', + [string] $Patch='-1', + [string] $PreRelease='-1', + [string] $CommitHash='-1' +) + +# *** +# *** Initialize +# *** +if ($IsWindows) { Set-ExecutionPolicy Unrestricted -Scope Process -Force } +$VerbosePreference = 'SilentlyContinue' #'Continue' +if ($MyInvocation.MyCommand -and $MyInvocation.MyCommand.Path) { + [String]$ThisScript = $MyInvocation.MyCommand.Path + [String]$ThisDir = Split-Path $ThisScript + [DateTime]$Now = Get-Date + Write-Debug "*****************************" + Write-Debug "*** Starting: $ThisScript on $Now" + Write-Debug "*****************************" + # Imports + Import-Module "$ThisDir/../System.psm1" +} else { + Write-Verbose "No script file context detected. Skipping module import." +} + +# *** +# *** Validate and cleanse +# *** +If($IsWindows){ + $Path = Set-Unc -Path $Path +} + +# *** +# *** Locals +# *** + +# *** +# *** Execute +# *** + + +# Calculate versions using Get-Version.ps1 (pass-through all arguments) +$ThisDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$getVersionScript = Join-Path $ThisDir 'Get-Version.ps1' +$getVersionArgs = @{ + Major = $Major + Minor = $Minor + Revision = $Revision + Build = $Build + Patch = $Patch + PreRelease = $PreRelease + CommitHash = $CommitHash + VersionToReplace = $VersionToReplace +} +$versionJson = & $getVersionScript @getVersionArgs +$versionObj = $versionJson | ConvertFrom-Json +Write-Debug "Get-Version: $($versionJson | ConvertTo-Json -Depth 10)" + +$FileVersion = $versionObj.FileVersion +$AssemblyVersion = $versionObj.AssemblyVersion +$InformationalVersion = $versionObj.InformationalVersion +$SemanticVersion = $versionObj.SemanticVersion +Write-Debug "FileVersion: $FileVersion SemanticVersion: $SemanticVersion AssemblyVersion: $AssemblyVersion InformationalVersion: $InformationalVersion" + +# *.csproj C# Project files +Update-ContentsByTag -Path $Path -Value $SemanticVersion -Open '' -Close '' -Include *.csproj +Update-ContentsByTag -Path $Path -Value $FileVersion -Open '' -Close '' -Include *.csproj +Update-ContentsByTag -Path $Path -Value $AssemblyVersion -Open '' -Close '' -Include *.csproj +Update-ContentsByTag -Path $Path -Value $InformationalVersion -Open '' -Close '' -Include *.csproj +# *.props/.targets/Directory.Build.props/targets (common for shared versioning) +Update-ContentsByTag -Path $Path -Value $SemanticVersion -Open '' -Close '' -Include *.props,*.targets,Directory.Build.props,Directory.Build.targets +Update-ContentsByTag -Path $Path -Value $SemanticVersion -Open '' -Close '' -Include *.props,*.targets,Directory.Build.props,Directory.Build.targets +# Package.json version +Update-LineByContains -Path $Path -Contains 'version' -Line """version"": ""$FileVersion""," -Include package.json +# OpenApiConfigurationOptions.cs version +Update-LineByContains -Path $Path -Contains 'Version' -Line "Version = ""$AssemblyVersion""," -Include OpenApiConfigurationOptions.cs +# *.nuspec NuGet packages +Update-ContentsByTag -Path $Path -Value $SemanticVersion -Open '' -Close '' -Include *.nuspec +# Assembly.cs C# assembly manifest +Update-LineByContains -Path $Path -Contains "FileVersion(" -Line "[assembly: FileVersion(""$FileVersion"")]" -Include AssemblyInfo.cs +Update-LineByContains -Path $Path -Contains "AssemblyVersion(" -Line "[assembly: AssemblyVersion(""$AssemblyVersion"")]" -Include AssemblyInfo.cs +# *.vsixmanifest VSIX Visual Studio Templates +Update-TextByContains -Path $Path -Contains "$null +if (-not $ghAuth) { + Write-Host "GitHub CLI not authenticated. Please login." -ForegroundColor Red + gh auth login +} + +Write-Host "Checking if repo exists..." +$repoExists = gh repo view "$Owner/$Repo" 2>$null +if (-not $repoExists) { + $createArgs = @( + 'repo', 'create', "$Owner/$Repo", + "--$vis", + '--add-readme', + '--gitignore', 'VisualStudio' + ) + if ($license) { $createArgs += @('--license', $license) } + Write-Host "DEBUG: gh $($createArgs -join ' ')" + gh @createArgs | Out-Null + Write-Host "Created repo $Owner/$Repo" + # Re-check repo existence after creation + $repoExists = gh repo view "$Owner/$Repo" 2>$null +} +else { + Write-Host "Repo $Owner/$Repo already exists. Skipping creation." +} + +# ---- 1) Allow auto-merge (repo-level toggle) +# Enables future workflows to set --auto on PRs +if ($repoExists) { + $autoMergeStatus = gh api "repos/$Owner/$Repo" | ConvertFrom-Json | Select-Object -ExpandProperty allow_auto_merge + if (-not $autoMergeStatus) { + gh api -X PATCH "repos/$Owner/$Repo" -f allow_auto_merge=true | Out-Null + Write-Host "Enabled auto-merge." + } + else { + Write-Host "Auto-merge already enabled." + } +} + +# ---- 2) Enable security & analysis: Secret Scanning + Push Protection (fixed payload) +if ($repoExists) { + $repoJson = gh api "repos/$Owner/$Repo" | ConvertFrom-Json + if ($repoJson.PSObject.Properties.Name -contains 'security_and_analysis') { + $secStatus = $repoJson.security_and_analysis + if ($secStatus.secret_scanning.status -ne "enabled" -or $secStatus.secret_scanning_push_protection.status -ne "enabled") { + $ghArgs = @( + 'api', + '-X', 'PATCH', + "repos/$Owner/$Repo", + '-f', 'secret_scanning.status=enabled', + '-f', 'secret_scanning_push_protection.status=enabled' + ) + $response = gh @ghArgs + Write-Host "Enabled secret scanning and push protection." + } + else { + Write-Host "Secret scanning and push protection already enabled." + } + } else { + Write-Host "Warning: 'security_and_analysis' property not found in repo API response. Skipping secret scanning and push protection setup." + } +} + +# ---- 3) Enable Dependabot alerts & security updates (and add version updates file) +# Alerts / Security updates are repository settings endpoints. +# (If your org enforces these by default, you can skip.) +# List/enable endpoints are under Repositories API group. +# Add dependabot.yml (version updates) if you want scheduled updates: +$dependabotYml = @" +version: 2 +updates: + - package-ecosystem: "nuget" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" +"@ + +$tmp = New-TemporaryFile +$dependabotYml | Set-Content -NoNewline -Path $tmp +if ($repoExists) { + $fileExists = gh api "/repos/$Owner/$Repo/contents/.github/dependabot.yml" 2>$null + if (-not $fileExists) { + gh api --method PUT "/repos/$Owner/$Repo/contents/.github/dependabot.yml" ` + -f message="chore: add dependabot version updates" ` + -f content="$( [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes((Get-Content $tmp -Raw))) )" ` + -f branch="main" | Out-Null + Write-Host "Added dependabot.yml." + } + else { + Write-Host "dependabot.yml already exists. Skipping." + } +} +Remove-Item $tmp -Force + +# ---- 4) (Option A) Add Advanced CodeQL workflow file for full automation +# Or Advanced setup is workflow-based & fully automatable. [8](https://graphite.com/guides/github-merge-queue) +$codeqlYml = @" +name: CodeQL +on: + push: + branches: [ main, dev ] + pull_request: + branches: [ main, dev ] + schedule: + - cron: '0 6 * * 1' +permissions: + contents: read + security-events: write +jobs: + analyze: + runs-on: ubuntu-latest + strategy: + matrix: + language: [ 'csharp' ] + steps: + - uses: actions/checkout@v4 + - uses: github/codeql-action/init@v4 + with: + languages: '`${{ matrix.language }}' + - uses: github/codeql-action/autobuild@v3 + - uses: github/codeql-action/analyze@v3 +"@ + +$tmp = New-TemporaryFile +$codeqlYml | Set-Content -NoNewline -Path $tmp +if ($repoExists) { + $fileExists = gh api "/repos/$Owner/$Repo/contents/.github/workflows/codeql-analysis.yml" 2>$null + if (-not $fileExists) { + gh api --method PUT "/repos/$Owner/$Repo/contents/.github/workflows/codeql-analysis.yml" ` + -f message="ci: add CodeQL advanced workflow" ` + -f content="$( [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes((Get-Content $tmp -Raw))) )" ` + -f branch="main" | Out-Null + Write-Host "Added CodeQL workflow." + } + else { + Write-Host "CodeQL workflow already exists. Skipping." + } +} +Remove-Item $tmp -Force + +# ---- 5) Create a new ruleset called 'main-ruleset' (modern GitHub Ruleset API) +# This is the new recommended way to enforce branch policies. +if ($repoExists) { + Write-Host "Creating 'main-ruleset' for branch 'main'..." + $rulesetBodyObj = @{ + name = "main-ruleset" + target = "branch" + enforcement = "active" + conditions = @{ + ref_name = @{ + include = @("refs/heads/main") + exclude = @() + } + } + rules = @( + # Require PR before merging + @{ type = "pull_request" }, + # Require linear history + @{ type = "required_linear_history" } + ) + # Note: To allow force-push for emergencies, add a bypass_actors array with your user/team and bypass_mode="always". + # Example: bypass_actors = @(@{ actor_id = 123456; actor_type = "User"; bypass_mode = "always" }) + } + $rulesetBody = $rulesetBodyObj | ConvertTo-Json -Compress -Depth 5 + + $existingRulesets = gh api "/repos/$Owner/$Repo/rulesets" | ConvertFrom-Json + $mainRuleset = $existingRulesets | Where-Object { $_.name -eq "main-ruleset" } + if (-not $mainRuleset) { + $response = $rulesetBody | gh api -X POST "/repos/$Owner/$Repo/rulesets" --input - -H "Accept: application/vnd.github+json" + Write-Host "'main-ruleset' created." + } + else { + Write-Host "'main-ruleset' already exists. Skipping creation." + } +} +# Ref: https://docs.github.com/en/rest/branches/rulesets?apiVersion=2022-11-28 + +# ---- 6) Create Environments: development & production +# (You can set branch policy + required reviewers) +# Create/Update Environment (PUT) + optional deployment branch policy & protection rules +# Note: required_reviewers require usernames or team slugs (max 6). [7](https://docs.github.com/en/rest/deployments/environments)[12](https://docs.github.com/en/actions/reference/workflows-and-actions/deployments-and-environments) + +# development +if ($repoExists) { + Write-Host "Checking if development environment exists..." + $devEnvResponse = gh api "repos/$Owner/$Repo/environments/development" 2>&1 + if ($devEnvResponse -match '"Not Found"' -or $devEnvResponse -match '404') { + Write-Host "Development environment does not exist. Creating..." + gh api -X PUT "repos/$Owner/$Repo/environments/development" ` + -H "Accept: application/vnd.github+json" | Out-Null + Write-Host "Development environment created." + } + else { + Write-Host "Development environment already exists. Skipping." + } +} +# Optionally add a custom branch policy pattern for dev (requires extra POST endpoint under env policies). +# See community examples for adding custom branch policies after creation. [13](https://stackoverflow.com/questions/70943164/create-environment-for-repository-using-gh) + +# NOTE: Replace reviewers with concrete users/teams via separate calls if needed. +if ($repoExists) { + Write-Host "Checking if production environment exists..." + $prodEnvResponse = gh api "repos/$Owner/$Repo/environments/production" 2>&1 + if ($prodEnvResponse -match '"Not Found"' -or $prodEnvResponse -match '404') { + Write-Host "Production environment does not exist. Creating..." + gh api -X PUT "repos/$Owner/$Repo/environments/production" ` + -H "Accept: application/vnd.github+json" | Out-Null + Write-Host "Production environment created." + } + else { + Write-Host "Production environment already exists. Skipping." + } +} +# Ref: Environments API supports branch policy and protection rules. [7](https://docs.github.com/en/rest/deployments/environments) + +Write-Host "✅ Bootstrap completed for $Owner/$Repo" diff --git a/.github/scripts/repo/New-GithubSecret.ps1 b/.github/scripts/repo/New-GithubSecret.ps1 new file mode 100644 index 0000000..fad416a --- /dev/null +++ b/.github/scripts/repo/New-GithubSecret.ps1 @@ -0,0 +1,69 @@ +# ================================ +# GitHub repo secrets (PowerShell) +# Creates secrets +# Requires: GitHub CLI (gh) + authenticated session +# ================================ +# +# Pre-requisites (auto-executed): +# - Installs GitHub CLI if not present +# - Prompts for GitHub authentication if not already authenticated +# +# Example usage (copy/paste): +# +# .\New-GithubSecret.ps1 -Owner goodtocode -Repo my-repo -Environment development -SecretName MY_SECRET -SecretValue "secret-value" +# +param( + [Parameter(Mandatory=$true)][string]$Owner, + [Parameter(Mandatory=$true)][string]$Repo, + [Parameter(Mandatory=$true)][string]$Environment, + [Parameter(Mandatory=$true)][string]$SecretName, + [Parameter(Mandatory=$true)][string]$SecretValue +) + +if (-not (Get-Command gh -ErrorAction SilentlyContinue)) { + Write-Host "GitHub CLI not found. Installing via winget..." -ForegroundColor Red + winget install --id GitHub.cli -e --silent + Write-Host "GitHub CLI installed. Please restart your terminal or PowerShell session, then re-run this script." -ForegroundColor Red + exit +} + +# Check authentication +$ghAuth = gh auth status 2>$null +if (-not $ghAuth) { + Write-Host "GitHub CLI not authenticated. Please login." -ForegroundColor Red + gh auth login +} + +gh @createArgs | Out-Null + +Write-Host "Checking if repo exists..." +$repoExists = gh repo view "$Owner/$Repo" 2>$null +if (-not $repoExists) { + Write-Host "Repo $Owner/$Repo does not exist. Cannot create secrets for a non-existent repository." + exit +} else { + Write-Host "Repo $Owner/$Repo already exists. Skipping creation." +} + +# Check Environment + if ($repoExists) { + Write-Host "Checking if $Environment environment exists..." + $envExist = gh api "repos/$Owner/$Repo/environments/$Environment" 2>$null + if (-not $envExist) { + Write-Host "$Environment environment does not exist. Cannot create secrets for a non-existent environment." + } +} + +# Add environment secrets (examples) +if ($repoExists -and $envExist) { + Write-Host "Checking if $SecretName secret exists in $Environment..." + $devSecretList = gh secret list --repo "$Owner/$Repo" --env "$Environment" 2>&1 + if ($devSecretList -match '"Not Found"' -or $devSecretList -match '404' -or -not ($devSecretList | Select-String "$SecretName")) { + Write-Host "$SecretName secret does not exist in $Environment. Creating..." + gh secret set "$SecretName" --body "$SecretValue" --repo "$Owner/$Repo" --env "$Environment" + Write-Host "$SecretName secret added to $Environment." + } else { + Write-Host "$SecretName already exists in $Environment. Skipping." + } +} +Write-Host "✅ Secrets completed for $Owner/$Repo in $Environment" diff --git a/.github/scripts/az-create-for-rbac.cmd b/.github/scripts/repo/az-create-for-rbac.cmd similarity index 100% rename from .github/scripts/az-create-for-rbac.cmd rename to .github/scripts/repo/az-create-for-rbac.cmd diff --git a/.github/workflows/gtc-platform-hub-iac.yml b/.github/workflows/gtc-platform-hub-iac.yml new file mode 100644 index 0000000..161115a --- /dev/null +++ b/.github/workflows/gtc-platform-hub-iac.yml @@ -0,0 +1,127 @@ +name: 'Platform Hub IaC' + +on: + push: + branches-ignore: + - main + paths: + - .github/workflows/gtc-platform-hub-iac.yml + - .azure/**/*.bicep + - .azure/**/*.bicepparams + pull_request: + types: [closed] + branches: + - main + paths: + - .github/workflows/gtc-platform-hub-iac.yml + - .azure/**/*.bicep + - .azure/**/*.bicepparams + workflow_dispatch: + inputs: + environment: + description: "Environment to run" + required: true + default: "development" + runcd: + description: "Run CD pipeline" + required: true + default: "false" + +env: + DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: "true" + HUB_LOCATION: "westus2" + HUB_NETWORK_RG_NAME: "gtc-hubnetwork-plat-wus2-001-rg" + HUB_NETWORK_BICEP_TEMPLATE: ".azure/templates/platform-hub-network-publicroute.bicep" + HUB_NETWORK_BICEP_PARAMETERS: ".azure/variables/platform-hub-network-publicroute-plat.bicepparam" + HUB_MGMT_RG_NAME: "gtc-hubmgmt-plat-wus2-001-rg" + HUB_MGMT_BICEP_TEMPLATE: ".azure/templates/platform-hub-mgmt.bicep" + HUB_MGMT_BICEP_PARAMETERS: ".azure/variables/platform-hub-mgmt-plat.bicepparam" + +permissions: + id-token: write + contents: read + security-events: write + +jobs: + ci: + name: "Validate hub IaC" + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' || github.event_name == 'push' + environment: development + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: "Az CLI login" + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.PLATFORM_SUBSCRIPTION_ID }} + + - name: Create ${{ env.HUB_NETWORK_RG_NAME }} + uses: Azure/cli@v2.1.0 + with: + inlineScript: | + az group create -n ${{ env.HUB_NETWORK_RG_NAME }} -l ${{ env.HUB_LOCATION }} + + - name: Validate ${{ env.HUB_NETWORK_RG_NAME }} + uses: Azure/cli@v2.1.0 + with: + inlineScript: | + az deployment group what-if --resource-group ${{ env.HUB_NETWORK_RG_NAME }} --template-file ${{ env.HUB_NETWORK_BICEP_TEMPLATE }} --parameters ${{ env.HUB_NETWORK_BICEP_PARAMETERS }} + + - name: Create ${{ env.HUB_MGMT_RG_NAME }} + uses: Azure/cli@v2.1.0 + with: + inlineScript: | + az group create -n ${{ env.HUB_MGMT_RG_NAME }} -l ${{ env.HUB_LOCATION }} + + - name: Validate ${{ env.HUB_MGMT_RG_NAME }} + uses: Azure/cli@v2.1.0 + with: + inlineScript: | + az deployment group what-if --resource-group ${{ env.HUB_MGMT_RG_NAME }} --template-file ${{ env.HUB_MGMT_BICEP_TEMPLATE }} --parameters ${{ env.HUB_MGMT_BICEP_PARAMETERS }} + + cd: + name: "Deploy landing zone IaC" + runs-on: ubuntu-latest + needs: ci + if: | + (github.event_name == 'push' && github.ref == 'refs/heads/main') || + (github.event_name == 'workflow_dispatch' && (github.event.inputs.runcd == 'true' || github.event.inputs.runcd == true)) + environment: development + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: "Az CLI login" + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.PLATFORM_SUBSCRIPTION_ID }} + + - name: Create ${{ env.HUB_NETWORK_RG_NAME }} + uses: Azure/cli@v2.1.0 + with: + inlineScript: | + az group create -n ${{ env.HUB_NETWORK_RG_NAME }} -l ${{ env.HUB_LOCATION }} + + - name: Deploy ${{ env.HUB_NETWORK_RG_NAME }} + uses: Azure/cli@v2.1.0 + with: + inlineScript: | + az deployment group create --resource-group ${{ env.HUB_NETWORK_RG_NAME }} --template-file ${{ env.HUB_NETWORK_BICEP_TEMPLATE }} --parameters ${{ env.HUB_NETWORK_BICEP_PARAMETERS }} + + - name: Create ${{ env.HUB_MGMT_RG_NAME }} + uses: Azure/cli@v2.1.0 + with: + inlineScript: | + az group create -n ${{ env.HUB_MGMT_RG_NAME }} -l ${{ env.HUB_LOCATION }} + + - name: Deploy ${{ env.HUB_MGMT_RG_NAME }} + uses: Azure/cli@v2.1.0 + with: + inlineScript: | + az deployment group create --resource-group ${{ env.HUB_MGMT_RG_NAME }} --template-file ${{ env.HUB_MGMT_BICEP_TEMPLATE }} --parameters ${{ env.HUB_MGMT_BICEP_PARAMETERS }} diff --git a/.github/workflows/gtc-platform-spoke-iac.yml b/.github/workflows/gtc-platform-spoke-iac.yml new file mode 100644 index 0000000..b0a178e --- /dev/null +++ b/.github/workflows/gtc-platform-spoke-iac.yml @@ -0,0 +1,128 @@ +name: 'Platform Spoke IaC' + +on: + push: + branches-ignore: + - main + paths: + - .github/workflows/gtc-platform-spoke-iac.yml + - .azure/**/*.bicep + - .azure/**/*.bicepparams + pull_request: + types: [closed] + branches: + - main + paths: + - .github/workflows/gtc-platform-spoke-iac.yml + - .azure/**/*.bicep + - .azure/**/*.bicepparams + workflow_dispatch: + inputs: + environment: + description: "Environment to run" + required: true + default: "development" + runcd: + description: "Run CD pipeline" + required: true + default: "false" + +env: + DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: "true" + HUB_MGMT_RG_NAME: "gtc-hubmgmt-plat-wus2-001-rg" + SPOKE_LOCATION: "westus2" + SPOKE_NETWORK_RG_NAME: "gtc-spokenetwork-dev-wus2-001-rg" + SPOKE_NETWORK_BICEP_TEMPLATE: ".azure/templates/platform-spoke-network-publicroute.bicep" + SPOKE_NETWORK_BICEP_PARAMETERS: ".azure/variables/platform-spoke-network-publicroute-dev.bicepparam" + SPOKE_MGMT_RG_NAME: "gtc-spokemgmt-dev-wus2-001-rg" + SPOKE_MGMT_BICEP_TEMPLATE: ".azure/templates/platform-spoke-mgmt.bicep" + SPOKE_MGMT_BICEP_PARAMETERS: ".azure/variables/platform-spoke-mgmt-dev.bicepparam" + +permissions: + id-token: write + contents: read + security-events: write + +jobs: + ci: + name: "Validate spoke IaC" + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' || github.event_name == 'push' + environment: development + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: "Az CLI login" + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Create ${{ env.SPOKE_NETWORK_RG_NAME }} + uses: Azure/cli@v2.1.0 + with: + inlineScript: | + az group create -n ${{ env.SPOKE_NETWORK_RG_NAME }} -l ${{ env.SPOKE_LOCATION }} + + - name: Validate ${{ env.SPOKE_NETWORK_RG_NAME }} + uses: Azure/cli@v2.1.0 + with: + inlineScript: | + az deployment group what-if --resource-group ${{ env.SPOKE_NETWORK_RG_NAME }} --template-file ${{ env.SPOKE_NETWORK_BICEP_TEMPLATE }} --parameters ${{ env.SPOKE_NETWORK_BICEP_PARAMETERS }} + + - name: Create ${{ env.SPOKE_MGMT_RG_NAME }} + uses: Azure/cli@v2.1.0 + with: + inlineScript: | + az group create -n ${{ env.SPOKE_MGMT_RG_NAME }} -l ${{ env.SPOKE_LOCATION }} + + - name: Validate ${{ env.SPOKE_MGMT_RG_NAME }} + uses: Azure/cli@v2.1.0 + with: + inlineScript: | + az deployment group what-if --resource-group ${{ env.SPOKE_MGMT_RG_NAME }} --template-file ${{ env.SPOKE_MGMT_BICEP_TEMPLATE }} --parameters ${{ env.SPOKE_MGMT_BICEP_PARAMETERS }} --parameters hubMgmtSubscriptionId=${{ secrets.PLATFORM_SUBSCRIPTION_ID }} hubMgmtResourceGroupName=${{ env.HUB_MGMT_RG_NAME }} + + cd: + name: "Deploy landing zone IaC" + runs-on: ubuntu-latest + needs: ci + if: | + (github.event_name == 'push' && github.ref == 'refs/heads/main') || + (github.event_name == 'workflow_dispatch' && (github.event.inputs.runcd == 'true' || github.event.inputs.runcd == true)) + environment: development + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: "Az CLI login" + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Create ${{ env.SPOKE_NETWORK_RG_NAME }} + uses: Azure/cli@v2.1.0 + with: + inlineScript: | + az group create -n ${{ env.SPOKE_NETWORK_RG_NAME }} -l ${{ env.SPOKE_LOCATION }} + + - name: Deploy ${{ env.SPOKE_NETWORK_RG_NAME }} + uses: Azure/cli@v2.1.0 + with: + inlineScript: | + az deployment group create --resource-group ${{ env.SPOKE_NETWORK_RG_NAME }} --template-file ${{ env.SPOKE_NETWORK_BICEP_TEMPLATE }} --parameters ${{ env.SPOKE_NETWORK_BICEP_PARAMETERS }} + + - name: Create ${{ env.SPOKE_MGMT_RG_NAME }} + uses: Azure/cli@v2.1.0 + with: + inlineScript: | + az group create -n ${{ env.SPOKE_MGMT_RG_NAME }} -l ${{ env.SPOKE_LOCATION }} + + - name: Deploy ${{ env.SPOKE_MGMT_RG_NAME }} + uses: Azure/cli@v2.1.0 + with: + inlineScript: | + az deployment group create --resource-group ${{ env.SPOKE_MGMT_RG_NAME }} --template-file ${{ env.SPOKE_MGMT_BICEP_TEMPLATE }} --parameters ${{ env.SPOKE_MGMT_BICEP_PARAMETERS }} --parameters hubMgmtSubscriptionId=${{ secrets.PLATFORM_SUBSCRIPTION_ID }} hubMgmtResourceGroupName=${{ env.HUB_MGMT_RG_NAME }} \ No newline at end of file diff --git a/.github/workflows/gtc-rg-semkernel-api-ci-cd.yml b/.github/workflows/gtc-rg-semkernel-api-ci-cd.yml deleted file mode 100644 index 707131d..0000000 --- a/.github/workflows/gtc-rg-semkernel-api-ci-cd.yml +++ /dev/null @@ -1,318 +0,0 @@ -name: CI/CD Build, Test and Deploy - -on: - pull_request: - branches: - - main - paths: - - .github/workflows/gtc-rg-semkernel-api-ci-cd.yml - - src/** - - .github/workflows/gtc-rg-semkernel-iac.yml - - .azure/**/*.bicep - - .azure/**/*.bicepparams - push: - branches: - - main - paths: - - .github/workflows/gtc-rg-semkernel-api-ci-cd.yml - - src/** - - .github/workflows/gtc-rg-semkernel-iac.yml - - .azure/**/*.bicep - - .azure/**/*.bicepparams - workflow_dispatch: - inputs: - environment: - description: 'Environment to run' - required: true - default: 'development' - mode: - description: 'Running mode' - -permissions: - actions: read - id-token: write - contents: read - security-events: write - -jobs: - ci: - name: 'CI Build, Test, Code QL, Publish' - runs-on: ubuntu-latest - if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' - environment: development - strategy: - matrix: - DOTNET_VERSION: ['9.x'] - - env: - AZURE_WEBAPP_NAME: api-semkernel-dev-001 - AZURE_WEBAPP_PACKAGE_PATH: '.' - AZURE_RG_NAME: 'gtc-rg-devtest-semkernel-dev-001' - RUNTIME_ENV: 'Development' - SRC_PATH: './src' - SRC_SLN: 'SemanticKernelMicroservice.sln' - API_PATH: 'Presentation.WebApi' - API_PROJECT: 'Presentation.WebApi.csproj' - TEST_PATH: 'Tests.Specs.Integration' - TEST_PROJECT: 'Tests.Specs.Integration.csproj' - SCRIPTS_PATH: './.github/scripts' - - steps: - - name: checkout - uses: actions/checkout@v3 - - - name: dotnet version ${{ matrix.DOTNET_VERSION }} - uses: actions/setup-dotnet@v4 - with: - dotnet-version: ${{ matrix.DOTNET_VERSION }} - - - name: Set-Version.ps1 - run: | - $version = ${{ env.SCRIPTS_PATH }}/Set-Version.ps1 -Path ${{ env.SRC_PATH }} -VersionToReplace 1.0.0 - echo $version - echo "VERSION=$version" >> $GITHUB_ENV - shell: pwsh - - - name: pipeline configuration secrets - run: | - echo "ASPNETCORE_ENVIRONMENT=${{ env.RUNTIME_ENV }}" >> $GITHUB_ENV - echo "AZURE_FUNCTIONS_ENVIRONMENT=${{ env.RUNTIME_ENV }}" >> $GITHUB_ENV - echo "OpenAI:ApiKey=${{ secrets.OPENAI_APIKEY }}" >> $GITHUB_ENV - shell: pwsh - - - name: App Settings Variable Substitution - uses: microsoft/variable-substitution@v1 - with: - files: '${{ env.SRC_PATH }}/${{ env.API_PATH }}/appsettings.json, ${{ env.SRC_PATH }}/${{ env.API_PATH }}/appsettings.${{ env.RUNTIME_ENV }}.json, ${{ env.SRC_PATH }}/${{ env.TEST_PATH }}/appsettings.test.json' - env: - OpenAI.ApiKey: ${{ secrets.OPENAI_APIKEY }} - - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: csharp - - - name: Build - run: | - dotnet build ${{ env.SRC_PATH }}/${{ env.SRC_SLN }} --configuration Release - shell: pwsh - - - name: Test - run: | - mkdir -p TestResults-${{ matrix.DOTNET_VERSION }} - dotnet test ${{ env.SRC_PATH }}/${{ env.TEST_PATH }}/${{ env.TEST_PROJECT }} --logger "trx;LogFileName=test_results.trx" --results-directory TestResults-${{ matrix.DOTNET_VERSION }} --verbosity normal - shell: pwsh - - - name: Upload test results - uses: actions/upload-artifact@v4 - with: - name: test-results-dotnet-${{ matrix.DOTNET_VERSION }} - path: TestResults-${{ matrix.DOTNET_VERSION }} - if: ${{ always() }} - - - name: Run Coverage Script - run: | - pwsh ${{ env.SRC_PATH }}/Get-CodeCoverage.ps1 ` - -TestProjectFilter '${{ env.TEST_PROJECT }}' ` - -ProdPackagesOnly ` - -ProductionAssemblies Cannery.Insights.Core.Application Cannery.Insights.Presentation.WebApi Cannery.Insights.Presentation.Blazor - shell: pwsh - - - name: Upload Coverage Report - uses: actions/upload-artifact@v4 - with: - name: coverage-report-${{ matrix.DOTNET_VERSION }} - path: ${{ env.SRC_PATH }}/TestResults/Reports/**/index.html - if: ${{ always() }} - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - - cd: - name: 'CD Deploy .NET' - runs-on: ubuntu-latest - needs: ci - if: | - github.event_name == 'push' && - contains(github.ref, 'refs/tags/deploy') - environment: development - strategy: - matrix: - DOTNET_VERSION: ['9.x'] - - env: - AZURE_WEBAPP_NAME: api-semkernel-dev-001 - AZURE_WEBAPP_PACKAGE_PATH: '.' - AZURE_RG_NAME: 'gtc-rg-devtest-semkernel-dev-001' - RUNTIME_ENV: 'Development' - SRC_PATH: './src' - SRC_SLN: 'SemanticKernelMicroservice.sln' - API_PATH: 'Presentation.WebApi' - API_PROJECT: 'Presentation.WebApi.csproj' - APPI_NAME: 'appi-semkernel-dev-001' - INFRA_PATH: 'Infrastructure.SqlServer' - INFRA_PROJECT: 'Infrastructure.SqlServer.csproj' - INFRA_DBCONTEXT: 'SemanticKernelContext' - TEST_PATH: 'Tests.Specs.Integration' - TEST_PROJECT: 'Tests.Specs.Integration.csproj' - SCRIPTS_PATH: './.github/scripts' - SQL_NAME: 'sql-semkernel-dev-001' - SQLDB_NAME: 'sqldb-semkernel-dev-001' - AZURE_CRED: '{"clientId":"${{ secrets.AZURE_CLIENT_ID }}","subscriptionId":"${{ secrets.SUBSCRIPTION_ID }}","tenantId":"${{ secrets.AZURE_TENANT_ID }}"}' - - steps: - - name: checkout - uses: actions/checkout@v3 - - - name: dotnet version ${{ matrix.DOTNET_VERSION }} - uses: actions/setup-dotnet@v4 - with: - dotnet-version: ${{ matrix.DOTNET_VERSION }} - - - name: Set-Version.ps1 - run: | - $version = ${{ env.SCRIPTS_PATH }}/Set-Version.ps1 -Path ${{ env.SRC_PATH }} -VersionToReplace 1.0.0 - echo $version - echo "VERSION=$version" >> $GITHUB_ENV - shell: pwsh - - - name: pipeline configuration secrets - run: | - echo "ASPNETCORE_ENVIRONMENT=${{ env.RUNTIME_ENV }}" >> $GITHUB_ENV - echo "AZURE_FUNCTIONS_ENVIRONMENT=${{ env.RUNTIME_ENV }}" >> $GITHUB_ENV - echo "OpenAI:ApiKey=${{ secrets.OPENAI_APIKEY }}" >> $GITHUB_ENV - shell: pwsh - - - name: App Settings Variable Substitution - uses: microsoft/variable-substitution@v1 - with: - files: '${{ env.SRC_PATH }}/${{ env.API_PATH }}/appsettings.json, ${{ env.SRC_PATH }}/${{ env.API_PATH }}/appsettings.${{ env.RUNTIME_ENV }}.json, ${{ env.SRC_PATH }}/${{ env.TEST_PATH }}/appsettings.test.json' - env: - OpenAI.ApiKey: ${{ secrets.OPENAI_APIKEY }} - - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: csharp - - - name: Build - run: | - dotnet build ${{ env.SRC_PATH }}/${{ env.SRC_SLN }} --configuration Release - shell: pwsh - - - name: Test - run: | - mkdir -p TestResults-${{ matrix.DOTNET_VERSION }} - dotnet test ${{ env.SRC_PATH }}/${{ env.TEST_PATH }}/${{ env.TEST_PROJECT }} --logger "trx;LogFileName=test_results.trx" --results-directory TestResults-${{ matrix.DOTNET_VERSION }} --verbosity normal - shell: pwsh - - - name: Upload test results - uses: actions/upload-artifact@v4 - with: - name: test-results-dotnet-${{ matrix.DOTNET_VERSION }} - path: TestResults-${{ matrix.DOTNET_VERSION }} - if: ${{ always() }} - - - name: Run Coverage Script - run: | - pwsh ${{ env.SRC_PATH }}/Get-CodeCoverage.ps1 ` - -TestProjectFilter '${{ env.TEST_PROJECT }}' ` - -ProdPackagesOnly ` - -ProductionAssemblies Cannery.Insights.Core.Application Cannery.Insights.Presentation.WebApi Cannery.Insights.Presentation.Blazor - shell: pwsh - - - name: Upload Coverage Report - uses: actions/upload-artifact@v4 - with: - name: coverage-report-${{ matrix.DOTNET_VERSION }} - path: ${{ env.SRC_PATH }}/TestResults/Reports/**/index.html - if: ${{ always() }} - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - - - name: Publish - run: | - dotnet publish ${{ env.SRC_PATH }}/${{ env.API_PATH }}/${{ env.API_PROJECT }} --configuration Release -o ${{ env.AZURE_WEBAPP_NAME }} - shell: pwsh - - - name: az login - uses: azure/login@v1 - with: - client-id: ${{ secrets.AZURE_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - - - name: get publish profile - id: publishprofile - uses: aliencube/publish-profile-actions@v1 - env: - AZURE_CREDENTIALS: ${{ env.AZURE_CRED }} - with: - resourceGroupName: ${{ env.AZURE_RG_NAME }} - appName: ${{ env.AZURE_WEBAPP_NAME }} - - - name: functionapp deploy - uses: Azure/functions-action@v1 - with: - app-name: ${{ env.AZURE_WEBAPP_NAME }} - package: '${{ env.AZURE_WEBAPP_PACKAGE_PATH }}/${{ env.AZURE_WEBAPP_NAME }}' - publish-profile: ${{ steps.publishprofile.outputs.profile }} - if: ${{ false }} - - - name: webapps deploy - uses: azure/webapps-deploy@v2 - with: - app-name: ${{ env.AZURE_WEBAPP_NAME }} - publish-profile: ${{ steps.publishprofile.outputs.profile }} - package: '${{ env.AZURE_WEBAPP_PACKAGE_PATH }}/${{ env.AZURE_WEBAPP_NAME }}' - #slot-name: staging - - - name: Reset publish profile - uses: aliencube/publish-profile-actions@v1 - env: - AZURE_CREDENTIALS: ${{ secrets.AZURE_CRED }} - with: - resourceGroupName: ${{ env.AZURE_RG_NAME }} - appName: ${{ env.AZURE_WEBAPP_NAME }} - reset: true - - # Get instrumentation key: az monitor app-insights component show --app -g - # Get connection: az monitor app-insights component show -g ${{ env.AZURE_RG_NAME }} --app ${{ env.APPI_NAME }} - - name: ${{ env.AZURE_WEBAPP_NAME }} app settings - run: | - az config set extension.use_dynamic_install=yes_without_prompt - $TEMP_JSON = az monitor app-insights component show -g ${{ env.AZURE_RG_NAME }} --app ${{ env.APPI_NAME }} | ConvertFrom-Json - $INSTR_KEY = $TEMP_JSON.instrumentationKey - $CONN_STR = $TEMP_JSON.connectionString - az webapp config appsettings set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_WEBAPP_NAME }} --settings APPINSIGHTS_INSTRUMENTATIONKEY=$INSTR_KEY - az webapp config appsettings set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_WEBAPP_NAME }} --settings APPLICATIONINSIGHTS_CONNECTION_STRING=$CONN_STR - az webapp config appsettings set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_WEBAPP_NAME }} --settings ASPNETCORE_ENVIRONMENT=${{ env.RUNTIME_ENV }} - shell: pwsh - - # Get Azure SQL DB Conenction: az sql db show-connection-string --client ado.net --server ${{ env.SQL_NAME }} --name ${{ env.SQLDB_NAME }} -o tsv - - name: ${{ env.AZURE_WEBAPP_NAME }} connection strings - run: | - $TEMP_STR=az sql db show-connection-string --client ado.net --server ${{ env.SQL_NAME }} --name ${{ env.SQLDB_NAME }} -o tsv - $TEMP_STR=$TEMP_STR.replace("", "${{ secrets.SQL_ADMIN_USER }}") - $TEMP_STR=$TEMP_STR.replace("", "${{ secrets.SQL_ADMIN_PASSWORD }}") - az webapp config connection-string set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_WEBAPP_NAME }} -t SQLServer --settings DefaultConnection=$TEMP_STR - shell: pwsh - - - name: ${{ env.AZURE_WEBAPP_NAME }} dotnet ef migrations - run: | - $TEMP_STR=az sql db show-connection-string --client ado.net --server ${{ env.SQL_NAME }} --name ${{ env.SQLDB_NAME }} -o tsv - $TEMP_STR=$TEMP_STR.replace("", "${{ secrets.SQL_ADMIN_USER }}") - $TEMP_STR=$TEMP_STR.replace("", "${{ secrets.SQL_ADMIN_PASSWORD }}") - dotnet tool install --global dotnet-ef - dotnet tool restore - dotnet ef migrations add v${{ env.VERSION }} --project ${{ env.SRC_PATH }}/${{ env.INFRA_PATH }}/${{ env.INFRA_PROJECT }} --startup-project ${{ env.SRC_PATH }}/${{ env.API_PATH }}/${{ env.API_PROJECT }} --context ${{ env.INFRA_DBCONTEXT }} - dotnet ef migrations script --project ${{ env.SRC_PATH }}/${{ env.INFRA_PATH }}/${{ env.INFRA_PROJECT }} --startup-project ${{ env.SRC_PATH }}/${{ env.API_PATH }}/${{ env.API_PROJECT }} --context ${{ env.INFRA_DBCONTEXT }} --output ${{ github.workspace }}/sql/migrations.sql --idempotent - dotnet ef database update --project ${{ env.SRC_PATH }}/${{ env.INFRA_PATH }}/${{ env.INFRA_PROJECT }} --startup-project ${{ env.SRC_PATH }}/${{ env.API_PATH }}/${{ env.API_PROJECT }} --context ${{ env.INFRA_DBCONTEXT }} --connection $TEMP_STR - shell: pwsh - - - name: Swap to production slot - run: | - az webapp deployment slot swap --resource-group ${{ env.AZURE_RG_NAME }} --name ${{ env.AZURE_WEBAPP_NAME }} --slot staging --target-slot production - echo "Swap finished. App Service Application URL: https://$(az webapp show --resource-group ${{ env.AZURE_RG_NAME }} --name ${{ env.AZURE_WEBAPP_NAME }} --query hostNames[0] -o tsv)" - if: ${{ false }} \ No newline at end of file diff --git a/.github/workflows/gtc-rg-semkernel-iac.yml b/.github/workflows/gtc-semker-iac.yml similarity index 55% rename from .github/workflows/gtc-rg-semkernel-iac.yml rename to .github/workflows/gtc-semker-iac.yml index b31107f..266ae02 100644 --- a/.github/workflows/gtc-rg-semkernel-iac.yml +++ b/.github/workflows/gtc-semker-iac.yml @@ -1,11 +1,11 @@ -name: IaC what-if and Deploy +name: 'Landing Zone IaC' on: push: branches-ignore: - main paths: - - .github/workflows/gtc-rg-semkernel-iac.yml + - .github/workflows/gtc-semker-iac.yml - .azure/**/*.bicep - .azure/**/*.bicepparams pull_request: @@ -13,7 +13,7 @@ on: branches: - main paths: - - .github/workflows/gtc-rg-semkernel-iac.yml + - .github/workflows/gtc-semker-iac.yml - .azure/**/*.bicep - .azure/**/*.bicepparams workflow_dispatch: @@ -22,22 +22,21 @@ on: description: "Environment to run" required: true default: "development" - mode: - description: "Running mode" + runcd: + description: "Run CD pipeline" + required: true + default: "false" env: DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: "true" - # Shared Landing Zone - SHARED_RG_NAME: "gtc-rg-devtest-shared-dev-001" - SHARED_RG_LOCATION: "westus2" - SHARED_BICEP_TEMPLATE: ".azure/templates/landingzone-shared.bicep" - SHARED_BICEP_PARAMETERS: ".azure/variables/landingzone-shared-development.bicepparam" - # Product Landing Zone + SPOKE_MGMT_RG_NAME: "gtc-spokemgmt-dev-wus2-001-rg" + SPOKE_MGMT_RG_LOCATION: "westus2" + SPOKE_BICEP_TEMPLATE: ".azure/templates/platform-spoke-mgmt.bicep" + SPOKE_BICEP_PARAMETERS: ".azure/variables/platform-spoke-mgmt-dev.bicepparam" + PRODUCT_RG_NAME: "gtc-semker-dev-wus2-001-rg" PRODUCT_RG_LOCATION: "westus2" - PRODUCT_RG_NAME: "gtc-rg-devtest-semkernel-dev-001" - KEYVAULT_NAME: "kv-semkernel-dev-002" - PRODUCT_BICEP_TEMPLATE: ".azure/templates/landingzone-api-sql.bicep" - PRODUCT_BICEP_PARAMETERS: ".azure/variables/landingzone-api-sql-development.bicepparam" + PRODUCT_BICEP_TEMPLATE: ".azure/templates/landingzone-web-api-sql.bicep" + PRODUCT_BICEP_PARAMETERS: ".azure/variables/landingzone-web-api-sql-dev.bicepparam" permissions: id-token: write @@ -45,9 +44,10 @@ permissions: security-events: write jobs: - development_Stage_deploy_landing_zone: - name: "Deploy landing zone IaC" + ci: + name: "Validate landing zone IaC" runs-on: ubuntu-latest + if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' || github.event_name == 'push' environment: development steps: - name: Checkout @@ -60,26 +60,6 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - # SHARED RG - - name: Create ${{ env.SHARED_RG_NAME }} - uses: Azure/cli@v2.1.0 - with: - inlineScript: | - az group create -n ${{ env.SHARED_RG_NAME }} -l ${{ env.SHARED_RG_LOCATION }} - - - name: Validate ${{ env.SHARED_RG_NAME }} - uses: Azure/cli@v2.1.0 - with: - inlineScript: | - az deployment group what-if --resource-group ${{ env.SHARED_RG_NAME }} --template-file ${{ env.SHARED_BICEP_TEMPLATE }} --parameters ${{ env.SHARED_BICEP_PARAMETERS }} - - - name: Deploy ${{ env.SHARED_RG_NAME }} - uses: Azure/cli@v2.1.0 - with: - inlineScript: | - az deployment group create --resource-group ${{ env.SHARED_RG_NAME }} --template-file ${{ env.SHARED_BICEP_TEMPLATE }} --parameters ${{ env.SHARED_BICEP_PARAMETERS }} - - # PRODUCT RG - name: Create ${{ env.PRODUCT_RG_NAME }} uses: Azure/cli@v2.1.0 with: @@ -91,9 +71,29 @@ jobs: with: inlineScript: | az deployment group what-if --resource-group ${{ env.PRODUCT_RG_NAME }} --template-file ${{ env.PRODUCT_BICEP_TEMPLATE }} --parameters ${{ env.PRODUCT_BICEP_PARAMETERS }} --parameters sqlAdminUser=${{ secrets.SQL_ADMIN_USER }} sqlAdminPassword=${{ secrets.SQL_ADMIN_PASSWORD }} + + cd: + name: "Deploy landing zone IaC" + runs-on: ubuntu-latest + needs: ci + if: | + (github.event_name == 'push' && github.ref == 'refs/heads/main') || + (github.event_name == 'workflow_dispatch' && (github.event.inputs.runcd == 'true' || github.event.inputs.runcd == true)) + environment: development + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: "Az CLI login" + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: Deploy ${{ env.PRODUCT_RG_NAME }} uses: Azure/cli@v2.1.0 with: inlineScript: | - az deployment group create --resource-group ${{ env.PRODUCT_RG_NAME }} --template-file ${{ env.PRODUCT_BICEP_TEMPLATE }} --parameters ${{ env.PRODUCT_BICEP_PARAMETERS }} --parameters sqlAdminUser=${{ secrets.SQL_ADMIN_USER }} sqlAdminPassword=${{ secrets.SQL_ADMIN_PASSWORD }} \ No newline at end of file + az deployment group create --resource-group ${{ env.PRODUCT_RG_NAME }} --template-file ${{ env.PRODUCT_BICEP_TEMPLATE }} --parameters ${{ env.PRODUCT_BICEP_PARAMETERS }} --parameters sqlAdminUser=${{ secrets.SQL_ADMIN_USER }} sqlAdminPassword=${{ secrets.SQL_ADMIN_PASSWORD }} + \ No newline at end of file diff --git a/.github/workflows/gtc-semker-web-api-sql.yml b/.github/workflows/gtc-semker-web-api-sql.yml new file mode 100644 index 0000000..19b5246 --- /dev/null +++ b/.github/workflows/gtc-semker-web-api-sql.yml @@ -0,0 +1,326 @@ +name: 'Web & API & SQL CI/CD' +on: + pull_request: + branches: + - main + paths: + - .github/workflows/gtc-semker-web-api-sql.yml + - src/** + push: + branches: + - main + paths: + - .github/workflows/gtc-semker-web-api-sql.yml + - src/** + workflow_dispatch: + inputs: + environment: + description: "Environment to run" + required: true + default: "development" + runcd: + description: "Run CD pipeline" + required: true + default: "false" + +permissions: + id-token: write + contents: read + security-events: write + +env: + API_ARTIFACT_OUTPUT: 'publish_output' + WEB_ARTIFACT_OUTPUT: 'publish_web_output' + API_ARTIFACT_NAME: 'api-artifact' + WEB_ARTIFACT_NAME: 'web-artifact' + MIGRATION_ARTIFACT_NAME: 'ef-migrations' + MIGRATION_ARTIFACT_PATH: 'ef-migrations.sql' + MIGRATION_ARTIFACT_OUTPUT_PATH: './' + +jobs: + ci: + name: "Web, API and SQL CI" + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' || github.event_name == 'push' + environment: development + strategy: + matrix: + DOTNET_VERSION: ["10.x"] + env: + SCRIPTS_PATH: "./.github/scripts" + SRC_PATH: "./src" + SRC_SLN: "SemanticKernelBlazor.sln" + API_PATH: "Presentation.WebApi" + API_PROJECT: "Presentation.WebApi.csproj" + TEST_PATH: "Tests.Specs.Integration" + TEST_PROJECT: "Tests.Specs.Integration.csproj" + WEB_PATH: "Presentation.Blazor" + WEB_PROJECT: "Presentation.Blazor.csproj" + INFRA_PATH: 'Infrastructure.SqlServer' + INFRA_PROJECT: 'Infrastructure.SqlServer.csproj' + INFRA_DBCONTEXT: 'SemanticKernelContext' + RUNTIME_ENV: "Development" + CONFIGURATION: "Release" + VERSION_MAJOR: '1' + VERSION_MINOR: '1' + + steps: + - name: checkout + uses: actions/checkout@v3 + + - name: dotnet version ${{ matrix.DOTNET_VERSION }} + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ matrix.DOTNET_VERSION }} + + - name: Get Semantic Version + id: get_version + run: | + $json = & "${{ env.SCRIPTS_PATH }}/ci/Get-Version.ps1" -Major ${{ env.VERSION_MAJOR }} -Minor ${{ env.VERSION_MINOR }} -Patch $env:GITHUB_RUN_NUMBER + $versionObj = ($json | ConvertFrom-Json) + echo "SEMANTIC_VERSION=$($versionObj.SemanticVersion)" >> $env:GITHUB_OUTPUT + echo "FILE_VERSION=$($versionObj.FileVersion)" >> $env:GITHUB_OUTPUT + echo "ASSEMBLY_VERSION=$($versionObj.AssemblyVersion)" >> $env:GITHUB_OUTPUT + echo "INFORMATIONAL_VERSION=$($versionObj.InformationalVersion)" >> $env:GITHUB_OUTPUT + shell: pwsh + + - name: Set-Version.ps1 + run: | + $version = ${{ env.SCRIPTS_PATH }}/ci/Set-Version.ps1 -Path ${{ env.SRC_PATH }} -VersionToReplace 1.0.0 -Major ${{ env.VERSION_MAJOR }} -Minor ${{ env.VERSION_MINOR }} -Patch $env:GITHUB_RUN_NUMBER + echo $version + echo "VERSION=$version" >> $GITHUB_ENV + shell: pwsh + + - name: pipeline environment configuration + run: | + echo "ASPNETCORE_ENVIRONMENT=${{ env.RUNTIME_ENV }}" >> $GITHUB_ENV + echo "OpenAI:ApiKey=${{ secrets.OPENAI_APIKEY }}" >> $GITHUB_ENV + shell: pwsh + + - name: App Settings Variable Substitution + uses: microsoft/variable-substitution@v1 + with: + files: '${{ env.SRC_PATH }}/${{ env.API_PATH }}/appsettings.json, ${{ env.SRC_PATH }}/${{ env.API_PATH }}/appsettings.${{ env.RUNTIME_ENV }}.json, ${{ env.SRC_PATH }}/${{ env.TEST_PATH }}/appsettings.test.json' + env: + OpenAI.ApiKey: ${{ secrets.OPENAI_APIKEY }} + + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: csharp + + - name: Build + run: | + dotnet build ${{ env.SRC_PATH }}/${{ env.SRC_SLN }} --configuration ${{ env.CONFIGURATION }} + shell: pwsh + + - name: Install dotnet-ef + run: | + dotnet tool install --global dotnet-ef + echo Home: $env:HOME + echo "GITHUB_PATH=$env:HOME/.dotnet/tools" >> $GITHUB_ENV + shell: pwsh + + - name: Check for uncommitted EF Core model changes (diagnostic) + run: | + Write-Host "Working Directory: $(Get-Location)" + $tempDir = "${{ env.SRC_PATH }}/${{ env.INFRA_PATH }}/.temp-migrations" + if (Test-Path $tempDir) { Remove-Item -Recurse -Force $tempDir } + Write-Host "Running command: dotnet ef migrations add TempCheck --project ${{ env.SRC_PATH }}/${{ env.INFRA_PATH }}/${{ env.INFRA_PROJECT }} --startup-project ${{ env.SRC_PATH }}/${{ env.API_PATH }}/${{ env.API_PROJECT }} --context ${{ env.INFRA_DBCONTEXT }} --output-dir $tempDir" + dotnet ef migrations add TempCheck --project ${{ env.SRC_PATH }}/${{ env.INFRA_PATH }}/${{ env.INFRA_PROJECT }} --startup-project ${{ env.SRC_PATH }}/${{ env.API_PATH }}/${{ env.API_PROJECT }} --context ${{ env.INFRA_DBCONTEXT }} --output-dir $tempDir --configuration ${{ env.CONFIGURATION }} --no-build + if ($LASTEXITCODE -ne 0) { + Write-Host "::error::dotnet ef migrations add failed." + exit 1 + } + if (Test-Path $tempDir) { + $files = Get-ChildItem -Path $tempDir + $files | ForEach-Object { Write-Host $_.FullName } + if ($files) { + Write-Host "::error::Uncommitted model changes detected! Please run 'dotnet ef migrations add' locally and commit the migration files." + Remove-Item -Recurse -Force $tempDir + exit 1 + } + Remove-Item -Recurse -Force $tempDir + } + shell: pwsh + + - name: Create ${{ env.INFRA_DBCONTEXT }} SQL Script + run: | + dotnet ef migrations script --project ${{ env.SRC_PATH }}/${{ env.INFRA_PATH }}/${{ env.INFRA_PROJECT }} --startup-project ${{ env.SRC_PATH }}/${{ env.API_PATH }}/${{ env.API_PROJECT }} --context ${{ env.INFRA_DBCONTEXT }} --output ${{ env.MIGRATION_ARTIFACT_PATH }} --idempotent --configuration ${{ env.CONFIGURATION }} --no-build + shell: pwsh + + - name: Upload migration script + uses: actions/upload-artifact@v4 + with: + name: ${{ env.MIGRATION_ARTIFACT_NAME }} + path: ${{ env.MIGRATION_ARTIFACT_PATH }} + + - name: Test + run: | + mkdir -p TestResults-${{ matrix.DOTNET_VERSION }} + dotnet test ${{ env.SRC_PATH }}/${{ env.TEST_PATH }}/${{ env.TEST_PROJECT }} --configuration ${{ env.CONFIGURATION }} --results-directory TestResults-${{ matrix.DOTNET_VERSION }} --collect:"Code Coverage" --verbosity normal + shell: pwsh + + - name: Upload test results + uses: actions/upload-artifact@v4 + with: + name: dotnet-results-${{ matrix.DOTNET_VERSION }} + path: TestResults-${{ matrix.DOTNET_VERSION }} + if: ${{ always() }} + + - name: Pack Web API artifact + run: | + dotnet publish ${{ env.SRC_PATH }}/${{ env.API_PATH }}/${{ env.API_PROJECT }} --configuration ${{ env.CONFIGURATION }} --no-build --output publish_output + shell: pwsh + + - name: Upload Web API artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ env.API_ARTIFACT_NAME }} + path: ${{ env.API_ARTIFACT_OUTPUT }}/** + + - name: Pack Blazor artifact + run: | + dotnet publish ${{ env.SRC_PATH }}/${{ env.WEB_PATH }}/${{ env.WEB_PROJECT }} --configuration ${{ env.CONFIGURATION }} --no-build --output publish_web_output + shell: pwsh + + - name: Upload Blazor artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ env.WEB_ARTIFACT_NAME }} + path: ${{ env.WEB_ARTIFACT_OUTPUT }}/** + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + if: ${{ !github.event.repository.private }} + + - name: Run Coverage Script + run: | + pwsh ${{ env.SRC_PATH }}/Get-CodeCoverage.ps1 ` + -TestProjectFilter '${{ env.TEST_PROJECT }}' ` + -ProdPackagesOnly + shell: pwsh + + - name: Upload Coverage Report + uses: actions/upload-artifact@v4 + with: + name: coverage-report-${{ matrix.DOTNET_VERSION }} + path: ${{ env.SRC_PATH }}/TestResults/Reports/**/index.html + if: ${{ always() }} + + cd: + name: "Web, API and SQL CD" + runs-on: ubuntu-latest + needs: ci + if: | + (github.event_name == 'push' && github.ref == 'refs/heads/main') || + (github.event_name == 'workflow_dispatch' && (github.event.inputs.runcd == 'true' || github.event.inputs.runcd == true)) + environment: development + strategy: + matrix: + DOTNET_VERSION: ["10.x"] + env: + SPOKE_RG_NAME: 'gtc-spokemgmt-dev-wus2-001-rg' + APPI_NAME: 'spokemgmt-dev-wus2-001-appi' + AZURE_APIAPP_NAME: semker-dev-wus2-001-api + AZURE_WEBAPP_PACKAGE_PATH: '.' + AZURE_WEBAPP_NAME: semker-dev-wus2-001-web + AZURE_RG_NAME: 'gtc-semker-dev-wus2-001-rg' + SQL_NAME: 'semker-dev-wus2-001-sql' + SQLDB_NAME: 'semker-dev-wus2-001-sqldb' + RUNTIME_ENV: "Development" + + steps: + - name: Download Web API artifact + uses: actions/download-artifact@v4 + with: + name: ${{ env.API_ARTIFACT_NAME }} + path: ${{ env.API_ARTIFACT_OUTPUT }} + + - name: Download Web Artifact + uses: actions/download-artifact@v4 + with: + name: ${{ env.WEB_ARTIFACT_NAME }} + path: ${{ env.WEB_ARTIFACT_OUTPUT }} + + - name: Download migration script + uses: actions/download-artifact@v4 + with: + name: ${{ env.MIGRATION_ARTIFACT_NAME }} + path: ${{ env.MIGRATION_ARTIFACT_OUTPUT_PATH }} + + + - name: az login + uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + + - name: Get SQL connection string + id: get_sql_conn + run: | + $TEMP_STR=az sql db show-connection-string --client ado.net --server ${{ env.SQL_NAME }} --name ${{ env.SQLDB_NAME }} -o tsv + $TEMP_STR=$TEMP_STR.replace("", "${{ secrets.SQL_ADMIN_USER }}") + $TEMP_STR=$TEMP_STR.replace("", "${{ secrets.SQL_ADMIN_PASSWORD }}") + echo "CONN_STR=$TEMP_STR" >> $env:GITHUB_OUTPUT + shell: pwsh + + - name: Deploy SQL script + uses: azure/sql-action@v2 + with: + connection-string: ${{ steps.get_sql_conn.outputs.CONN_STR }} + path: ${{ env.MIGRATION_ARTIFACT_PATH }} + + - name: Deploy Web API to Azure App Service + uses: azure/webapps-deploy@v2 + with: + app-name: ${{ env.AZURE_APIAPP_NAME }} + package: ${{ env.API_ARTIFACT_OUTPUT }} + + - name: ${{ env.AZURE_APIAPP_NAME }} app settings + run: | + az config set extension.use_dynamic_install=yes_without_prompt + $TEMP_JSON = az monitor app-insights component show -g ${{ env.SPOKE_RG_NAME }} --app ${{ env.APPI_NAME }} | ConvertFrom-Json + $INSTR_KEY = $TEMP_JSON.instrumentationKey + $CONN_STR = $TEMP_JSON.connectionString + az webapp config appsettings set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_APIAPP_NAME }} --settings APPINSIGHTS_INSTRUMENTATIONKEY=$INSTR_KEY + az webapp config appsettings set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_APIAPP_NAME }} --settings APPLICATIONINSIGHTS_CONNECTION_STRING=$CONN_STR + az webapp config appsettings set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_APIAPP_NAME }} --settings ASPNETCORE_ENVIRONMENT=${{ env.RUNTIME_ENV }} + az webapp config appsettings set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_APIAPP_NAME }} --settings EntraExternalId:TenantId=${{ secrets.EEID_TENANT_ID }} + az webapp config appsettings set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_APIAPP_NAME }} --settings EntraExternalId:ClientId=${{ secrets.API_CLIENT_ID }} + az webapp config appsettings set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_APIAPP_NAME }} --settings OpenAI:ApiKey=${{ secrets.OPENAI_APIKEY }} + shell: pwsh + + - name: ${{ env.AZURE_APIAPP_NAME }} connection strings + run: | + az webapp config connection-string set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_APIAPP_NAME }} -t SQLServer --settings DefaultConnection="${{ steps.get_sql_conn.outputs.CONN_STR }}" + shell: pwsh + + - name: Deploy ${{ env.AZURE_WEBAPP_NAME }} + uses: azure/webapps-deploy@v2 + with: + app-name: ${{ env.AZURE_WEBAPP_NAME }} + package: ${{ env.WEB_ARTIFACT_OUTPUT }} + + - name: ${{ env.AZURE_WEBAPP_NAME }} app settings + run: | + az config set extension.use_dynamic_install=yes_without_prompt + $TEMP_JSON = az monitor app-insights component show -g ${{ env.SPOKE_RG_NAME }} --app ${{ env.APPI_NAME }} | ConvertFrom-Json + $INSTR_KEY = $TEMP_JSON.instrumentationKey + $CONN_STR = $TEMP_JSON.connectionString + az webapp config appsettings set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_WEBAPP_NAME }} --settings APPINSIGHTS_INSTRUMENTATIONKEY=$INSTR_KEY + az webapp config appsettings set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_WEBAPP_NAME }} --settings APPLICATIONINSIGHTS_CONNECTION_STRING=$CONN_STR + az webapp config appsettings set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_WEBAPP_NAME }} --settings ASPNETCORE_ENVIRONMENT=${{ env.RUNTIME_ENV }} + az webapp config appsettings set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_WEBAPP_NAME }} --settings EntraExternalId:TenantId=${{ secrets.EEID_TENANT_ID }} + az webapp config appsettings set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_WEBAPP_NAME }} --settings EntraExternalId:ClientId=${{ secrets.WEB_CLIENT_ID }} + az webapp config appsettings set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_WEBAPP_NAME }} --settings EntraExternalId:ClientSecret=${{ secrets.WEB_CLIENT_SECRET }} + az webapp config appsettings set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_WEBAPP_NAME }} --settings BackendApi:BaseUrl="https://${{ env.AZURE_APIAPP_NAME }}.azurewebsites.net" + az webapp config appsettings set -g ${{ env.AZURE_RG_NAME }} -n ${{ env.AZURE_WEBAPP_NAME }} --settings BackendApi:ClientId=${{ secrets.API_CLIENT_ID }} + shell: pwsh + + - name: Swap to production slot + run: | + az webapp deployment slot swap --resource-group ${{ env.AZURE_RG_NAME }} --name ${{ env.AZURE_APIAPP_NAME }} --slot staging --target-slot production + echo "Swap finished. App Service Application URL: https://$(az webapp show --resource-group ${{ env.AZURE_RG_NAME }} --name ${{ env.AZURE_APIAPP_NAME }} --query hostNames[0] -o tsv)" + if: ${{ false }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2135767..ad9e126 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ # Documentation *.Presentation.WebApi.xml +[Ss]wagger/ swagger.json .config/ diff --git a/README.md b/README.md index 1dd1f5d..596e378 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ To get started, follow the steps below: 4. Create your SQL Server database & schema (via *dotnet ef* command) ``` cd ../../ - dotnet ef database update --project .\src\Infrastructure.SqlServer\Infrastructure.SqlServer.csproj --startup-project .\src\Presentation.WebApi\Presentation.WebApi.csproj --context SemanticKernelContext --connection "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=SemanticKernelMicroservice;Min Pool Size=3;MultipleActiveResultSets=True;Trusted_Connection=Yes;TrustServerCertificate=True;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30" + dotnet ef database update --project .\src\Infrastructure.SqlServer\Infrastructure.SqlServer.csproj --startup-project .\src\Presentation.WebApi\Presentation.WebApi.csproj --context SemanticKernelContext --connection "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=SemanticKernel;Min Pool Size=3;MultipleActiveResultSets=True;Trusted_Connection=Yes;TrustServerCertificate=True;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30" ``` 5. Run Tests (Tests.Specs.Integration) ``` @@ -156,7 +156,7 @@ dotnet user-secrets set "ConnectionStrings:DefaultConnection" "YOUR_SQL_CONNECTI 3. (Optional) If you have an existing database, scaffold current entities into your project ``` - dotnet ef dbcontext scaffold "Data Source=localhost;Initial Catalog=semantickernelmicroservice;Min Pool Size=3;MultipleActiveResultSets=True;Trusted_Connection=Yes;TrustServerCertificate=True;" Microsoft.EntityFrameworkCore.SqlServer -t WeatherForecastView -c WeatherChannelContext -f -o WebApi + dotnet ef dbcontext scaffold "Data Source=localhost;Initial Catalog=SemanticKernel;Min Pool Size=3;MultipleActiveResultSets=True;Trusted_Connection=Yes;TrustServerCertificate=True;" Microsoft.EntityFrameworkCore.SqlServer -t WeatherForecastView -c WeatherChannelContext -f -o WebApi ``` 4. Create an initial migration @@ -168,7 +168,7 @@ dotnet user-secrets set "ConnectionStrings:DefaultConnection" "YOUR_SQL_CONNECTI 6. When ready to deploy new entities and configurations ``` - dotnet ef database update --project .\src\Infrastructure.SqlServer\Infrastructure.SqlServer.csproj --startup-project .\src\Presentation.WebApi\Presentation.WebApi.csproj --context SemanticKernelContext --connection "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=SemanticKernelMicroservice;Min Pool Size=3;MultipleActiveResultSets=True;Trusted_Connection=Yes;TrustServerCertificate=True;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30" + dotnet ef database update --project .\src\Infrastructure.SqlServer\Infrastructure.SqlServer.csproj --startup-project .\src\Presentation.WebApi\Presentation.WebApi.csproj --context SemanticKernelContext --connection "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=SemanticKernel;Min Pool Size=3;MultipleActiveResultSets=True;Trusted_Connection=Yes;TrustServerCertificate=True;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30" ``` 7. When an entity changes, is created or deleted, create a new migration. Suggest doing this each new version. ``` @@ -274,6 +274,7 @@ The key differences between Entity Framework (EF) and Semantic Kernel memory: * [github.com/goodtocode](https://www.github.com/goodtocode) # Technologies +* [ASP.NET Core Fluent UI](https://www.fluentui-blazor.net/) * [ASP.NET .Net](https://docs.microsoft.com/en-us/aspnet/core/introduction-to-aspnet-core) * [Entity Framework Core](https://docs.microsoft.com/en-us/ef/core/) @@ -288,6 +289,7 @@ The key differences between Entity Framework (EF) and Semantic Kernel memory: * Entity Framework Core * Microsoft.AspNetCore.App * Microsoft.AspNetCore.Cors +* Microsoft.Aspnetcore.Fluentui * Swashbuckle.AspNetCore.SwaggerGen * Swashbuckle.AspNetCore.SwaggerUI @@ -307,5 +309,6 @@ The key differences between Entity Framework (EF) and Semantic Kernel memory: | 1.1.7 | 2025-Aug-22 | Deprecated MediatR (removed from solution) | | 1.1.8 | 2025-Aug-23 | Updated docs. Fixed runtime message post | | 1.1.9 | 2025-Oct-31 | Added build/test precursor, plugin compatibility, improved code coverage | +| 2.0.0 | 2026-Feb-02 | Blazor Fluent UI (Microsoft.Aspnetcore.FluentUI) fluentui-blazor.net | This project is licensed with the [MIT license](https://mit-license.org/). \ No newline at end of file diff --git a/data/.vscode/tasks.json b/data/.vscode/tasks.json new file mode 100644 index 0000000..295b8a2 --- /dev/null +++ b/data/.vscode/tasks.json @@ -0,0 +1,17 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build", + "type": "shell", + "command": "dotnet build", + "problemMatcher": "$sqlproj-problem-matcher", + "isBackground": true, + "group": { + "kind": "build", + "isDefault": "false" + }, + "detail": "Builds the SQL project" + } + ] +} \ No newline at end of file diff --git a/data/Admin/Drop Tables.sql b/data/Admin/Drop Tables.sql new file mode 100644 index 0000000..e2b68a4 --- /dev/null +++ b/data/Admin/Drop Tables.sql @@ -0,0 +1,45 @@ +IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[__EFMigrationsHistory]') AND type in (N'U')) +DROP TABLE [dbo].[__EFMigrationsHistory] +GO + +-- +-- Identity +-- +IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Actors]') AND type in (N'U')) +DROP TABLE [dbo].[Actors] +GO + +-- +-- Content +-- +-- Tagging +IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Tags]') AND type in (N'U')) +DROP TABLE [dbo].[Tags] +GO + +-- +-- Chat +-- +IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ChatMessages]') AND type in (N'U')) +DROP TABLE [dbo].[ChatMessages] +GO + +IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ChatSessions]') AND type in (N'U')) +DROP TABLE [dbo].[ChatSessions] +GO + +IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TextAudio]') AND type in (N'U')) +DROP TABLE [dbo].[TextAudio] +GO + +IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TextImages]') AND type in (N'U')) +DROP TABLE [dbo].[TextImages] +GO + +IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TextPrompts]') AND type in (N'U')) +DROP TABLE [dbo].[TextPrompts] +GO + +IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TextResponses]') AND type in (N'U')) +DROP TABLE [dbo].[TextResponses] +GO \ No newline at end of file diff --git a/src/Presentation.Blazor/Pages/Chat/Components/NewChatSession.razor.css b/data/Chat/ChatMessages.sql similarity index 100% rename from src/Presentation.Blazor/Pages/Chat/Components/NewChatSession.razor.css rename to data/Chat/ChatMessages.sql diff --git a/data/Chat/ChatSessions.sql b/data/Chat/ChatSessions.sql new file mode 100644 index 0000000..e69de29 diff --git a/data/Chat/Multimedia.sql b/data/Chat/Multimedia.sql new file mode 100644 index 0000000..1b1d451 --- /dev/null +++ b/data/Chat/Multimedia.sql @@ -0,0 +1 @@ +-- Write your own SQL object definition here, and it'll be included in your package. diff --git a/data/Data.sqlproj b/data/Data.sqlproj new file mode 100644 index 0000000..b312bcc --- /dev/null +++ b/data/Data.sqlproj @@ -0,0 +1,19 @@ + + + + + Data + {20420E32-B4C5-4E60-8F3F-AEC211310355} + Microsoft.Data.Tools.Schema.Sql.Sql160DatabaseSchemaProvider + 1033, CI + + + + + + + + + + + \ No newline at end of file diff --git a/data/Identity.sql b/data/Identity.sql new file mode 100644 index 0000000..e69de29 diff --git a/src/.editorconfig b/src/.editorconfig index 9d47ae3..5cedb2b 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -1,6 +1,88 @@ # Setting errors for SDK projects under dotnet folder [*.cs] dotnet_separate_import_directive_groups = false +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_prefer_system_threading_lock = true:suggestion +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_indent_labels = one_less_than_current #dotnet_diagnostic.CA2007.severity = error # Do not directly await a Task #dotnet_diagnostic.VSTHRD111.severity = error # Use .ConfigureAwait(bool) -#dotnet_diagnostic.IDE1006.severity = error # Naming rule violations \ No newline at end of file +#dotnet_diagnostic.IDE1006.severity = error # Naming rule violations +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf diff --git a/src/App.razor b/src/App.razor new file mode 100644 index 0000000..e69de29 diff --git a/src/Presentation.Blazor/Components/Analytics/ClarityAnalytics.razor b/src/Aspects.Components/Analytics/ClarityAnalytics.razor similarity index 84% rename from src/Presentation.Blazor/Components/Analytics/ClarityAnalytics.razor rename to src/Aspects.Components/Analytics/ClarityAnalytics.razor index d20979e..b03c4de 100644 --- a/src/Presentation.Blazor/Components/Analytics/ClarityAnalytics.razor +++ b/src/Aspects.Components/Analytics/ClarityAnalytics.razor @@ -1,6 +1,4 @@ -@namespace Goodtocode.SemanticKernel.Presentation.Blazor.Components.Analytics - -@code { +@code { [Parameter] public string ProjectId { get; set; } = string.Empty; } diff --git a/src/Aspects.Components/Aspects.Components.csproj b/src/Aspects.Components/Aspects.Components.csproj new file mode 100644 index 0000000..1e318ff --- /dev/null +++ b/src/Aspects.Components/Aspects.Components.csproj @@ -0,0 +1,34 @@ + + + + Cannery.Aspects.Components + Cannery.Aspects.Components + 1.0.0 + net10.0 + enable + enable + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Aspects.Components/Auth/Components/UserAuthMenu.razor b/src/Aspects.Components/Auth/Components/UserAuthMenu.razor new file mode 100644 index 0000000..d54c4db --- /dev/null +++ b/src/Aspects.Components/Auth/Components/UserAuthMenu.razor @@ -0,0 +1,31 @@ +@using Cannery.Aspects.Components.Auth.Routing +@using Microsoft.AspNetCore.Components.Authorization +@using Microsoft.FluentUI.AspNetCore.Components + +@inject NavigationManager Navigation +@inject AuthenticationStateProvider AuthStateProvider + + + + + Sign Out + + + + + Sign In + + + + +@code { + private void SignIn() + { + Navigation.NavigateTo(RouteConstants.SignIn, forceLoad: true); + } + + private void SignOut() + { + Navigation.NavigateTo(RouteConstants.SignOut, forceLoad: true); + } +} diff --git a/src/Aspects.Components/Auth/Components/UserProfile.razor b/src/Aspects.Components/Auth/Components/UserProfile.razor new file mode 100644 index 0000000..5ee3d34 --- /dev/null +++ b/src/Aspects.Components/Auth/Components/UserProfile.razor @@ -0,0 +1,78 @@ +@using Cannery.Aspects.Components.Auth.Routing +@using Microsoft.AspNetCore.Components.Authorization + +@inject NavigationManager Navigation +@inject AuthenticationStateProvider AuthStateProvider + + + + + + + + + + + Sign out + + + + + + + + + Sign In + + + + +@code { + private string? UserDisplayName; + private string? UserImageUrl; + private const string DefaultImageUrl = "img/goodtocode-logo.png"; + + protected override async Task OnInitializedAsync() + { + var authState = await AuthStateProvider.GetAuthenticationStateAsync(); + var user = authState.User; + + if (user.Identity?.IsAuthenticated == true) + { + UserDisplayName = user.FindFirst("name")?.Value; + UserImageUrl = user.FindFirst("picture")?.Value; + } + } + + private string GetUserImageUrl() + { + return string.IsNullOrWhiteSpace(UserImageUrl) ? DefaultImageUrl : UserImageUrl; + } + + private void SignOut() + { + Navigation.NavigateTo(RouteConstants.SignOut, forceLoad: true); + } + + private void ViewAccount() + { + Navigation.NavigateTo(RouteConstants.Account, forceLoad: true); + } + + private string GetInitials(string? name) + { + if (string.IsNullOrWhiteSpace(name)) + return string.Empty; + + var parts = name.Split(' ', StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 1) + return parts[0].Substring(0, 1).ToUpperInvariant(); + + return string.Concat(parts[0][0], parts[^1][0]).ToUpperInvariant(); + } +} diff --git a/src/Aspects.Components/Auth/IUserClaimsInfo.cs b/src/Aspects.Components/Auth/IUserClaimsInfo.cs new file mode 100644 index 0000000..e727597 --- /dev/null +++ b/src/Aspects.Components/Auth/IUserClaimsInfo.cs @@ -0,0 +1,50 @@ +namespace Cannery.Aspects.Components.Auth; + +/// +/// Represents a user's information, including identifiers, personal details, and contact information. +/// +/// This interface provides a standardized structure for accessing user-related data, such as unique +/// identifiers, name details, and email address. It is commonly used in scenarios where user identity and contact +/// information need to be retrieved or processed. +public interface IUserClaimsInfo +{ + /// + /// Gets the unique identifier for the object. + /// + Guid ObjectId { get; } + + /// + /// Gets the unique identifier of the tenant associated with the current context. + /// + Guid TenantId { get; } + + /// + /// Gets the first name of the individual. + /// + string Givenname { get; } + + /// + /// Gets the last name of the individual. + /// + string Surname { get; } + + /// + /// Gets the email address associated with the entity. + /// + string Email { get; } + + /// + /// Gets the highest role + /// + ICollection Roles { get; } + + /// + /// Gets the collection of scopes associated with the current operation. + /// + ICollection Scopes { get; } + + /// + /// Gets the collection of group names associated with the current user. + /// + ICollection Groups { get; } +} diff --git a/src/Aspects.Components/Auth/Middleware/DownstreamApiAccessTokenProvider.cs b/src/Aspects.Components/Auth/Middleware/DownstreamApiAccessTokenProvider.cs new file mode 100644 index 0000000..8f166da --- /dev/null +++ b/src/Aspects.Components/Auth/Middleware/DownstreamApiAccessTokenProvider.cs @@ -0,0 +1,26 @@ +using Goodtocode.SecuredHttpClient.Middleware; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Identity.Web; + +namespace Cannery.Aspects.Components.Auth.Middleware; + +public class DownstreamApiAccessTokenProvider(IHttpContextAccessor httpContextAccessor, ITokenAcquisition tokenAcquisition, IConfiguration configuration) : IAccessTokenProvider +{ + private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor; + private readonly ITokenAcquisition _tokenAcquisition = tokenAcquisition; + private readonly IConfiguration _configuration = configuration; + + public async Task GetAccessTokenAsync() + { + var context = _httpContextAccessor.HttpContext; + if (!context?.User?.Identity?.IsAuthenticated == true) + return string.Empty; + + var accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync([ + $"api://{_configuration["BackendApi:ClientId"] ?? Guid.Empty.ToString()}/.default" + ], user: context?.User); + + return accessToken ?? string.Empty; + } +} \ No newline at end of file diff --git a/src/Aspects.Components/Auth/Middleware/MsGraphAccessTokenProvider.cs b/src/Aspects.Components/Auth/Middleware/MsGraphAccessTokenProvider.cs new file mode 100644 index 0000000..9c1b6b3 --- /dev/null +++ b/src/Aspects.Components/Auth/Middleware/MsGraphAccessTokenProvider.cs @@ -0,0 +1,21 @@ +using Goodtocode.SecuredHttpClient.Middleware; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.AspNetCore.Http; + +namespace Cannery.Aspects.Components.Auth.Middleware; + +public class MsGraphAccessTokenProvider(IHttpContextAccessor httpContextAccessor) : IAccessTokenProvider +{ + private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor; + + public async Task GetAccessTokenAsync() + { + var context = _httpContextAccessor.HttpContext; + if (context == null) + return string.Empty; + + var accessToken = await context.GetTokenAsync(OpenIdConnectDefaults.AuthenticationScheme, "access_token"); + return accessToken ?? string.Empty; + } +} \ No newline at end of file diff --git a/src/Aspects.Components/Auth/Routing/LoginLogoutEndpointRouteBuilderExtensions.cs b/src/Aspects.Components/Auth/Routing/LoginLogoutEndpointRouteBuilderExtensions.cs new file mode 100644 index 0000000..7ef0337 --- /dev/null +++ b/src/Aspects.Components/Auth/Routing/LoginLogoutEndpointRouteBuilderExtensions.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; + +namespace Cannery.Aspects.Components.Auth.Routing; + +public static class LoginLogoutEndpointRouteBuilderExtensions +{ + public static IEndpointConventionBuilder MapSignInSignOut(this IEndpointRouteBuilder endpoints) + { + var group = endpoints.MapGroup(string.Empty); + + group.MapGet("/SignIn", (string? returnUrl) => TypedResults.Challenge(GetAuthProperties(returnUrl))) + .AllowAnonymous(); + + group.MapGet("/SignOut", (string? returnUrl) => TypedResults.SignOut(GetAuthProperties(returnUrl), + [CookieAuthenticationDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme])); + + return group; + } + + private static AuthenticationProperties GetAuthProperties(string? returnUrl) => + new() + { + RedirectUri = returnUrl switch + { + string => new Uri(returnUrl, UriKind.Absolute).PathAndQuery, + null => "/", + } + }; +} \ No newline at end of file diff --git a/src/Aspects.Components/Auth/Routing/RedirectToAccessDenied.razor b/src/Aspects.Components/Auth/Routing/RedirectToAccessDenied.razor new file mode 100644 index 0000000..ccad79a --- /dev/null +++ b/src/Aspects.Components/Auth/Routing/RedirectToAccessDenied.razor @@ -0,0 +1,11 @@ +@inject NavigationManager Navigation + +Access Denied +Redirecting to sign-in...> + +@code { + protected override void OnInitialized() + { + Navigation.NavigateTo(RouteConstants.SignIn, forceLoad: true); + } +} diff --git a/src/Aspects.Components/Auth/Routing/RedirectToResetPassword.razor b/src/Aspects.Components/Auth/Routing/RedirectToResetPassword.razor new file mode 100644 index 0000000..b099506 --- /dev/null +++ b/src/Aspects.Components/Auth/Routing/RedirectToResetPassword.razor @@ -0,0 +1,10 @@ +@inject NavigationManager Navigation + +Redirecting to password reset...> + +@code { + protected override void OnInitialized() + { + Navigation.NavigateTo("https://goodtocodesecuredev.ciamlogin.com/19800ad2-82e2-4946-abd5-c87ba78a7224/oauth2/v2.0/authorize?p=B2C_1A_PasswordReset", forceLoad: true); + } +} diff --git a/src/Aspects.Components/Auth/Routing/RedirectToSignIn.razor b/src/Aspects.Components/Auth/Routing/RedirectToSignIn.razor new file mode 100644 index 0000000..2d21ec2 --- /dev/null +++ b/src/Aspects.Components/Auth/Routing/RedirectToSignIn.razor @@ -0,0 +1,10 @@ +@inject NavigationManager Navigation + +Redirecting to sign-in... + +@code { + protected override void OnInitialized() + { + Navigation.NavigateTo(RouteConstants.SignIn, forceLoad: true); + } +} diff --git a/src/Aspects.Components/Auth/Routing/RedirectToSignOut.razor b/src/Aspects.Components/Auth/Routing/RedirectToSignOut.razor new file mode 100644 index 0000000..279183c --- /dev/null +++ b/src/Aspects.Components/Auth/Routing/RedirectToSignOut.razor @@ -0,0 +1,12 @@ +@using Cannery.Aspects.Components.Auth.Routing + +@inject NavigationManager Navigation + +Signing out... + +@code { + protected override void OnInitialized() + { + Navigation.NavigateTo(RouteConstants.SignOut, forceLoad: true); + } +} diff --git a/src/Aspects.Components/Auth/Routing/RouteConstants.cs b/src/Aspects.Components/Auth/Routing/RouteConstants.cs new file mode 100644 index 0000000..65540c0 --- /dev/null +++ b/src/Aspects.Components/Auth/Routing/RouteConstants.cs @@ -0,0 +1,12 @@ +namespace Cannery.Aspects.Components.Auth.Routing; + +public partial struct RouteConstants +{ + public const string Route = "authentication"; + public const string SignIn = "authentication/SignIn"; + public const string SignOut = "authentication/SignOut"; + public const string SignUp = "authentication/SignUp"; + public const string Account = "authentication/EditProfile"; + public const string ChangePassword = "authentication/ChangePassword"; + public const string ForgotPassword = "authentication/ForgotPassword"; +} diff --git a/src/Aspects.Components/Auth/Services/IUserSyncService.cs b/src/Aspects.Components/Auth/Services/IUserSyncService.cs new file mode 100644 index 0000000..c7e8a9d --- /dev/null +++ b/src/Aspects.Components/Auth/Services/IUserSyncService.cs @@ -0,0 +1,9 @@ +using System.Security.Claims; + +namespace Cannery.Aspects.Components.Auth.Services; + +public interface IUserSyncService +{ + void UserChanged(ClaimsPrincipal? user); + Task SyncUserAsync(ClaimsPrincipal? user); +} diff --git a/src/Aspects.Components/Auth/UserClaimsInfo.cs b/src/Aspects.Components/Auth/UserClaimsInfo.cs new file mode 100644 index 0000000..540f43d --- /dev/null +++ b/src/Aspects.Components/Auth/UserClaimsInfo.cs @@ -0,0 +1,64 @@ +using Microsoft.AspNetCore.Http; + +namespace Cannery.Aspects.Components.Auth; + +/// +/// User information implementation that retrieves data from the current HTTP context. +/// +/// HttpContext containing claims +public class UserClaimsInfo(IHttpContextAccessor contextAccessor) : IUserClaimsInfo +{ + private readonly HttpContext? context = contextAccessor?.HttpContext; + + /// + /// Gets the unique identifier of the user object associated with the current context. + /// + /// This property retrieves the value of the "objectidentifier" claim from the current user's context. + /// Ensure that the claim is present and properly formatted as a GUID in the authentication token. + public Guid ObjectId => Guid.TryParse( + context?.User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value, + out var objectId) ? objectId : Guid.Empty; + + /// + /// Gets the unique identifier of the tenant associated with the current user. + /// + /// The tenant ID is extracted from the user's claims using the claim type + /// "http://schemas.microsoft.com/identity/claims/tenantid". Ensure that the claim is present and valid in the + /// user's identity for this property to return a meaningful value. + public Guid TenantId => Guid.TryParse( + context?.User.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid")?.Value, + out var tenantId) ? tenantId : Guid.Empty; + + /// + /// Gets the first name of the user based on the associated claims. + /// + public string Givenname => context?.User.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname")?.Value ?? string.Empty; + + /// + /// Gets the last name of the current user based on their claims. + /// + public string Surname => context?.User.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname")?.Value ?? string.Empty; + + /// + /// Gets the email address of the current user based on their User Principal Name (UPN) claim. + /// + public string Email => context?.User.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn")?.Value ?? string.Empty; + + /// + /// Gets the collection of scopes associated with the current user. + /// + /// Scopes are typically used to define the permissions or access levels granted to the user. + /// This property retrieves the scopes from the user's claims, specifically from the claim with the type + /// "http://schemas.microsoft.com/identity/claims/scope". + public ICollection Scopes => context?.User.FindFirst("http://schemas.microsoft.com/identity/claims/scope")?.Value?.Split(' ').ToList() ?? []; + + /// + /// Gets the collection of roles associated with the current user. + /// + public ICollection Roles => context?.User.FindAll("http://schemas.microsoft.com/ws/2008/06/identity/claims/role").Select(c => c.Value).ToList() ?? []; + + /// + /// Gets the collection of group names associated with the current user. + /// + public ICollection Groups => context?.User.FindAll("groups").Select(c => c.Value).ToList() ?? []; +} \ No newline at end of file diff --git a/src/Aspects.Components/FormChangeType.cs b/src/Aspects.Components/FormChangeType.cs new file mode 100644 index 0000000..6054a4d --- /dev/null +++ b/src/Aspects.Components/FormChangeType.cs @@ -0,0 +1,8 @@ +namespace Cannery.Aspects.Components; + +public enum FormChangeType +{ + Created, + Updated, + Deleted +} diff --git a/src/Aspects.Components/Icons/ArrowIcon.razor b/src/Aspects.Components/Icons/ArrowIcon.razor new file mode 100644 index 0000000..41f5e47 --- /dev/null +++ b/src/Aspects.Components/Icons/ArrowIcon.razor @@ -0,0 +1,15 @@ + + + + +@code { + [Parameter] public string Direction { get; set; } = "right"; + [Parameter] public string Size { get; set; } = "1em"; + + private string PathData => Direction switch + { + "left" => "M12 8H4M8 12l-4-4 4-4", + "right" => "M4 8h8M8 4l4 4-4 4", + _ => "M4 8h8M8 4l4 4-4 4" + }; +} \ No newline at end of file diff --git a/src/Presentation.Blazor/Components/Skeleton/SkeletonList.razor b/src/Aspects.Components/Skeleton/SkeletonList.razor similarity index 54% rename from src/Presentation.Blazor/Components/Skeleton/SkeletonList.razor rename to src/Aspects.Components/Skeleton/SkeletonList.razor index 7236c44..b8fc5d9 100644 --- a/src/Presentation.Blazor/Components/Skeleton/SkeletonList.razor +++ b/src/Aspects.Components/Skeleton/SkeletonList.razor @@ -1,17 +1,20 @@ -@namespace Goodtocode.SemanticKernel.Presentation.Blazor.Components.Skeleton - -@if (!_isTimeoutReached) +@if (!_isTimeoutReached) { -
-
-
-
-
+ + + + + + } @code { [Parameter] - public int Timeout { get; set; } = 0; // Timeout in milliseconds + public int Timeout { get; set; } = 0; + [Parameter] + public string Class { get; set; } = string.Empty; + [Parameter] + public string Style { get; set; } = string.Empty; private bool _isTimeoutReached = false; private CancellationTokenSource? _cts; diff --git a/src/Aspects.Components/Skeleton/SkeletonTable.razor b/src/Aspects.Components/Skeleton/SkeletonTable.razor new file mode 100644 index 0000000..a2078e6 --- /dev/null +++ b/src/Aspects.Components/Skeleton/SkeletonTable.razor @@ -0,0 +1,53 @@ +@if (!_isTimeoutReached) +{ + + + + + + + + + +} + +@code { + [Parameter] + public int Timeout { get; set; } = 0; + [Parameter] + public string Class { get; set; } = string.Empty; + [Parameter] + public string Style { get; set; } = string.Empty; + + private bool _isTimeoutReached = false; + private CancellationTokenSource? _cts; + + protected override async Task OnParametersSetAsync() + { + if (Timeout > 0) + { + _cts?.Cancel(); + _cts = new CancellationTokenSource(); + try + { + await Task.Delay(Timeout, _cts.Token); + _isTimeoutReached = true; + StateHasChanged(); + } + catch (TaskCanceledException) + { + // Ignore cancellation + } + } + else + { + _isTimeoutReached = false; + } + } + + public void Dispose() + { + _cts?.Cancel(); + _cts?.Dispose(); + } +} diff --git a/src/Aspects.Components/Typography/H1Label.razor b/src/Aspects.Components/Typography/H1Label.razor new file mode 100644 index 0000000..8b8d452 --- /dev/null +++ b/src/Aspects.Components/Typography/H1Label.razor @@ -0,0 +1,17 @@ + + @ChildContent + + + + +@code { + [Parameter] public RenderFragment? ChildContent { get; set; } + [Parameter] public string? Class { get; set; } = "h1-label"; + [Parameter] public string? Style { get; set; } = string.Empty; +} diff --git a/src/Aspects.Components/Typography/H2Label.razor b/src/Aspects.Components/Typography/H2Label.razor new file mode 100644 index 0000000..adf690b --- /dev/null +++ b/src/Aspects.Components/Typography/H2Label.razor @@ -0,0 +1,17 @@ + + @ChildContent + + + + +@code { + [Parameter] public RenderFragment? ChildContent { get; set; } + [Parameter] public string? Class { get; set; } = "h2-label"; + [Parameter] public string? Style { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/src/Aspects.Components/Typography/H3Label.razor b/src/Aspects.Components/Typography/H3Label.razor new file mode 100644 index 0000000..114aa5c --- /dev/null +++ b/src/Aspects.Components/Typography/H3Label.razor @@ -0,0 +1,17 @@ + + @ChildContent + + + + +@code { + [Parameter] public RenderFragment? ChildContent { get; set; } + [Parameter] public string? Class { get; set; } = "h3-label"; + [Parameter] public string? Style { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/src/Aspects.Components/Typography/PLabel.razor b/src/Aspects.Components/Typography/PLabel.razor new file mode 100644 index 0000000..1e13377 --- /dev/null +++ b/src/Aspects.Components/Typography/PLabel.razor @@ -0,0 +1,18 @@ + + @ChildContent + + + + +@code { + [Parameter] public RenderFragment? ChildContent { get; set; } + [Parameter] public string? Class { get; set; } = "p-label"; + [Parameter] public string? Style { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/src/Aspects.Components/Wizard/WizardExample.razor b/src/Aspects.Components/Wizard/WizardExample.razor new file mode 100644 index 0000000..b30d994 --- /dev/null +++ b/src/Aspects.Components/Wizard/WizardExample.razor @@ -0,0 +1,20 @@ +@page "/wizard-example" + +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Web + +Your wizard of steps + + + +

Put your step 1 component here. Feel free to bind.

+
+ +

Put your step 2 component here. Feel free to bind.

+
+
+ +@code { + private WizardLayout? wizardLayout; + +} \ No newline at end of file diff --git a/src/Aspects.Components/Wizard/WizardLayout.razor b/src/Aspects.Components/Wizard/WizardLayout.razor new file mode 100644 index 0000000..c135b49 --- /dev/null +++ b/src/Aspects.Components/Wizard/WizardLayout.razor @@ -0,0 +1,101 @@ +@using System.Collections.ObjectModel +@using Cannery.Aspects.Components.Icons + + +
+ @if (ShowBreadcrumbs) + { + + } + + @if (Steps.Count > 0 && CurrentStepIndex < Steps.Count) + { + @Steps[CurrentStepIndex].ChildContent + } + else + { + @ChildContent + } + +
+ + +
+
+
+ +@code { + [Parameter] public required RenderFragment ChildContent { get; set; } + [Parameter] public bool ShowBreadcrumbs { get; set; } = true; + + internal List Steps { get; set; } = new(); + internal int CurrentStepIndex { get; set; } = 0; + + public bool IsFirstStep => CurrentStepIndex == 0; + public bool IsLastStep => CurrentStepIndex >= Steps.Count - 1; + + public void RegisterStep(WizardStep step) + { + if (!Steps.Contains(step)) + { + Steps.Add(step); + StateHasChanged(); + } + } + + public void Next() + { + if (!IsLastStep) + { + CurrentStepIndex++; + StateHasChanged(); + } + } + + public void Back() + { + if (!IsFirstStep) + { + CurrentStepIndex--; + StateHasChanged(); + } + } + + public void GoToStep(int index) + { + if (index >= 0 && index < Steps.Count) + { + CurrentStepIndex = index; + StateHasChanged(); + } + } + + public bool IsActiveStep(WizardStep step) => Steps.IndexOf(step) == CurrentStepIndex; + + public WizardContext Context => new(this); + + public class WizardContext + { + private readonly WizardLayout _layout; + public WizardContext(WizardLayout layout) => _layout = layout; + public void RegisterStep(WizardStep step) => _layout.RegisterStep(step); + public void Next() => _layout.Next(); + public void Back() => _layout.Back(); + public int CurrentStep => _layout.CurrentStepIndex; + public IReadOnlyList Steps => _layout.Steps.AsReadOnly(); + public void GoToStep(int index) => _layout.GoToStep(index); + } +} \ No newline at end of file diff --git a/src/Aspects.Components/Wizard/WizardStep.razor b/src/Aspects.Components/Wizard/WizardStep.razor new file mode 100644 index 0000000..82dea89 --- /dev/null +++ b/src/Aspects.Components/Wizard/WizardStep.razor @@ -0,0 +1,27 @@ +@inherits ComponentBase + +@if (IsActiveStep()) +{ +
+ @ChildContent +
+} + +@code { + [CascadingParameter] public required WizardLayout.WizardContext Wizard { get; set; } + [Parameter] public required string Title { get; set; } + [Parameter] public required RenderFragment ChildContent { get; set; } + + protected override void OnInitialized() + { + Wizard?.RegisterStep(this); + } + + private bool IsActiveStep() + { + if (Wizard?.Steps == null) + return false; + int thisIndex = Wizard.Steps.ToList().IndexOf(this); + return Wizard.CurrentStep == thisIndex; + } +} \ No newline at end of file diff --git a/src/Aspects.Components/_Imports.razor b/src/Aspects.Components/_Imports.razor new file mode 100644 index 0000000..b76d949 --- /dev/null +++ b/src/Aspects.Components/_Imports.razor @@ -0,0 +1,6 @@ +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.FluentUI.AspNetCore.Components +@using Icons = Microsoft.FluentUI.AspNetCore.Components.Icons +@using System.ComponentModel.DataAnnotations \ No newline at end of file diff --git a/src/Common.Assertion/AssertionFailedException.cs b/src/Common.Assertion/AssertionFailedException.cs deleted file mode 100644 index 094540a..0000000 --- a/src/Common.Assertion/AssertionFailedException.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Goodtocode.Assertion; - -public class AssertionFailedException(string message) : Exception(message) -{ -} - - diff --git a/src/Common.Assertion/AssertionRules.cs b/src/Common.Assertion/AssertionRules.cs deleted file mode 100644 index 0d9c7bd..0000000 --- a/src/Common.Assertion/AssertionRules.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace Goodtocode.Assertion; - -public static class AssertionRules -{ - public static void Should(this T? obj, string message = "Expected value to be not null") - { - if (obj is null) throw new AssertionFailedException(message); - } - - public static void ShouldNot(this T? obj, string message = "Expected value to be null") - { - if (obj is not null) throw new AssertionFailedException(message); - } - - public static void ShouldBe(this T actual, T expected, string? message = null) - { - if (!EqualityComparer.Default.Equals(actual, expected)) - { - throw new AssertionFailedException(message ?? $"Expected value to be '{expected}', but was '{actual}'."); - } - } - - public static void ShouldBeTrue(this bool condition, string message = "Expected condition to be true") - { - if (!condition) throw new AssertionFailedException(message); - } - - public static void ShouldBeFalse(this bool condition, string message = "Expected condition to be false") - { - if (condition) throw new AssertionFailedException(message); - } - - public static void ShouldBeNull(this T? obj, string message = "Expected value to be null") - { - if (obj is not null) throw new AssertionFailedException(message); - } - - public static void ShouldNotBeNull(this T? obj, string message = "Expected value to be not null") - { - if (obj is null) throw new AssertionFailedException(message); - } - - public static void ShouldBeEmpty(this T value, string message = "Expected value to be empty") where T : struct - { - if (!EqualityComparer.Default.Equals(value, default)) - throw new AssertionFailedException(message); - } - - public static void ShouldNotBeEmpty(this T value, string message = "Expected value to not be empty") where T : struct - { - if (EqualityComparer.Default.Equals(value, default)) - throw new AssertionFailedException(message); - } -} - diff --git a/src/Common.Assertion/AssertionScope.cs b/src/Common.Assertion/AssertionScope.cs deleted file mode 100644 index 42f2384..0000000 --- a/src/Common.Assertion/AssertionScope.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Goodtocode.Assertion; - -public class AssertionScope : IDisposable -{ - private static readonly AsyncLocal>> _scopes = new(); - - public AssertionScope() - { - if (_scopes.Value == null) - _scopes.Value = new Stack>(); - _scopes.Value.Push([]); - } - - public static void AddFailure(string message) - { - if (_scopes.Value != null && _scopes.Value.Count > 0) - _scopes.Value.Peek().Add(message); - } - - public void Dispose() - { - var failures = _scopes.Value?.Pop(); - if (failures != null && failures.Count > 0) - { - throw new AssertionFailedException(string.Join(Environment.NewLine, failures)); - } - GC.SuppressFinalize(this); - } - - public static bool IsActive => _scopes.Value != null && _scopes.Value.Count > 0; -} diff --git a/src/Common.Assertion/Common.Assertion.csproj b/src/Common.Assertion/Common.Assertion.csproj deleted file mode 100644 index 32b47f4..0000000 --- a/src/Common.Assertion/Common.Assertion.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - Goodtocode.Assertion - Goodtocode.Assertion - 1.0.0 - net9.0 - false - enable - enable - - - \ No newline at end of file diff --git a/src/Common.Domain.Types/Common.Domain.Types.csproj b/src/Common.Domain.Types/Common.Domain.Types.csproj deleted file mode 100644 index b31665c..0000000 --- a/src/Common.Domain.Types/Common.Domain.Types.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - Goodtocode.Domain.Types - Goodtocode.Domain.Types - 1.0.0 - net9.0 - enable - enable - - - diff --git a/src/Common.Domain.Types/DomainEntity/DomainEntity.cs b/src/Common.Domain.Types/DomainEntity/DomainEntity.cs deleted file mode 100644 index e4e0dc1..0000000 --- a/src/Common.Domain.Types/DomainEntity/DomainEntity.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.Runtime.Serialization; - -namespace Goodtocode.Domain.Types.DomainEntity; - -public abstract class DomainEntity : IDomainEntity -{ - private readonly List> _domainEvents = []; - - [Key] - public Guid Id { get; set; } - public string PartitionKey { get; set; } = string.Empty; - public DateTime CreatedOn { get; set; } = DateTime.UtcNow; - public DateTime? ModifiedOn { get; set; } - public DateTime? DeletedOn { get; set; } - public DateTimeOffset Timestamp { get; set; } = DateTimeOffset.UtcNow; - - [IgnoreDataMember] - public IReadOnlyList> DomainEvents => _domainEvents; - - protected DomainEntity() - { - } - - protected DomainEntity(Guid id) - : this() - { - Id = id; - } - - public void AddDomainEvent(IDomainEvent domainEvent) - { - _domainEvents.Add(domainEvent); - } - - public void ClearDomainEvents() - { - _domainEvents.Clear(); - } - - public override bool Equals(object? obj) - { - if (obj is not DomainEntity other) - return false; - - if (ReferenceEquals(this, other)) - return true; - - if (GetRealType() != other.GetRealType()) - return false; - - if (Id == Guid.Empty || other.Id == Guid.Empty) - return false; - - return Id == other.Id; - } - - public static bool operator ==(DomainEntity? a, DomainEntity? b) - { - if (a is null && b is null) - return true; - - if (a is null || b is null) - return false; - - return a.Equals(b); - } - - public static bool operator !=(DomainEntity? a, DomainEntity? b) - { - return !(a == b); - } - - public override int GetHashCode() - { - unchecked - { - int hash = 17; - hash = hash * 23 + GetRealType().ToString().GetHashCode(StringComparison.Ordinal); - hash = hash * 23 + Id.GetHashCode(); - return hash; - } - } - - - private Type GetRealType(string namespaceRoot = "") - { - var type = GetType(); - - if (type.ToString().Contains(namespaceRoot, StringComparison.InvariantCulture)) - return type.BaseType ?? type.GetType(); - - return type; - } -} \ No newline at end of file diff --git a/src/Common.Domain.Types/DomainEntity/IDomainEntity.cs b/src/Common.Domain.Types/DomainEntity/IDomainEntity.cs deleted file mode 100644 index 6901b81..0000000 --- a/src/Common.Domain.Types/DomainEntity/IDomainEntity.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Goodtocode.Domain.Types.DomainEntity; - -public interface IDomainEntity -{ - Guid Id { get; } - string PartitionKey { get; } - DateTime CreatedOn { get; } - DateTime? ModifiedOn { get; } - DateTime? DeletedOn { get; } - DateTimeOffset Timestamp { get; } - void AddDomainEvent(IDomainEvent domainEvent); - void ClearDomainEvents(); - bool Equals(object obj); - int GetHashCode(); -} \ No newline at end of file diff --git a/src/Common.Domain.Types/DomainEvent/DomainEventExample.cs b/src/Common.Domain.Types/DomainEvent/DomainEventExample.cs deleted file mode 100644 index 96e051b..0000000 --- a/src/Common.Domain.Types/DomainEvent/DomainEventExample.cs +++ /dev/null @@ -1,143 +0,0 @@ -//using MediatR; -//using System; -//using System.Collections.Generic; -//using System.Text; - -//namespace Goodtocode.Domain.Types.Domain -//{ -// public class OrderStartedDomainEvent : INotification -// { -// public string UserId { get; } -// public int CardTypeId { get; } -// public string CardNumber { get; } -// public string CardSecurityNumber { get; } -// public string CardHolderName { get; } -// public DateTime CardExpiration { get; } - -// public OrderStartedDomainEvent(int cardTypeId, string cardNumber, -// string cardSecurityNumber, string cardHolderName, -// DateTime cardExpiration) -// { -// CardTypeId = cardTypeId; -// CardNumber = cardNumber; -// CardSecurityNumber = cardSecurityNumber; -// CardHolderName = cardHolderName; -// CardExpiration = cardExpiration; -// } -// } - -// public class domainobject -// { -// public void example() -// { -// var orderStartedDomainEvent = new OrderStartedDomainEvent(this, //Order object -// cardTypeId, cardNumber, -// cardSecurityNumber, -// cardHolderName, -// cardExpiration); -// this.AddDomainEvent(orderStartedDomainEvent); -// } -// } - -// public class OrderingContext : DbContext, IUnitOfWork -// { -// ... -// public async Task SaveEntitiesAsync(CancellationToken cancellationToken = default(CancellationToken)) -// { -// Dispatch Domain Events collection. -// Choices: -// A) Right BEFORE committing data (EF SaveChanges) into the DB. This makes -// a single transaction including side effects from the domain event -// handlers that are using the same DbContext with Scope lifetime -// B) Right AFTER committing data (EF SaveChanges) into the DB. This makes -// multiple transactions. You will need to handle eventual consistency and -// compensatory actions in case of failures. -// await _mediator.DispatchDomainEventsAsync(this); - -// After this line runs, all the changes (from the Command Handler and Domain -// event handlers) performed through the DbContext will be committed -// var result = await base.SaveChangesAsync(); -// } -// } - -// public abstract class Entity -// { -// ... -// private List _domainEvents; -// public List DomainEvents => _domainEvents; - -// public void AddDomainEvent(INotification eventItem) -// { -// _domainEvents = _domainEvents ?? new List(); -// _domainEvents.Add(eventItem); -// } - -// public void RemoveDomainEvent(INotification eventItem) -// { -// _domainEvents?.Remove(eventItem); -// } -// ... Additional code -// } - - -// IoC Dispatcher Example - -// public class MediatorModule : Autofac.Module -// { -// protected override void Load(ContainerBuilder builder) -// { -// // Other registrations ... -// // Register the DomainEventHandler classes (they implement IAsyncNotificationHandler<>) -// // in assembly holding the Domain Events -// builder.RegisterAssemblyTypes(typeof(ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler) -// .GetTypeInfo().Assembly) -// .AsClosedTypesOf(typeof(IAsyncNotificationHandler<>)); -// // Other registrations ... -// } -// } - -// public class ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler -// : INotificationHandler -// { -// private readonly ILoggerFactory _logger; -// private readonly IBuyerRepository _buyerRepository; -// private readonly IIdentityService _identityService; - -// public ValidateOrAddBuyerAggregateWhenOrderStartedDomainEventHandler( -// ILoggerFactory logger, -// IBuyerRepository buyerRepository, -// IIdentityService identityService) -// { -// // ...Parameter validations... -// } - -// public async Task Handle(OrderStartedDomainEvent orderStartedEvent) -// { -// var cardTypeId = (orderStartedEvent.CardTypeId != 0) ? orderStartedEvent.CardTypeId : 1; -// var userGuid = _identityService.GetUserIdentity(); -// var buyer = await _buyerRepository.FindAsync(userGuid); -// bool buyerOriginallyExisted = (buyer == null) ? false : true; - -// if (!buyerOriginallyExisted) -// { -// buyer = new Buyer(userGuid); -// } - -// buyer.VerifyOrAddPaymentMethod(cardTypeId, -// $"Payment Method on {DateTime.UtcNow}", -// orderStartedEvent.CardNumber, -// orderStartedEvent.CardSecurityNumber, -// orderStartedEvent.CardHolderName, -// orderStartedEvent.CardExpiration, -// orderStartedEvent.Order.Id); - -// var buyerUpdated = buyerOriginallyExisted ? _buyerRepository.Update(buyer) -// : _buyerRepository.Add(buyer); - -// await _buyerRepository.UnitOfWork -// .SaveEntitiesAsync(); - -// // Logging code using buyerUpdated info, etc. -// } -// } -//} diff --git a/src/Common.Domain.Types/DomainEvent/IDomainEvent.cs b/src/Common.Domain.Types/DomainEvent/IDomainEvent.cs deleted file mode 100644 index e47187e..0000000 --- a/src/Common.Domain.Types/DomainEvent/IDomainEvent.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Goodtocode.Domain.Types; - -public interface IDomainEvent -{ - T Item { get; } -} \ No newline at end of file diff --git a/src/Common.HttpClient.ClientCredentialFlow/Common.HttpClient.ClientCredentialFlow.csproj b/src/Common.HttpClient.ClientCredentialFlow/Common.HttpClient.ClientCredentialFlow.csproj deleted file mode 100644 index 4ab7317..0000000 --- a/src/Common.HttpClient.ClientCredentialFlow/Common.HttpClient.ClientCredentialFlow.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - Goodtocode.HttpClient.ClientCredentialFlow - Goodtocode.HttpClient.ClientCredentialFlow - 1.0.0 - net9.0 - false - enable - enable - - - - - - - - - - diff --git a/src/Common.HttpClient.ClientCredentialFlow/ConfigureServices.cs b/src/Common.HttpClient.ClientCredentialFlow/ConfigureServices.cs deleted file mode 100644 index 680883a..0000000 --- a/src/Common.HttpClient.ClientCredentialFlow/ConfigureServices.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Goodtocode.HttpClient.ClientCredentialFlow.Middleware; -using Goodtocode.HttpClient.ClientCredentialFlow.Options; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace Goodtocode.HttpClient.ClientCredentialFlow; - -public static class ConfigureServices -{ - public static IServiceCollection AddHttpClientJitterService(this IServiceCollection services, - IConfiguration configuration, - string clientName, - Uri baseAddress, - int maxRetry = 5) - { - // Add strongly-typed and validated options for downstream use via DI. - services.AddOptions() - .Bind(configuration.GetSection(nameof(ClientCredential))) - .ValidateDataAnnotations() - .ValidateOnStart(); - services.AddSingleton(); - services.AddTransient(); - - services.AddHttpClient(clientName, options => - { - options.DefaultRequestHeaders.Clear(); - options.BaseAddress = baseAddress; - }) - .AddHttpMessageHandler() - .AddStandardResilienceHandler(options => - { - options.Retry.UseJitter = true; - options.Retry.MaxRetryAttempts = maxRetry; - }); - - return services; - } -} \ No newline at end of file diff --git a/src/Common.HttpClient.ClientCredentialFlow/Middleware/BearerToken.cs b/src/Common.HttpClient.ClientCredentialFlow/Middleware/BearerToken.cs deleted file mode 100644 index b1a414f..0000000 --- a/src/Common.HttpClient.ClientCredentialFlow/Middleware/BearerToken.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using Goodtocode.HttpClient.ClientCredentialFlow.Options; -using Microsoft.Extensions.Options; - -namespace Goodtocode.HttpClient.ClientCredentialFlow.Middleware; - -public class BearerToken(IOptions accessTokenSetting) -{ - private readonly IOptions _accessTokenSetting = accessTokenSetting; - - private string Token { get; set; } = string.Empty; - - private DateTime ExpirationDateUtc { get; set; } - - private bool TokenIsExpired - { - get - { - if (string.IsNullOrWhiteSpace(Token)) - return true; - - var timeDif = ExpirationDateUtc - DateTime.UtcNow; - var minutesUntilExpiration = timeDif.TotalMinutes; - return minutesUntilExpiration < 1; - } - } - - public async Task GetAccessTokenAsync() - { - if (TokenIsExpired) - Token = await GetNewAccessToken(); - - return Token; - } - - private async Task GetNewAccessToken() - { - var httpClient = new System.Net.Http.HttpClient(); - var content = new FormUrlEncodedContent(new Dictionary - { - { "scope", _accessTokenSetting.Value.Scope }, - { "client_id", _accessTokenSetting.Value.ClientId }, - { "client_secret", _accessTokenSetting.Value.ClientSecret }, - { "grant_type", "client_credentials" } - }); - - var response = await httpClient.PostAsync(_accessTokenSetting.Value.TokenUrl, content); - if (!response.IsSuccessStatusCode) return string.Empty; - var tokenResponse = JsonSerializer.Deserialize(await response.Content.ReadAsStringAsync()); - ExpirationDateUtc = DateTime.UtcNow.AddSeconds(tokenResponse?.ExpiresIn ?? 3600); - return tokenResponse?.AccessToken ?? string.Empty; - } - - private class BearerTokenDto - { - [JsonPropertyName("token_type")] public string? TokenType { get; set; } - - [JsonPropertyName("expires_in")] public int ExpiresIn { get; set; } - - [JsonPropertyName("ext_expires_in")] public int ExtExpiresIn { get; set; } - - [JsonPropertyName("access_token")] public string? AccessToken { get; set; } - } -} \ No newline at end of file diff --git a/src/Common.HttpClient.ClientCredentialFlow/Middleware/TokenHandler.cs b/src/Common.HttpClient.ClientCredentialFlow/Middleware/TokenHandler.cs deleted file mode 100644 index 1b90ccf..0000000 --- a/src/Common.HttpClient.ClientCredentialFlow/Middleware/TokenHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Net.Http.Headers; - -namespace Goodtocode.HttpClient.ClientCredentialFlow.Middleware; - -public class TokenHandler : DelegatingHandler -{ - private readonly BearerToken _accessToken; - - public TokenHandler(BearerToken bearerToken) - { - _accessToken = bearerToken; - } - - protected override async Task SendAsync(HttpRequestMessage request, - CancellationToken cancellationToken) - { - var token = await _accessToken.GetAccessTokenAsync(); - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); - return await base.SendAsync(request, cancellationToken); - } -} \ No newline at end of file diff --git a/src/Common.HttpClient.ClientCredentialFlow/Options/ClientCredential.cs b/src/Common.HttpClient.ClientCredentialFlow/Options/ClientCredential.cs deleted file mode 100644 index 32a13ab..0000000 --- a/src/Common.HttpClient.ClientCredentialFlow/Options/ClientCredential.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Goodtocode.HttpClient.ClientCredentialFlow.Options; - -public class ClientCredential -{ - public ClientCredential(string clientId, string clientSecret, string tokenUrl, string scope) - { - ClientId = clientId; - ClientSecret = clientSecret; - TokenUrl = tokenUrl; - Scope = scope; - } - - public string ClientId { get; } - public string ClientSecret { get; } - public string TokenUrl { get; } - public string Scope { get; } -} diff --git a/src/Common.Mediator/Common.Mediator.csproj b/src/Common.Mediator/Common.Mediator.csproj deleted file mode 100644 index 56c6b43..0000000 --- a/src/Common.Mediator/Common.Mediator.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - Goodtocode.Mediator - Goodtocode.Mediator - 1.0.0 - net9.0 - false - enable - enable - - - - - - - \ No newline at end of file diff --git a/src/Common.Mediator/IPipelineBehavior.cs b/src/Common.Mediator/IPipelineBehavior.cs deleted file mode 100644 index 41d05dc..0000000 --- a/src/Common.Mediator/IPipelineBehavior.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Goodtocode.Mediator; - -public delegate Task RequestDelegateInvoker(); -public interface IPipelineBehavior where TRequest : notnull -{ - Task Handle(TRequest request, RequestDelegateInvoker nextInvoker, CancellationToken cancellationToken); -} - -public delegate Task RequestDelegateInvoker(); -public interface IPipelineBehavior where TRequest : notnull -{ - Task Handle(TRequest request, RequestDelegateInvoker nextInvoker, CancellationToken cancellationToken); -} - diff --git a/src/Common.Mediator/IRequest.cs b/src/Common.Mediator/IRequest.cs deleted file mode 100644 index 1900d38..0000000 --- a/src/Common.Mediator/IRequest.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Goodtocode.Mediator; - -public interface IRequest { } - -public interface IRequest { } diff --git a/src/Common.Mediator/IRequestDispatcher.cs b/src/Common.Mediator/IRequestDispatcher.cs deleted file mode 100644 index b46ca87..0000000 --- a/src/Common.Mediator/IRequestDispatcher.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Goodtocode.Mediator; - -public interface IRequestDispatcher -{ - Task Send(IRequest request, CancellationToken cancellationToken = default); - Task Send(IRequest request, CancellationToken cancellationToken = default); -} diff --git a/src/Common.Mediator/IRequestHandler.cs b/src/Common.Mediator/IRequestHandler.cs deleted file mode 100644 index 6de4304..0000000 --- a/src/Common.Mediator/IRequestHandler.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Goodtocode.Mediator; - -public interface IRequestHandler - where TRequest : IRequest -{ - Task Handle(TRequest request, CancellationToken cancellationToken); -} - -public interface IRequestHandler - where TRequest : IRequest -{ - Task Handle(TRequest request, CancellationToken cancellationToken); -} \ No newline at end of file diff --git a/src/Common.Mediator/IRequestPreProcessor.cs b/src/Common.Mediator/IRequestPreProcessor.cs deleted file mode 100644 index cc8a0ad..0000000 --- a/src/Common.Mediator/IRequestPreProcessor.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Goodtocode.Mediator; - -public interface IRequestPreProcessor where TRequest : notnull -{ - Task Process(TRequest request, CancellationToken cancellationToken); -} diff --git a/src/Common.Mediator/ISender.cs b/src/Common.Mediator/ISender.cs deleted file mode 100644 index 673b343..0000000 --- a/src/Common.Mediator/ISender.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Goodtocode.Mediator; - -public interface ISender -{ - Task Send(IRequest request, CancellationToken cancellationToken = default); - Task Send(IRequest request, CancellationToken cancellationToken = default); -} \ No newline at end of file diff --git a/src/Common.Mediator/RequestDispatcher.cs b/src/Common.Mediator/RequestDispatcher.cs deleted file mode 100644 index 29bb7cb..0000000 --- a/src/Common.Mediator/RequestDispatcher.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace Goodtocode.Mediator; - - -public class RequestDispatcher(IServiceProvider serviceProvider) : IRequestDispatcher -{ - public async Task Send(IRequest request, CancellationToken cancellationToken = default) - { - var requestType = request.GetType(); - var handlerType = typeof(IRequestHandler<>).MakeGenericType(requestType); - var handler = serviceProvider.GetRequiredService(handlerType); - - var behaviorType = typeof(IPipelineBehavior<>).MakeGenericType(requestType); - var behaviors = serviceProvider.GetServices(behaviorType)?.ToList() ?? []; - - RequestDelegateInvoker handlerDelegate = () => - ((dynamic)handler).Handle((dynamic)request, cancellationToken); - - foreach (var behavior in behaviors.AsEnumerable().Reverse()) - { - var next = handlerDelegate; - handlerDelegate = () => ((dynamic)behavior)?.Handle((dynamic)request, next, cancellationToken) - ?? throw new InvalidOperationException("Pipeline behavior is null."); - } - - await handlerDelegate(); - } - - public async Task Send(IRequest request, CancellationToken cancellationToken = default) - { - var requestType = request.GetType(); - var handlerType = typeof(IRequestHandler<,>).MakeGenericType(requestType, typeof(TResponse)); - var handler = serviceProvider.GetRequiredService(handlerType); - - var behaviorType = typeof(IPipelineBehavior<,>).MakeGenericType(requestType, typeof(TResponse)); - var behaviors = serviceProvider.GetServices(behaviorType)?.ToList() ?? []; - - RequestDelegateInvoker handlerDelegate = () => - ((dynamic)handler).Handle((dynamic)request, cancellationToken); - - foreach (var behavior in behaviors.AsEnumerable().Reverse()) - { - var next = handlerDelegate; - handlerDelegate = () => ((dynamic)behavior)?.Handle((dynamic)request, next, cancellationToken) - ?? throw new InvalidOperationException("Pipeline behavior is null."); - } - - return await handlerDelegate(); - } -} diff --git a/src/Common.Mediator/Sender.cs b/src/Common.Mediator/Sender.cs deleted file mode 100644 index f1aba50..0000000 --- a/src/Common.Mediator/Sender.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Goodtocode.Mediator; - -public class Sender(IRequestDispatcher dispatcher) : ISender -{ - public Task Send(IRequest request, CancellationToken cancellationToken = default) - { - return dispatcher.Send(request, cancellationToken); - } - - public Task Send(IRequest request, CancellationToken cancellationToken = default) - { - return dispatcher.Send(request, cancellationToken); - } -} \ No newline at end of file diff --git a/src/Common.Validation/Common.Validation.csproj b/src/Common.Validation/Common.Validation.csproj deleted file mode 100644 index 81c5562..0000000 --- a/src/Common.Validation/Common.Validation.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - Goodtocode.Validation - Goodtocode.Validation - 1.0.0 - net9.0 - false - enable - enable - - - \ No newline at end of file diff --git a/src/Common.Validation/CustomValidationException.cs b/src/Common.Validation/CustomValidationException.cs deleted file mode 100644 index ccd8ffa..0000000 --- a/src/Common.Validation/CustomValidationException.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Goodtocode.Validation; - -public class CustomValidationException : Exception -{ - public CustomValidationException() - : base("One or more validation failures have occurred.") - { - Errors = new Dictionary(); - } - - public CustomValidationException(IEnumerable failures) - : this() - { - Errors = failures - .GroupBy(e => e.PropertyName, e => e.ErrorMessage) - .ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray()); - } - - public IDictionary Errors { get; } -} \ No newline at end of file diff --git a/src/Common.Validation/IRuleBuilder.cs b/src/Common.Validation/IRuleBuilder.cs deleted file mode 100644 index 6fe1cb1..0000000 --- a/src/Common.Validation/IRuleBuilder.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Goodtocode.Validation; - -public interface IRuleBuilder -{ - IRuleBuilder NotEmpty(string? errorMessage = null); - IRuleBuilder LessThanOrEqualTo(Func other, string? errorMessage = null); - IRuleBuilder GreaterThanOrEqualTo(Func other, string? errorMessage = null); - IRuleBuilder When(Func predicate); -} diff --git a/src/Common.Validation/IValidator.cs b/src/Common.Validation/IValidator.cs deleted file mode 100644 index 738842f..0000000 --- a/src/Common.Validation/IValidator.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Goodtocode.Validation; - -public interface IValidator -{ - void ValidateAndThrow(T instance); -} \ No newline at end of file diff --git a/src/Common.Validation/RuleBuilder.cs b/src/Common.Validation/RuleBuilder.cs deleted file mode 100644 index 7cf1c1c..0000000 --- a/src/Common.Validation/RuleBuilder.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System.Globalization; -using System.Linq.Expressions; - -namespace Goodtocode.Validation; - -public class RuleBuilder -{ - private readonly Expression> _selector; - private readonly string _propertyName; - private readonly List> _rules; - private Func? _condition; - - public RuleBuilder(Expression> selector, string propertyName, List> rules) - { - _selector = selector; - _propertyName = propertyName; - _rules = rules; - } - - public RuleBuilder NotEmpty(string? errorMessage = null) - { - _rules.Add(instance => - { - if (_condition != null && !_condition(instance)) return null; - - var value = _selector.Compile()(instance); - - bool isValid = value switch - { - null => false, - Guid guid => guid != Guid.Empty, - string str => !string.IsNullOrWhiteSpace(str), - int i => i != 0, - long l => l != 0L, - double d => d != 0.0, - decimal m => m != 0m, - DateTime dt => dt != DateTime.MinValue, - Enum e => Convert.ToInt32(e, CultureInfo.InvariantCulture) != 0, - _ => true - }; - - return isValid ? null : new ValidationFailure(_propertyName, errorMessage ?? $"{_propertyName} must not be empty"); - }); - return this; - } - - public RuleBuilder NotEqual(TProp other, string? errorMessage = null) - { - _rules.Add(instance => - { - if (_condition != null && !_condition(instance)) return null; - - var value = _selector.Compile()(instance); - var comparison = Comparer.Default.Compare(value, other); - return comparison != 0 ? null : new ValidationFailure(_propertyName, errorMessage ?? $"{_propertyName} must not be equal to {other}"); - }); - return this; - } - - public RuleBuilder Equal(TProp other, string? errorMessage = null) - { - _rules.Add(instance => - { - if (_condition != null && !_condition(instance)) return null; - - var value = _selector.Compile()(instance); - var comparison = Comparer.Default.Compare(value, other); - return comparison == 0 ? null : new ValidationFailure(_propertyName, errorMessage ?? $"{_propertyName} must not be equal to {other}"); - }); - return this; - } - - public RuleBuilder LessThanOrEqualTo(Func otherSelector, string? errorMessage = null) - { - _rules.Add(instance => - { - if (_condition != null && !_condition(instance)) return null; - - var value = _selector.Compile()(instance); - var other = otherSelector(instance); - var comparison = Comparer.Default.Compare(value, other); - return comparison <= 0 ? null : new ValidationFailure(_propertyName, errorMessage ?? $"{_propertyName} must be ≤ {other}"); - }); - return this; - } - - public RuleBuilder GreaterThanOrEqualTo(Func otherSelector, string? errorMessage = null) - { - _rules.Add(instance => - { - if (_condition != null && !_condition(instance)) return null; - - var value = _selector.Compile()(instance); - var other = otherSelector(instance); - var comparison = Comparer.Default.Compare(value, other); - return comparison >= 0 ? null : new ValidationFailure(_propertyName, errorMessage ?? $"{_propertyName} must be ≥ {other}"); - }); - return this; - } - - public RuleBuilder When(Func condition) - { - _condition = condition; - return this; - } -} diff --git a/src/Common.Validation/ValidationFailure.cs b/src/Common.Validation/ValidationFailure.cs deleted file mode 100644 index ce3d710..0000000 --- a/src/Common.Validation/ValidationFailure.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Goodtocode.Validation; - -public class ValidationFailure(string propertyName, string errorMessage) -{ - public string PropertyName { get; init; } = propertyName; - public string ErrorMessage { get; init; } = errorMessage; -} diff --git a/src/Common.Validation/ValidationResult.cs b/src/Common.Validation/ValidationResult.cs deleted file mode 100644 index 32eb8d0..0000000 --- a/src/Common.Validation/ValidationResult.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Goodtocode.Validation; - -public class ValidationResult -{ - public ValidationResult() : this([]) - { - } - - public ValidationResult(IEnumerable? failures) - { - Errors = [.. (failures ?? [])]; - } - - - public IReadOnlyList Errors { get; } - - public bool IsValid => Errors.Count == 0; -} diff --git a/src/Common.Validation/ValidationRule.cs b/src/Common.Validation/ValidationRule.cs deleted file mode 100644 index e78d76c..0000000 --- a/src/Common.Validation/ValidationRule.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Goodtocode.Validation; - -public class ValidationRule -{ - public Func Condition { get; set; } = _ => true; - public required Func ErrorMessage { get; set; } - public required Func IsValid { get; set; } -} diff --git a/src/Common.Validation/Validator.cs b/src/Common.Validation/Validator.cs deleted file mode 100644 index b6ac6b4..0000000 --- a/src/Common.Validation/Validator.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Linq.Expressions; - -namespace Goodtocode.Validation; - -public abstract class Validator : IValidator -{ - private readonly List> _rules = []; - - protected RuleBuilder RuleFor( - Expression> propertySelector) - { - var propertyName = GetPropertyName(propertySelector); - return new RuleBuilder(propertySelector, propertyName, _rules); - } - - public ValidationResult Validate(T instance) - { - var failures = _rules - .Select(rule => rule(instance)) - .Where(f => f != null) - .Cast() - .ToList(); - - return new ValidationResult(failures); - } - - public async Task ValidateAsync(T instance, CancellationToken cancellationToken = default) - { - return await Task.Run(() => Validate(instance), cancellationToken); - } - - public void ValidateAndThrow(T instance) - { - var result = Validate(instance); - if (!result.IsValid) - throw new CustomValidationException(result.Errors!); - } - - public async Task ValidateAndThrowAsync(T instance, CancellationToken cancellationToken = default) - { - var result = await ValidateAsync(instance, cancellationToken); - if (!result.IsValid) - throw new CustomValidationException(result.Errors!); - } - - private static string GetPropertyName(Expression> expression) - { - if (expression.Body is MemberExpression member) - return member.Member.Name; - throw new ArgumentException("Expression must be a property access.", nameof(expression)); - } -} \ No newline at end of file diff --git a/src/Core.Application/Abstractions/IAuthorResponse.cs b/src/Core.Application/Abstractions/IActorResponse.cs similarity index 72% rename from src/Core.Application/Abstractions/IAuthorResponse.cs rename to src/Core.Application/Abstractions/IActorResponse.cs index bd353cb..b0421de 100644 --- a/src/Core.Application/Abstractions/IAuthorResponse.cs +++ b/src/Core.Application/Abstractions/IActorResponse.cs @@ -1,8 +1,8 @@ namespace Goodtocode.SemanticKernel.Core.Application.Abstractions; -public interface IAuthorResponse +public interface IActorResponse { - Guid AuthorId { get; } + Guid ActorId { get; } string? Name { get; } string Status { get; } string? Message { get; } diff --git a/src/Core.Application/Abstractions/IActorsPlugin.cs b/src/Core.Application/Abstractions/IActorsPlugin.cs new file mode 100644 index 0000000..bf5c504 --- /dev/null +++ b/src/Core.Application/Abstractions/IActorsPlugin.cs @@ -0,0 +1,7 @@ +namespace Goodtocode.SemanticKernel.Core.Application.Abstractions; + +public interface IActorsPlugin : ISemanticPluginCompatible +{ + Task GetActorByIdAsync(Guid actorId, CancellationToken cancellationToken); + Task> GetActorsByNameAsync(string name, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/Core.Application/Abstractions/IAuthorsPlugin.cs b/src/Core.Application/Abstractions/IAuthorsPlugin.cs deleted file mode 100644 index fa747a0..0000000 --- a/src/Core.Application/Abstractions/IAuthorsPlugin.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Goodtocode.SemanticKernel.Core.Application.Abstractions; - -public interface IAuthorsPlugin : ISemanticPluginCompatible -{ - Task GetAuthorByIdAsync(Guid authorId, CancellationToken cancellationToken); - Task> GetAuthorsByNameAsync(string name, CancellationToken cancellationToken); -} \ No newline at end of file diff --git a/src/Core.Application/Abstractions/ISemanticKernelContext.cs b/src/Core.Application/Abstractions/ISemanticKernelContext.cs index d7862aa..98e1a25 100644 --- a/src/Core.Application/Abstractions/ISemanticKernelContext.cs +++ b/src/Core.Application/Abstractions/ISemanticKernelContext.cs @@ -1,20 +1,25 @@ -using Goodtocode.SemanticKernel.Core.Domain.Audio; -using Goodtocode.SemanticKernel.Core.Domain.Author; +using Goodtocode.SemanticKernel.Core.Domain.Actor; +using Goodtocode.SemanticKernel.Core.Domain.Audio; using Goodtocode.SemanticKernel.Core.Domain.ChatCompletion; using Goodtocode.SemanticKernel.Core.Domain.Image; using Goodtocode.SemanticKernel.Core.Domain.TextGeneration; +using Microsoft.EntityFrameworkCore.Metadata; namespace Goodtocode.SemanticKernel.Core.Application.Abstractions; public interface ISemanticKernelContext { - DbSet Authors { get; } DbSet ChatMessages { get; } - DbSet ChatSessions { get; } + DbSet ChatSessions {get; } DbSet TextPrompts { get; } DbSet TextResponses { get; } DbSet TextImages { get; } DbSet TextAudio { get; } + DbSet Actors { get; } Task SaveChangesAsync(CancellationToken cancellationToken = default); +#pragma warning disable CA1716 // Identifiers should not match keywords + DbSet Set() where TEntity : class; +#pragma warning restore CA1716 + IModel Model { get; } } \ No newline at end of file diff --git a/src/Core.Application/Abstractions/ISemanticPluginCompatible.cs b/src/Core.Application/Abstractions/ISemanticPluginCompatible.cs index 4ae7891..65327f1 100644 --- a/src/Core.Application/Abstractions/ISemanticPluginCompatible.cs +++ b/src/Core.Application/Abstractions/ISemanticPluginCompatible.cs @@ -5,5 +5,4 @@ public interface ISemanticPluginCompatible string PluginName { get; } string FunctionName { get; } Dictionary Parameters { get; } - } \ No newline at end of file diff --git a/src/Core.Application/Abstractions/IUserInfoRequest.cs b/src/Core.Application/Abstractions/IUserInfoRequest.cs new file mode 100644 index 0000000..e7de105 --- /dev/null +++ b/src/Core.Application/Abstractions/IUserInfoRequest.cs @@ -0,0 +1,16 @@ +using Goodtocode.SemanticKernel.Core.Domain.Auth; + +namespace Goodtocode.SemanticKernel.Core.Application.Abstractions; + +/// +/// Represents a request containing user information. +/// +/// This interface is used to encapsulate user information in a request. The property allows getting or setting the associated user details. +public interface IUserInfoRequest +{ + /// + /// Gets or sets the user information associated with the current context. + /// + IUserEntity? UserInfo { get; set; } +} diff --git a/src/Core.Application/Actor/ActorDto.cs b/src/Core.Application/Actor/ActorDto.cs new file mode 100644 index 0000000..e3e875e --- /dev/null +++ b/src/Core.Application/Actor/ActorDto.cs @@ -0,0 +1,34 @@ +using Goodtocode.SemanticKernel.Core.Domain.Actor; + +namespace Goodtocode.SemanticKernel.Core.Application.Actor; + +public class ActorDto +{ + public Guid Id { get; set; } = Guid.Empty; + public string Name { get; set; } = string.Empty; + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public Guid OwnerId { get; set; } + public Guid TenantId { get; set; } + public DateTime CreatedOn { get; private set; } = DateTime.UtcNow; + public DateTime? ModifiedOn { get; private set; } + public DateTime? DeletedOn { get; private set; } + + public static ActorDto CreateFrom(ActorEntity? entity) + { + if (entity is null) return null!; + return new ActorDto + { + Id = entity.Id, + FirstName = entity.FirstName ?? string.Empty, + LastName = entity.LastName ?? string.Empty, + Email = entity.Email ?? string.Empty, + OwnerId = entity.OwnerId, + TenantId = entity.TenantId, + CreatedOn = entity.CreatedOn, + ModifiedOn = entity.ModifiedOn, + DeletedOn = entity.DeletedOn + }; + } +} diff --git a/src/Core.Application/Actor/CreateActorCommand.cs b/src/Core.Application/Actor/CreateActorCommand.cs new file mode 100644 index 0000000..a2731fe --- /dev/null +++ b/src/Core.Application/Actor/CreateActorCommand.cs @@ -0,0 +1,57 @@ +using Goodtocode.SemanticKernel.Core.Application.Abstractions; +using Goodtocode.SemanticKernel.Core.Application.Common.Exceptions; +using Goodtocode.SemanticKernel.Core.Domain.Actor; + +namespace Goodtocode.SemanticKernel.Core.Application.Actor; + +public class CreateActorCommand : IRequest +{ + public Guid Id { get; set; } + public string? FirstName { get; set; } + public string? LastName { get; set; } + public string? Email { get; set; } + public Guid OwnerId { get; set; } + public Guid TenantId { get; set; } +} + +public class CreateAuthorCommandHandler(ISemanticKernelContext context) : IRequestHandler +{ + private readonly ISemanticKernelContext _context = context; + + public async Task Handle(CreateActorCommand request, CancellationToken cancellationToken) + { + GuardAgainstEmptyOwnerId(request?.OwnerId); + GuardAgainstIdExists(_context.Actors, request!.Id); + + var Actor = ActorEntity.Create(request!.Id == Guid.Empty ? Guid.NewGuid() : request!.Id, request.OwnerId, request.TenantId, request.FirstName, request.LastName, request.Email); + _context.Actors.Add(Actor); + try + { + await _context.SaveChangesAsync(cancellationToken); + } + catch (DbUpdateException) + { + throw new CustomValidationException( + [ + new("Id", "Id already exists") + ]); + } + + return ActorDto.CreateFrom(Actor); + } + + private static void GuardAgainstEmptyOwnerId(Guid? ownerId) + { + if (ownerId == Guid.Empty) + throw new CustomValidationException( + [ + new("OwnerId", "A OwnerId is required to link an actor with an account") + ]); + } + + private static void GuardAgainstIdExists(DbSet dbSet, Guid id) + { + if (dbSet.Any(x => x.Id == id)) + throw new CustomConflictException("Id already exists"); + } +} \ No newline at end of file diff --git a/src/Core.Application/Actor/CreateActorCommandValidator.cs b/src/Core.Application/Actor/CreateActorCommandValidator.cs new file mode 100644 index 0000000..7c36354 --- /dev/null +++ b/src/Core.Application/Actor/CreateActorCommandValidator.cs @@ -0,0 +1,9 @@ +namespace Goodtocode.SemanticKernel.Core.Application.Actor; + +public class CreateActorCommandValidator : Validator +{ + public CreateActorCommandValidator() + { + RuleFor(x => x.TenantId).NotEmpty(); + } +} \ No newline at end of file diff --git a/src/Core.Application/Actor/DeleteActorByExternalIdCommand.cs b/src/Core.Application/Actor/DeleteActorByExternalIdCommand.cs new file mode 100644 index 0000000..be26d99 --- /dev/null +++ b/src/Core.Application/Actor/DeleteActorByExternalIdCommand.cs @@ -0,0 +1,30 @@ +using Goodtocode.SemanticKernel.Core.Application.Abstractions; +using Goodtocode.SemanticKernel.Core.Application.Common.Exceptions; +using Goodtocode.SemanticKernel.Core.Domain.Actor; + +namespace Goodtocode.SemanticKernel.Core.Application.Actor; + +public class DeleteActorByOwnerIdCommand : IRequest +{ + public Guid OwnerId { get; set; } +} + +public class DeleteAuthorByOwnerIdCommandHandler(ISemanticKernelContext context) : IRequestHandler +{ + private readonly ISemanticKernelContext _context = context; + + public async Task Handle(DeleteActorByOwnerIdCommand request, CancellationToken cancellationToken) + { + var actor = await _context.Actors.Where(x => x.OwnerId == request.OwnerId).FirstOrDefaultAsync(cancellationToken); + GuardAgainstNotFound(actor); + + _context.Actors.Remove(actor!); + await _context.SaveChangesAsync(cancellationToken); + } + + private static void GuardAgainstNotFound(ActorEntity? Actor) + { + if (Actor == null) + throw new CustomNotFoundException("Actor Not Found"); + } +} \ No newline at end of file diff --git a/src/Core.Application/Actor/DeleteActorByExternalIdCommandValidator.cs b/src/Core.Application/Actor/DeleteActorByExternalIdCommandValidator.cs new file mode 100644 index 0000000..89634fc --- /dev/null +++ b/src/Core.Application/Actor/DeleteActorByExternalIdCommandValidator.cs @@ -0,0 +1,8 @@ +namespace Goodtocode.SemanticKernel.Core.Application.Actor; + +public class DeleteActorByOwnerIdCommandValidator : Validator +{ + public DeleteActorByOwnerIdCommandValidator() + { + } +} \ No newline at end of file diff --git a/src/Core.Application/Actor/DeleteActorCommand.cs b/src/Core.Application/Actor/DeleteActorCommand.cs new file mode 100644 index 0000000..8077913 --- /dev/null +++ b/src/Core.Application/Actor/DeleteActorCommand.cs @@ -0,0 +1,30 @@ +using Goodtocode.SemanticKernel.Core.Application.Abstractions; +using Goodtocode.SemanticKernel.Core.Application.Common.Exceptions; +using Goodtocode.SemanticKernel.Core.Domain.Actor; + +namespace Goodtocode.SemanticKernel.Core.Application.Actor; + +public class DeleteActorCommand : IRequest +{ + public Guid Id { get; set; } +} + +public class DeleteAuthorCommandHandler(ISemanticKernelContext context) : IRequestHandler +{ + private readonly ISemanticKernelContext _context = context; + + public async Task Handle(DeleteActorCommand request, CancellationToken cancellationToken) + { + var Actor = _context.Actors.Find(request.Id); + GuardAgainstNotFound(Actor); + + _context.Actors.Remove(Actor!); + await _context.SaveChangesAsync(cancellationToken); + } + + private static void GuardAgainstNotFound(ActorEntity? Actor) + { + if (Actor == null) + throw new CustomNotFoundException("Actor Not Found"); + } +} \ No newline at end of file diff --git a/src/Core.Application/Actor/DeleteActorCommandValidator.cs b/src/Core.Application/Actor/DeleteActorCommandValidator.cs new file mode 100644 index 0000000..6ce0628 --- /dev/null +++ b/src/Core.Application/Actor/DeleteActorCommandValidator.cs @@ -0,0 +1,9 @@ +namespace Goodtocode.SemanticKernel.Core.Application.Actor; + +public class DeleteActorCommandValidator : Validator +{ + public DeleteActorCommandValidator() + { + RuleFor(x => x.Id).NotEmpty(); + } +} \ No newline at end of file diff --git a/src/Core.Application/Author/GetAuthorChatSessionQuery.cs b/src/Core.Application/Actor/GetActorChatSessionQuery.cs similarity index 66% rename from src/Core.Application/Author/GetAuthorChatSessionQuery.cs rename to src/Core.Application/Actor/GetActorChatSessionQuery.cs index 7851942..f51b6a1 100644 --- a/src/Core.Application/Author/GetAuthorChatSessionQuery.cs +++ b/src/Core.Application/Actor/GetActorChatSessionQuery.cs @@ -3,22 +3,22 @@ using Goodtocode.SemanticKernel.Core.Application.Common.Exceptions; using Goodtocode.SemanticKernel.Core.Domain.ChatCompletion; -namespace Goodtocode.SemanticKernel.Core.Application.Author; +namespace Goodtocode.SemanticKernel.Core.Application.Actor; -public class GetAuthorChatSessionQuery : IRequest +public class GetActorChatSessionQuery : IRequest { - public Guid AuthorId { get; set; } + public Guid ActorId { get; set; } public Guid ChatSessionId { get; set; } } -public class GetAuthorChatSessionQueryHandler(ISemanticKernelContext context) : IRequestHandler +public class GetAuthorChatSessionQueryHandler(ISemanticKernelContext context) : IRequestHandler { private readonly ISemanticKernelContext _context = context; - public async Task Handle(GetAuthorChatSessionQuery request, CancellationToken cancellationToken) + public async Task Handle(GetActorChatSessionQuery request, CancellationToken cancellationToken) { var returnData = await _context.ChatSessions - .FirstOrDefaultAsync(x => x.Id == request.ChatSessionId && x.AuthorId == request.AuthorId, cancellationToken: cancellationToken); + .FirstOrDefaultAsync(x => x.Id == request.ChatSessionId && x.ActorId == request.ActorId, cancellationToken: cancellationToken); GuardAgainstNotFound(returnData); return ChatSessionDto.CreateFrom(returnData); diff --git a/src/Core.Application/Actor/GetActorChatSessionQueryValidator.cs b/src/Core.Application/Actor/GetActorChatSessionQueryValidator.cs new file mode 100644 index 0000000..452dbf5 --- /dev/null +++ b/src/Core.Application/Actor/GetActorChatSessionQueryValidator.cs @@ -0,0 +1,10 @@ +namespace Goodtocode.SemanticKernel.Core.Application.Actor; + +public class GetActorChatSessionQueryValidator : Validator +{ + public GetActorChatSessionQueryValidator() + { + RuleFor(x => x.ActorId).NotEmpty(); + RuleFor(x => x.ChatSessionId).NotEmpty(); + } +} \ No newline at end of file diff --git a/src/Core.Application/Author/GetAuthorChatSessionsPaginatedQuery.cs b/src/Core.Application/Actor/GetActorChatSessionsPaginatedQuery.cs similarity index 70% rename from src/Core.Application/Author/GetAuthorChatSessionsPaginatedQuery.cs rename to src/Core.Application/Actor/GetActorChatSessionsPaginatedQuery.cs index 691c5f8..5fdd263 100644 --- a/src/Core.Application/Author/GetAuthorChatSessionsPaginatedQuery.cs +++ b/src/Core.Application/Actor/GetActorChatSessionsPaginatedQuery.cs @@ -3,29 +3,29 @@ using Goodtocode.SemanticKernel.Core.Application.Common.Mappings; using Goodtocode.SemanticKernel.Core.Application.Common.Models; -namespace Goodtocode.SemanticKernel.Core.Application.Author; +namespace Goodtocode.SemanticKernel.Core.Application.Actor; -public class GetAuthorChatSessionsPaginatedQuery : IRequest> +public class GetActorChatSessionsPaginatedQuery : IRequest> { - public Guid AuthorId { get; set; } + public Guid ActorId { get; set; } public DateTime? StartDate { get; set; } public DateTime? EndDate { get; set; } public int PageNumber { get; init; } = 1; public int PageSize { get; init; } = 10; } -public class GetAuthorChatSessionsPaginatedQueryHandler(ISemanticKernelContext context) : IRequestHandler> +public class GetAuthorChatSessionsPaginatedQueryHandler(ISemanticKernelContext context) : IRequestHandler> { private readonly ISemanticKernelContext _context = context; - public async Task> Handle(GetAuthorChatSessionsPaginatedQuery request, CancellationToken cancellationToken) + public async Task> Handle(GetActorChatSessionsPaginatedQuery request, CancellationToken cancellationToken) { var returnData = await _context.ChatSessions .Include(x => x.Messages) .OrderByDescending(x => x.Timestamp) .Where(x => (request.StartDate == null || x.Timestamp > request.StartDate) && (request.EndDate == null || x.Timestamp < request.EndDate)) - .Select(x => ChatSessionDto.CreateFrom(x)) + .Select(x => ChatSessionDto.CreateFrom(x)) .PaginatedListAsync(request.PageNumber, request.PageSize); return returnData; diff --git a/src/Core.Application/Author/GetAuthorChatSessionsPaginatedQueryValidator.cs b/src/Core.Application/Actor/GetActorChatSessionsPaginatedQueryValidator.cs similarity index 59% rename from src/Core.Application/Author/GetAuthorChatSessionsPaginatedQueryValidator.cs rename to src/Core.Application/Actor/GetActorChatSessionsPaginatedQueryValidator.cs index 0c1ee58..f42226f 100644 --- a/src/Core.Application/Author/GetAuthorChatSessionsPaginatedQueryValidator.cs +++ b/src/Core.Application/Actor/GetActorChatSessionsPaginatedQueryValidator.cs @@ -1,10 +1,10 @@ -namespace Goodtocode.SemanticKernel.Core.Application.Author; +namespace Goodtocode.SemanticKernel.Core.Application.Actor; -public class GetAuthorChatSessionsPaginatedQueryValidator : Validator +public class GetActorChatSessionsPaginatedQueryValidator : Validator { - public GetAuthorChatSessionsPaginatedQueryValidator() + public GetActorChatSessionsPaginatedQueryValidator() { - RuleFor(x => x.AuthorId).NotEmpty(); + RuleFor(x => x.ActorId).NotEmpty(); RuleFor(v => v.StartDate).NotEmpty() .When(v => v.EndDate != null) diff --git a/src/Core.Application/Author/GetAuthorChatSessionsQuery.cs b/src/Core.Application/Actor/GetActorChatSessionsQuery.cs similarity index 62% rename from src/Core.Application/Author/GetAuthorChatSessionsQuery.cs rename to src/Core.Application/Actor/GetActorChatSessionsQuery.cs index 6a0ca55..4dd6562 100644 --- a/src/Core.Application/Author/GetAuthorChatSessionsQuery.cs +++ b/src/Core.Application/Actor/GetActorChatSessionsQuery.cs @@ -1,24 +1,24 @@ using Goodtocode.SemanticKernel.Core.Application.Abstractions; using Goodtocode.SemanticKernel.Core.Application.ChatCompletion; -namespace Goodtocode.SemanticKernel.Core.Application.Author; +namespace Goodtocode.SemanticKernel.Core.Application.Actor; -public class GetAuthorChatSessionsQuery : IRequest> +public class GetActorChatSessionsQuery : IRequest> { - public Guid AuthorId { get; set; } + public Guid ActorId { get; set; } public DateTime? StartDate { get; set; } public DateTime? EndDate { get; set; } } -public class GetAuthorChatSessionsQueryHandler(ISemanticKernelContext context) : IRequestHandler> +public class GetAuthorChatSessionsQueryHandler(ISemanticKernelContext context) : IRequestHandler> { private readonly ISemanticKernelContext _context = context; - public async Task> Handle(GetAuthorChatSessionsQuery request, CancellationToken cancellationToken) + public async Task> Handle(GetActorChatSessionsQuery request, CancellationToken cancellationToken) { var returnData = await _context.ChatSessions .OrderByDescending(x => x.Timestamp) - .Where(x => x.AuthorId == request.AuthorId + .Where(x => x.ActorId == request.ActorId && (request.StartDate == null || x.Timestamp > request.StartDate) && (request.EndDate == null || x.Timestamp < request.EndDate)) .Select(x => ChatSessionDto.CreateFrom(x)) diff --git a/src/Core.Application/Actor/GetActorChatSessionsQueryValidator.cs b/src/Core.Application/Actor/GetActorChatSessionsQueryValidator.cs new file mode 100644 index 0000000..0aa4898 --- /dev/null +++ b/src/Core.Application/Actor/GetActorChatSessionsQueryValidator.cs @@ -0,0 +1,18 @@ +namespace Goodtocode.SemanticKernel.Core.Application.Actor; + +public class GetActorChatSessionsQueryValidator : Validator +{ + public GetActorChatSessionsQueryValidator() + { + RuleFor(x => x.ActorId).NotEmpty(); + + RuleFor(v => v.StartDate).NotEmpty() + .When(v => v.EndDate != null) + .LessThanOrEqualTo(v => v.EndDate); + + RuleFor(v => v.EndDate) + .NotEmpty() + .When(v => v.StartDate != null) + .GreaterThanOrEqualTo(v => v.StartDate); + } +} \ No newline at end of file diff --git a/src/Core.Application/Actor/GetActorQuery.cs b/src/Core.Application/Actor/GetActorQuery.cs new file mode 100644 index 0000000..d0108cb --- /dev/null +++ b/src/Core.Application/Actor/GetActorQuery.cs @@ -0,0 +1,29 @@ +using Goodtocode.SemanticKernel.Core.Application.Abstractions; +using Goodtocode.SemanticKernel.Core.Application.Common.Exceptions; +using Goodtocode.SemanticKernel.Core.Domain.Actor; + +namespace Goodtocode.SemanticKernel.Core.Application.Actor; + +public class GetActorQuery : IRequest +{ + public Guid ActorId { get; set; } +} + +public class GetAuthorQueryHandler(ISemanticKernelContext context) : IRequestHandler +{ + private readonly ISemanticKernelContext _context = context; + + public async Task Handle(GetActorQuery request, CancellationToken cancellationToken) + { + var actor = await _context.Actors.FindAsync([request.ActorId, cancellationToken], cancellationToken: cancellationToken); + GuardAgainstNotFound(actor); + + return ActorDto.CreateFrom(actor); + } + + private static void GuardAgainstNotFound(ActorEntity? Actor) + { + if (Actor == null) + throw new CustomNotFoundException("Actor Not Found"); + } +} \ No newline at end of file diff --git a/src/Core.Application/Actor/GetActorQueryValidator.cs b/src/Core.Application/Actor/GetActorQueryValidator.cs new file mode 100644 index 0000000..f09c446 --- /dev/null +++ b/src/Core.Application/Actor/GetActorQueryValidator.cs @@ -0,0 +1,9 @@ +namespace Goodtocode.SemanticKernel.Core.Application.Actor; + +public class GetActorQueryValidator : Validator +{ + public GetActorQueryValidator() + { + RuleFor(x => x.ActorId).NotEmpty(); + } +} \ No newline at end of file diff --git a/src/Core.Application/Actor/GetMyActorQuery.cs b/src/Core.Application/Actor/GetMyActorQuery.cs new file mode 100644 index 0000000..7f65074 --- /dev/null +++ b/src/Core.Application/Actor/GetMyActorQuery.cs @@ -0,0 +1,30 @@ +using Goodtocode.SemanticKernel.Core.Application.Abstractions; +using Goodtocode.SemanticKernel.Core.Application.Common.Exceptions; +using Goodtocode.SemanticKernel.Core.Domain.Actor; +using Goodtocode.SemanticKernel.Core.Domain.Auth; + +namespace Goodtocode.SemanticKernel.Core.Application.Actor; + +public class GetMyActorQuery : IRequest, IUserInfoRequest +{ + public IUserEntity? UserInfo { get; set; } +} + +public class GetAuthorByOwnerIdQueryHandler(ISemanticKernelContext context) : IRequestHandler +{ + private readonly ISemanticKernelContext _context = context; + + public async Task Handle(GetMyActorQuery request, CancellationToken cancellationToken) + { + var actor = await _context.Actors.Where(x => x.OwnerId == request!.UserInfo!.OwnerId).FirstOrDefaultAsync(cancellationToken: cancellationToken); + GuardAgainstNotFound(actor); + + return ActorDto.CreateFrom(actor); + } + + private static void GuardAgainstNotFound(ActorEntity? Actor) + { + if (Actor == null) + throw new CustomNotFoundException("Actor Not Found"); + } +} \ No newline at end of file diff --git a/src/Core.Application/Actor/GetMyActorQueryValidator.cs b/src/Core.Application/Actor/GetMyActorQueryValidator.cs new file mode 100644 index 0000000..bca2327 --- /dev/null +++ b/src/Core.Application/Actor/GetMyActorQueryValidator.cs @@ -0,0 +1,9 @@ +namespace Goodtocode.SemanticKernel.Core.Application.Actor; + +public class GetMyActorQueryValidator : Validator +{ + public GetMyActorQueryValidator() + { + RuleFor(x => x.UserInfo).NotEmpty(); + } +} \ No newline at end of file diff --git a/src/Core.Application/Actor/SaveMyActorCommand.cs b/src/Core.Application/Actor/SaveMyActorCommand.cs new file mode 100644 index 0000000..67f0e30 --- /dev/null +++ b/src/Core.Application/Actor/SaveMyActorCommand.cs @@ -0,0 +1,49 @@ +using Goodtocode.SemanticKernel.Core.Application.Abstractions; +using Goodtocode.SemanticKernel.Core.Domain.Actor; +using Goodtocode.SemanticKernel.Core.Domain.Auth; + +namespace Goodtocode.SemanticKernel.Core.Application.Actor; + +public class SaveMyActorCommand : IRequest, IUserInfoRequest +{ + public string? FirstName { get; set; } + public string? LastName { get; set; } + public string? Email { get; set; } + public Guid TenantId { get; set; } + public IUserEntity? UserInfo { get; set; } +} + +public class SaveAuthorCommandHandler(ISemanticKernelContext context) : IRequestHandler +{ + private readonly ISemanticKernelContext _context = context; + + public async Task Handle(SaveMyActorCommand request, CancellationToken cancellationToken) + { + GuardAgainstEmptyTenantId(request?.TenantId); + + var actor = await _context.Actors.Where(x => x.OwnerId == request!.UserInfo!.OwnerId && x.TenantId == request.TenantId).FirstOrDefaultAsync(cancellationToken); + if (actor is not null) + { + actor.Update(request?.FirstName, request?.LastName ?? actor.LastName, request?.Email); + _context.Actors.Update(actor!); + } + else + { + actor = ActorEntity.Create(Guid.NewGuid(), request!.UserInfo!.OwnerId, request.TenantId, request.FirstName, request.LastName, request.Email); + _context.Actors.Add(actor); + } + + await _context.SaveChangesAsync(cancellationToken); + + return ActorDto.CreateFrom(actor); + } + + private static void GuardAgainstEmptyTenantId(Guid? tenantId) + { + if (tenantId == Guid.Empty) + throw new CustomValidationException( + [ + new("TenantId", "A TenantId is required to link an actor with an account") + ]); + } +} \ No newline at end of file diff --git a/src/Core.Application/Actor/SaveMyActorCommandValidator.cs b/src/Core.Application/Actor/SaveMyActorCommandValidator.cs new file mode 100644 index 0000000..66c2383 --- /dev/null +++ b/src/Core.Application/Actor/SaveMyActorCommandValidator.cs @@ -0,0 +1,10 @@ +namespace Goodtocode.SemanticKernel.Core.Application.Actor; + +public class SaveMyActorCommandValidator : Validator +{ + public SaveMyActorCommandValidator() + { + RuleFor(x => x.UserInfo).NotEmpty(); + RuleFor(x => x.TenantId).NotEmpty(); + } +} \ No newline at end of file diff --git a/src/Core.Application/Actor/UpdateActorCommand.cs b/src/Core.Application/Actor/UpdateActorCommand.cs new file mode 100644 index 0000000..6d0d23b --- /dev/null +++ b/src/Core.Application/Actor/UpdateActorCommand.cs @@ -0,0 +1,41 @@ +using Goodtocode.SemanticKernel.Core.Application.Abstractions; +using Goodtocode.SemanticKernel.Core.Application.Common.Exceptions; +using Goodtocode.SemanticKernel.Core.Domain.Actor; + +namespace Goodtocode.SemanticKernel.Core.Application.Actor; + +public class UpdateActorCommand : IRequest +{ + public Guid OwnerId { get; set; } + public string Name { get; set; } = string.Empty; +} + +public class UpdateAuthorCommandHandler(ISemanticKernelContext context) : IRequestHandler +{ + private readonly ISemanticKernelContext _context = context; + + public async Task Handle(UpdateActorCommand request, CancellationToken cancellationToken) + { + GuardAgainstIdEmpty(request.OwnerId); + var actor = await _context.Actors.Where(x => x.OwnerId == request.OwnerId).FirstOrDefaultAsync(cancellationToken); + GuardAgainstNotFound(actor); + + _context.Actors.Update(actor!); + await _context.SaveChangesAsync(cancellationToken); + } + + private static void GuardAgainstIdEmpty(Guid ownerId) + { + if (ownerId == Guid.Empty) + throw new CustomValidationException( + [ + new("OwnerId", "A valid OwnerId is required to update an actor") + ]); + } + + private static void GuardAgainstNotFound(ActorEntity? actor) + { + if (actor == null) + throw new CustomNotFoundException("Actor Not Found"); + } +} \ No newline at end of file diff --git a/src/Core.Application/Actor/UpdateActorCommandValidator.cs b/src/Core.Application/Actor/UpdateActorCommandValidator.cs new file mode 100644 index 0000000..ffe6e75 --- /dev/null +++ b/src/Core.Application/Actor/UpdateActorCommandValidator.cs @@ -0,0 +1,9 @@ +namespace Goodtocode.SemanticKernel.Core.Application.Actor; + +public class UpdateActorCommandValidator : Validator +{ + public UpdateActorCommandValidator() + { + RuleFor(x => x.Name).NotEmpty(); + } +} \ No newline at end of file diff --git a/src/Core.Application/Audio/CreateTextToAudioCommand.cs b/src/Core.Application/Audio/CreateTextToAudioCommand.cs index 76f4959..9a1d38f 100644 --- a/src/Core.Application/Audio/CreateTextToAudioCommand.cs +++ b/src/Core.Application/Audio/CreateTextToAudioCommand.cs @@ -1,4 +1,5 @@ using Goodtocode.SemanticKernel.Core.Application.Abstractions; +using Goodtocode.SemanticKernel.Core.Application.Common.Exceptions; using Goodtocode.SemanticKernel.Core.Domain.Audio; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.TextToAudio; @@ -8,7 +9,7 @@ namespace Goodtocode.SemanticKernel.Core.Application.Audio; public class CreateTextToAudioCommand : IRequest { public Guid Id { get; set; } - public Guid AuthorId { get; set; } + public Guid ActorId { get; set; } public string Prompt { get; set; } = string.Empty; } @@ -20,9 +21,9 @@ public class CreateTextToAudioCommandHandler(Kernel kernel, ISemanticKernelConte public async Task Handle(CreateTextToAudioCommand request, CancellationToken cancellationToken) { - GuardAgainstMissingAuthor(request.AuthorId); + GuardAgainstMissingActor(request.ActorId); GuardAgainstEmptyPrompt(request?.Prompt); - GuardAgainstIdExsits(_context.TextAudio, request!.Id); + GuardAgainstIdExists(_context.TextAudio, request!.Id); var service = _kernel.GetRequiredService(); var executionSettings = new PromptExecutionSettings @@ -31,19 +32,19 @@ public async Task Handle(CreateTextToAudioCommand request, Cancell }; var response = await service.GetAudioContentAsync(request.Prompt, executionSettings, _kernel, cancellationToken); - var textAudio = TextAudioEntity.Create(request.Id, request.AuthorId, request.Prompt, response.Data.GetValueOrDefault().ToArray(), response.Uri); + var textAudio = TextAudioEntity.Create(request.Id, request.ActorId, request.Prompt, response.Data.GetValueOrDefault().ToArray(), response.Uri); _context.TextAudio.Add(textAudio); await _context.SaveChangesAsync(cancellationToken); return TextAudioDto.CreateFrom(textAudio); } - private static void GuardAgainstMissingAuthor(Guid authorId) + private static void GuardAgainstMissingActor(Guid actorId) { - if (authorId == Guid.Empty) + if (actorId == Guid.Empty) throw new CustomValidationException( [ - new("AuthorId", "AuthorId required for sessions") + new("ActorId", "ActorId required for sessions") ]); } @@ -56,13 +57,10 @@ private static void GuardAgainstEmptyPrompt(string? prompt) ]); } - private static void GuardAgainstIdExsits(DbSet dbSet, Guid id) + private static void GuardAgainstIdExists(DbSet dbSet, Guid id) { if (dbSet.Any(x => x.Id == id)) - throw new CustomValidationException( - [ - new("Id", "Id already exists") - ]); + throw new CustomConflictException("Id already exists"); } } #pragma warning restore SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. \ No newline at end of file diff --git a/src/Core.Application/Audio/TextAudioDto.cs b/src/Core.Application/Audio/TextAudioDto.cs index 6ce9a9e..7f26b47 100644 --- a/src/Core.Application/Audio/TextAudioDto.cs +++ b/src/Core.Application/Audio/TextAudioDto.cs @@ -1,12 +1,11 @@ -using Goodtocode.SemanticKernel.Core.Application.Common.Mappings; -using Goodtocode.SemanticKernel.Core.Domain.Audio; +using Goodtocode.SemanticKernel.Core.Domain.Audio; namespace Goodtocode.SemanticKernel.Core.Application.Audio; public class TextAudioDto { public Guid Id { get; set; } = Guid.Empty; - public Guid AuthorId { get; set; } = Guid.Empty; + public Guid ActorId { get; set; } = Guid.Empty; public string Description { get; set; } = string.Empty; public ReadOnlyMemory? AudioBytes { get; set; } public Uri? AudioUrl { get; set; } @@ -18,7 +17,7 @@ public static TextAudioDto CreateFrom(TextAudioEntity? entity) return new TextAudioDto { Id = entity.Id, - AuthorId = entity.AuthorId, + ActorId = entity.ActorId, Description = entity.Description, AudioBytes = entity.AudioBytes, AudioUrl = entity.AudioUrl, diff --git a/src/Core.Application/Author/AuthorDto.cs b/src/Core.Application/Author/AuthorDto.cs deleted file mode 100644 index 7596642..0000000 --- a/src/Core.Application/Author/AuthorDto.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Goodtocode.SemanticKernel.Core.Application.Common.Mappings; -using Goodtocode.SemanticKernel.Core.Domain.Author; - -namespace Goodtocode.SemanticKernel.Core.Application.Author; - -public class AuthorDto -{ - public Guid Id { get; set; } = Guid.Empty; - public string Name { get; set; } = string.Empty; - public DateTime CreatedOn { get; private set; } = DateTime.UtcNow; - public DateTime? ModifiedOn { get; private set; } - public DateTime? DeletedOn { get; private set; } - - public static AuthorDto CreateFrom(AuthorEntity? entity) - { - if (entity is null) return null!; - return new AuthorDto - { - Id = entity.Id, - Name = entity.Name ?? string.Empty, - CreatedOn = entity.CreatedOn, - ModifiedOn = entity.ModifiedOn, - DeletedOn = entity.DeletedOn - }; - } -} diff --git a/src/Core.Application/Author/CreateAuthorCommand.cs b/src/Core.Application/Author/CreateAuthorCommand.cs deleted file mode 100644 index 949a204..0000000 --- a/src/Core.Application/Author/CreateAuthorCommand.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Goodtocode.SemanticKernel.Core.Application.Abstractions; -using Goodtocode.SemanticKernel.Core.Application.Common.Exceptions; -using Goodtocode.SemanticKernel.Core.Domain.Author; - -namespace Goodtocode.SemanticKernel.Core.Application.Author; - -public class CreateAuthorCommand : IRequest -{ - public Guid Id { get; set; } - public string? Name { get; set; } -} - -public class CreateAuthorCommandHandler(ISemanticKernelContext context) : IRequestHandler -{ - private readonly ISemanticKernelContext _context = context; - - public async Task Handle(CreateAuthorCommand request, CancellationToken cancellationToken) - { - GuardAgainstEmptyName(request?.Name); - GuardAgainstIdExsits(_context.Authors, request!.Id); - - var Author = AuthorEntity.Create(request!.Id == Guid.Empty ? Guid.NewGuid() : request!.Id, string.Empty); - _context.Authors.Add(Author); - try - { - await _context.SaveChangesAsync(cancellationToken); - } - catch (DbUpdateException) - { - throw new CustomValidationException( - [ - new("Id", "Id already exists") - ]); - } - - return AuthorDto.CreateFrom(Author); - } - - private static void GuardAgainstEmptyName(string? message) - { - if (string.IsNullOrWhiteSpace(message)) - throw new CustomValidationException( - [ - new("Name", "A name is required to get a response") - ]); - } - - private static void GuardAgainstIdExsits(DbSet dbSet, Guid id) - { - if (dbSet.Any(x => x.Id == id)) - throw new CustomValidationException( - [ - new("Id", "Id already exists") - ]); - } -} \ No newline at end of file diff --git a/src/Core.Application/Author/CreateAuthorCommandValidator.cs b/src/Core.Application/Author/CreateAuthorCommandValidator.cs deleted file mode 100644 index 806ad41..0000000 --- a/src/Core.Application/Author/CreateAuthorCommandValidator.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Goodtocode.SemanticKernel.Core.Application.Audio; - -namespace Goodtocode.SemanticKernel.Core.Application.Author; - -public class CreateAuthorCommandValidator : Validator -{ - public CreateAuthorCommandValidator() - { - RuleFor(x => x.Name).NotEmpty(); - } -} \ No newline at end of file diff --git a/src/Core.Application/Author/DeleteAuthorCommand.cs b/src/Core.Application/Author/DeleteAuthorCommand.cs deleted file mode 100644 index 11dd758..0000000 --- a/src/Core.Application/Author/DeleteAuthorCommand.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Goodtocode.SemanticKernel.Core.Application.Abstractions; -using Goodtocode.SemanticKernel.Core.Application.Common.Exceptions; -using Goodtocode.SemanticKernel.Core.Domain.Author; - -namespace Goodtocode.SemanticKernel.Core.Application.Author; - -public class DeleteAuthorCommand : IRequest -{ - public Guid Id { get; set; } -} - -public class DeleteAuthorCommandHandler(ISemanticKernelContext context) : IRequestHandler -{ - private readonly ISemanticKernelContext _context = context; - - public async Task Handle(DeleteAuthorCommand request, CancellationToken cancellationToken) - { - var Author = _context.Authors.Find(request.Id); - GuardAgainstNotFound(Author); - - _context.Authors.Remove(Author!); - await _context.SaveChangesAsync(cancellationToken); - } - - private static void GuardAgainstNotFound(AuthorEntity? Author) - { - if (Author == null) - throw new CustomNotFoundException("Author Not Found"); - } -} \ No newline at end of file diff --git a/src/Core.Application/Author/DeleteAuthorCommandValidator.cs b/src/Core.Application/Author/DeleteAuthorCommandValidator.cs deleted file mode 100644 index a1d290f..0000000 --- a/src/Core.Application/Author/DeleteAuthorCommandValidator.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Goodtocode.SemanticKernel.Core.Application.Author; - -public class DeleteAuthorCommandValidator : Validator -{ - public DeleteAuthorCommandValidator() - { - RuleFor(x => x.Id).NotEmpty(); - } -} \ No newline at end of file diff --git a/src/Core.Application/Author/GetAuthorChatSessionQueryValidator.cs b/src/Core.Application/Author/GetAuthorChatSessionQueryValidator.cs deleted file mode 100644 index c06b36e..0000000 --- a/src/Core.Application/Author/GetAuthorChatSessionQueryValidator.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Goodtocode.SemanticKernel.Core.Application.Author; - -public class GetAuthorChatSessionQueryValidator : Validator -{ - public GetAuthorChatSessionQueryValidator() - { - RuleFor(x => x.AuthorId).NotEmpty(); - RuleFor(x => x.ChatSessionId).NotEmpty(); - } -} \ No newline at end of file diff --git a/src/Core.Application/Author/GetAuthorChatSessionsQueryValidator.cs b/src/Core.Application/Author/GetAuthorChatSessionsQueryValidator.cs deleted file mode 100644 index 907ed50..0000000 --- a/src/Core.Application/Author/GetAuthorChatSessionsQueryValidator.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Goodtocode.SemanticKernel.Core.Application.Author; - -namespace Goodtocode.SemanticKernel.Core.Application.ChatCompletion; - -public class GetAuthorChatSessionsQueryValidator : Validator -{ - public GetAuthorChatSessionsQueryValidator() - { - RuleFor(x => x.AuthorId).NotEmpty(); - - RuleFor(v => v.StartDate).NotEmpty() - .When(v => v.EndDate != null) - .LessThanOrEqualTo(v => v.EndDate); - - RuleFor(v => v.EndDate) - .NotEmpty() - .When(v => v.StartDate != null) - .GreaterThanOrEqualTo(v => v.StartDate); - } -} \ No newline at end of file diff --git a/src/Core.Application/Author/GetAuthorQuery.cs b/src/Core.Application/Author/GetAuthorQuery.cs deleted file mode 100644 index 569d5d2..0000000 --- a/src/Core.Application/Author/GetAuthorQuery.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Goodtocode.SemanticKernel.Core.Application.Abstractions; -using Goodtocode.SemanticKernel.Core.Application.Common.Exceptions; -using Goodtocode.SemanticKernel.Core.Domain.Author; - -namespace Goodtocode.SemanticKernel.Core.Application.Author; - -public class GetAuthorQuery : IRequest -{ - public Guid AuthorId { get; set; } -} - -public class GetAuthorQueryHandler(ISemanticKernelContext context) : IRequestHandler -{ - private readonly ISemanticKernelContext _context = context; - - public async Task Handle(GetAuthorQuery request, CancellationToken cancellationToken) - { - var author = await _context.Authors.FindAsync([request.AuthorId, cancellationToken], cancellationToken: cancellationToken); - GuardAgainstNotFound(author); - - return AuthorDto.CreateFrom(author); - } - - private static void GuardAgainstNotFound(AuthorEntity? Author) - { - if (Author == null) - throw new CustomNotFoundException("Author Not Found"); - } -} \ No newline at end of file diff --git a/src/Core.Application/Author/GetAuthorQueryValidator.cs b/src/Core.Application/Author/GetAuthorQueryValidator.cs deleted file mode 100644 index 6cc1edf..0000000 --- a/src/Core.Application/Author/GetAuthorQueryValidator.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Goodtocode.SemanticKernel.Core.Application.Author; - -public class GetAuthorQueryValidator : Validator -{ - public GetAuthorQueryValidator() - { - RuleFor(x => x.AuthorId).NotEmpty(); - } -} \ No newline at end of file diff --git a/src/Core.Application/Author/UpdateAuthorCommand.cs b/src/Core.Application/Author/UpdateAuthorCommand.cs deleted file mode 100644 index 60790f2..0000000 --- a/src/Core.Application/Author/UpdateAuthorCommand.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Goodtocode.SemanticKernel.Core.Application.Abstractions; -using Goodtocode.SemanticKernel.Core.Application.Common.Exceptions; -using Goodtocode.SemanticKernel.Core.Domain.Author; - -namespace Goodtocode.SemanticKernel.Core.Application.Author; - -public class UpdateAuthorCommand : IRequest -{ - public Guid Id { get; set; } - public string Name { get; set; } = string.Empty; -} - -public class UpdateAuthorCommandHandler(ISemanticKernelContext context) : IRequestHandler -{ - private readonly ISemanticKernelContext _context = context; - - public async Task Handle(UpdateAuthorCommand request, CancellationToken cancellationToken) - { - var Author = _context.Authors.Find(request.Id); - GuardAgainstNotFound(Author); - - - _context.Authors.Update(Author!); - await _context.SaveChangesAsync(cancellationToken); - } - - private static void GuardAgainstNotFound(AuthorEntity? Author) - { - if (Author == null) - throw new CustomNotFoundException("Author Not Found"); - } -} \ No newline at end of file diff --git a/src/Core.Application/Author/UpdateAuthorCommandValidator.cs b/src/Core.Application/Author/UpdateAuthorCommandValidator.cs deleted file mode 100644 index 0331dc2..0000000 --- a/src/Core.Application/Author/UpdateAuthorCommandValidator.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Goodtocode.SemanticKernel.Core.Application.Author; - -public class UpdateAuthorCommandValidator : Validator -{ - public UpdateAuthorCommandValidator() - { - RuleFor(x => x.Id).NotEmpty(); - RuleFor(x => x.Name).NotEmpty(); - } -} \ No newline at end of file diff --git a/src/Core.Application/ChatCompletion/ChatSessionDto.cs b/src/Core.Application/ChatCompletion/ChatSessionDto.cs index 44b9baa..78f71bd 100644 --- a/src/Core.Application/ChatCompletion/ChatSessionDto.cs +++ b/src/Core.Application/ChatCompletion/ChatSessionDto.cs @@ -6,7 +6,7 @@ public class ChatSessionDto { public Guid Id { get; set; } = Guid.Empty; public string Title { get; set; } = string.Empty; - public Guid AuthorId { get; set; } = Guid.Empty; + public Guid ActorId { get; set; } = Guid.Empty; public DateTimeOffset Timestamp { get; set; } public ICollection? Messages { get; set; } @@ -17,7 +17,7 @@ public static ChatSessionDto CreateFrom(ChatSessionEntity? entity) { Id = entity.Id, Title = entity.Title ?? string.Empty, - AuthorId = entity.AuthorId, + ActorId = entity.ActorId, Timestamp = entity.Timestamp, Messages = entity.Messages?.Select(ChatMessageDto.CreateFrom).ToList() }; diff --git a/src/Core.Application/ChatCompletion/CreateChatMessageCommand.cs b/src/Core.Application/ChatCompletion/CreateChatMessageCommand.cs index 146ae0d..e646b37 100644 --- a/src/Core.Application/ChatCompletion/CreateChatMessageCommand.cs +++ b/src/Core.Application/ChatCompletion/CreateChatMessageCommand.cs @@ -1,16 +1,18 @@ using Goodtocode.SemanticKernel.Core.Application.Abstractions; using Goodtocode.SemanticKernel.Core.Application.Common.Exceptions; +using Goodtocode.SemanticKernel.Core.Domain.Auth; using Goodtocode.SemanticKernel.Core.Domain.ChatCompletion; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.ChatCompletion; namespace Goodtocode.SemanticKernel.Core.Application.ChatCompletion; -public class CreateChatMessageCommand : IRequest +public class CreateChatMessageCommand : IRequest, IUserInfoRequest { public Guid Id { get; set; } public Guid ChatSessionId { get; set; } public string? Message { get; set; } + public IUserEntity? UserInfo { get; set; } } public class CreateChatMessageCommandHandler(Kernel kernel, ISemanticKernelContext context) : IRequestHandler @@ -22,7 +24,9 @@ public async Task Handle(CreateChatMessageCommand request, Cance { GuardAgainstSessionNotFound(_context.ChatSessions, request!.ChatSessionId); GuardAgainstEmptyMessage(request?.Message); - GuardAgainstIdExsits(_context.ChatMessages, request!.Id); + GuardAgainstIdExists(_context.ChatMessages, request!.Id); + GuardAgainstEmptyUser(request?.UserInfo); + GuardAgainstUnauthorizedUser(_context.ChatSessions, request!.UserInfo!); var chatSession = _context.ChatSessions.Find(request.ChatSessionId); @@ -73,12 +77,28 @@ private static void GuardAgainstEmptyMessage(string? message) ]); } - private static void GuardAgainstIdExsits(DbSet dbSet, Guid id) + private static void GuardAgainstIdExists(DbSet dbSet, Guid id) { if (dbSet.Any(x => x.Id == id)) + throw new CustomConflictException("Id already exists"); + } + + private static void GuardAgainstEmptyUser(IUserEntity? userInfo) + { + if (userInfo == null || userInfo.OwnerId == Guid.Empty || userInfo.TenantId == Guid.Empty) + throw new CustomValidationException( + [ + new("UserInfo", "User information is required to create a chat message") + ]); + } + + private static void GuardAgainstUnauthorizedUser(DbSet dbSet, IUserEntity userInfo) + { + bool isAuthorized = dbSet.Any(x => x.Actor != null && x.Actor.OwnerId == userInfo.OwnerId); + if (!isAuthorized) throw new CustomValidationException( [ - new("Id", "Id already exists") + new("UserInfo", "User is not authorized to create a chat message in this session") ]); } } diff --git a/src/Core.Application/ChatCompletion/CreateChatSessionCommand.cs b/src/Core.Application/ChatCompletion/CreateChatSessionCommand.cs index 53669a3..9aa8e22 100644 --- a/src/Core.Application/ChatCompletion/CreateChatSessionCommand.cs +++ b/src/Core.Application/ChatCompletion/CreateChatSessionCommand.cs @@ -1,6 +1,6 @@ using Goodtocode.SemanticKernel.Core.Application.Abstractions; using Goodtocode.SemanticKernel.Core.Application.Common.Exceptions; -using Goodtocode.SemanticKernel.Core.Domain.Author; +using Goodtocode.SemanticKernel.Core.Domain.Actor; using Goodtocode.SemanticKernel.Core.Domain.ChatCompletion; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.ChatCompletion; @@ -10,8 +10,7 @@ namespace Goodtocode.SemanticKernel.Core.Application.ChatCompletion; public class CreateChatSessionCommand : IRequest { public Guid Id { get; set; } - public Guid AuthorId { get; set; } - public string? AuthorName { get; set; } + public Guid ActorId { get; set; } public string? Title { get; set; } public string? Message { get; set; } } @@ -23,9 +22,9 @@ public class CreateChatSessionCommandHandler(Kernel kernel, ISemanticKernelConte public async Task Handle(CreateChatSessionCommand request, CancellationToken cancellationToken) { - GuardAgainstMissingAuthor(request.AuthorId); + GuardAgainstEmtpyActorId(request.ActorId); GuardAgainstEmptyMessage(request?.Message); - GuardAgainstIdExsits(_context.ChatSessions, request!.Id); + GuardAgainstIdExists(_context.ChatSessions, request!.Id); var service = _kernel.GetRequiredService(); ChatHistory chatHistory = []; @@ -36,18 +35,15 @@ public async Task Handle(CreateChatSessionCommand request, Cance }; var response = await service.GetChatMessageContentAsync(chatHistory, executionSettings, _kernel, cancellationToken); - var author = await _context.Authors - .FirstOrDefaultAsync(x => x.Id == request.AuthorId, cancellationToken); - if (author == null) - { - author = AuthorEntity.Create(request.AuthorId, request?.AuthorName); - _context.Authors.Add(author); - } + var actor = await _context.Actors + .FirstOrDefaultAsync(x => x.OwnerId == request.ActorId, cancellationToken); + GuardAgainstActorNotFound(actor); + var title = request!.Title ?? $"{request!.Message![..(request.Message!.Length >= 25 ? 25 : request.Message!.Length)]}"; var chatSession = ChatSessionEntity.Create( request.Id, - request.AuthorId, - title, + actor!.Id, + title, Enum.TryParse(response.Role.ToString().ToLowerInvariant(), out var role) ? role : ChatMessageRole.assistant, request.Message!, response.ToString() @@ -58,12 +54,21 @@ public async Task Handle(CreateChatSessionCommand request, Cance return ChatSessionDto.CreateFrom(chatSession); } - private static void GuardAgainstMissingAuthor(Guid authorId) + private static void GuardAgainstActorNotFound(ActorEntity? actor) { - if (authorId == Guid.Empty) + if (actor == null) throw new CustomValidationException( [ - new("AuthorId", "AuthorId required for sessions") + new("ActorId", "ActorId required for sessions") + ]); + } + + private static void GuardAgainstEmtpyActorId(Guid actorId) + { + if (actorId == Guid.Empty) + throw new CustomValidationException( + [ + new("ActorId", "ActorId required for sessions") ]); } @@ -76,12 +81,9 @@ private static void GuardAgainstEmptyMessage(string? message) ]); } - private static void GuardAgainstIdExsits(DbSet dbSet, Guid id) + private static void GuardAgainstIdExists(DbSet dbSet, Guid id) { if (dbSet.Any(x => x.Id == id)) - throw new CustomValidationException( - [ - new("Id", "Id already exists") - ]); + throw new CustomConflictException("Id already exists"); } } \ No newline at end of file diff --git a/src/Core.Application/Image/DeleteTextImageCommand.cs b/src/Core.Application/ChatCompletion/DeleteTextImageCommand.cs similarity index 93% rename from src/Core.Application/Image/DeleteTextImageCommand.cs rename to src/Core.Application/ChatCompletion/DeleteTextImageCommand.cs index 4cc8466..f729674 100644 --- a/src/Core.Application/Image/DeleteTextImageCommand.cs +++ b/src/Core.Application/ChatCompletion/DeleteTextImageCommand.cs @@ -2,7 +2,7 @@ using Goodtocode.SemanticKernel.Core.Application.Common.Exceptions; using Goodtocode.SemanticKernel.Core.Domain.Image; -namespace Goodtocode.SemanticKernel.Core.Application.Image; +namespace Goodtocode.SemanticKernel.Core.Application.ChatCompletion; public class DeleteTextImageCommand : IRequest { diff --git a/src/Core.Application/Image/DeleteTextImageCommandValidator.cs b/src/Core.Application/ChatCompletion/DeleteTextImageCommandValidator.cs similarity index 71% rename from src/Core.Application/Image/DeleteTextImageCommandValidator.cs rename to src/Core.Application/ChatCompletion/DeleteTextImageCommandValidator.cs index 160bdcc..6a41117 100644 --- a/src/Core.Application/Image/DeleteTextImageCommandValidator.cs +++ b/src/Core.Application/ChatCompletion/DeleteTextImageCommandValidator.cs @@ -1,4 +1,4 @@ -namespace Goodtocode.SemanticKernel.Core.Application.Image; +namespace Goodtocode.SemanticKernel.Core.Application.ChatCompletion; public class DeleteTextImageCommandValidator : Validator { diff --git a/src/Core.Application/ChatCompletion/GetMyChatSessionQuery.cs b/src/Core.Application/ChatCompletion/GetMyChatSessionQuery.cs new file mode 100644 index 0000000..e097e3e --- /dev/null +++ b/src/Core.Application/ChatCompletion/GetMyChatSessionQuery.cs @@ -0,0 +1,40 @@ +using Goodtocode.SemanticKernel.Core.Application.Abstractions; +using Goodtocode.SemanticKernel.Core.Application.Common.Exceptions; +using Goodtocode.SemanticKernel.Core.Domain.Auth; +using Goodtocode.SemanticKernel.Core.Domain.ChatCompletion; + +namespace Goodtocode.SemanticKernel.Core.Application.ChatCompletion; + +public class GetMyChatSessionQuery : IRequest, IUserInfoRequest +{ + public Guid ChatSessionId { get; set; } + public IUserEntity? UserInfo { get; set; } +} + +public class GetAuthorChatSessionByOwnerIdQueryHandler(ISemanticKernelContext context) : IRequestHandler +{ + private readonly ISemanticKernelContext _context = context; + + public async Task Handle(GetMyChatSessionQuery request, CancellationToken cancellationToken) + { + var returnData = await _context.ChatSessions + .Where(cs => cs.Id == request.ChatSessionId) + .Join(_context.Actors, + cs => cs.ActorId, + a => a.Id, + (cs, a) => new { ChatSession = cs, Actor = a }) + .Where(joined => joined.Actor.OwnerId == request.UserInfo!.OwnerId) + .Select(joined => joined.ChatSession) + .FirstOrDefaultAsync(cancellationToken); + + GuardAgainstNotFound(returnData); + + return ChatSessionDto.CreateFrom(returnData); + } + + private static void GuardAgainstNotFound(ChatSessionEntity? entity) + { + if (entity is null) + throw new CustomNotFoundException("Chat Session Not Found"); + } +} \ No newline at end of file diff --git a/src/Core.Application/ChatCompletion/GetMyChatSessionQueryValidator.cs b/src/Core.Application/ChatCompletion/GetMyChatSessionQueryValidator.cs new file mode 100644 index 0000000..5177647 --- /dev/null +++ b/src/Core.Application/ChatCompletion/GetMyChatSessionQueryValidator.cs @@ -0,0 +1,10 @@ +namespace Goodtocode.SemanticKernel.Core.Application.ChatCompletion; + +public class GetMyChatSessionQueryValidator : Validator +{ + public GetMyChatSessionQueryValidator() + { + RuleFor(x => x.UserInfo).NotEmpty(); + RuleFor(x => x.ChatSessionId).NotEmpty(); + } +} \ No newline at end of file diff --git a/src/Core.Application/ChatCompletion/GetMyChatSessionsPaginatedQuery.cs b/src/Core.Application/ChatCompletion/GetMyChatSessionsPaginatedQuery.cs new file mode 100644 index 0000000..c40bd36 --- /dev/null +++ b/src/Core.Application/ChatCompletion/GetMyChatSessionsPaginatedQuery.cs @@ -0,0 +1,40 @@ +using Goodtocode.SemanticKernel.Core.Application.Abstractions; +using Goodtocode.SemanticKernel.Core.Application.Common.Mappings; +using Goodtocode.SemanticKernel.Core.Application.Common.Models; +using Goodtocode.SemanticKernel.Core.Domain.Auth; + +namespace Goodtocode.SemanticKernel.Core.Application.ChatCompletion; + +public class GetMyChatSessionsPaginatedQuery : IRequest>, IUserInfoRequest +{ + public DateTime? StartDate { get; set; } + public DateTime? EndDate { get; set; } + public int PageNumber { get; init; } = 1; + public int PageSize { get; init; } = 10; + public IUserEntity? UserInfo { get; set; } +} + +public class GetAuthorChatSessionsByExternalIdPaginatedQueryHandler(ISemanticKernelContext context) : IRequestHandler> +{ + private readonly ISemanticKernelContext _context = context; + + public async Task> Handle(GetMyChatSessionsPaginatedQuery request, CancellationToken cancellationToken) + { + var returnData = await _context.ChatSessions + .Include(x => x.Messages) + .OrderByDescending(x => x.Timestamp) + .Where(x => (request.StartDate == null || x.Timestamp > request.StartDate) + && (request.EndDate == null || x.Timestamp < request.EndDate)) + .Join(_context.Actors, + cs => cs.ActorId, + a => a.Id, + (cs, a) => new { ChatSession = cs, Actor = a }) + .Where(joined => joined.Actor.OwnerId == request.UserInfo!.OwnerId) + .Select(joined => joined.ChatSession) + .Select(x => ChatSessionDto.CreateFrom(x)) + .PaginatedListAsync(request.PageNumber, request.PageSize); + + return returnData; + + } +} \ No newline at end of file diff --git a/src/Core.Application/ChatCompletion/GetMyChatSessionsPaginatedQueryValidator.cs b/src/Core.Application/ChatCompletion/GetMyChatSessionsPaginatedQueryValidator.cs new file mode 100644 index 0000000..a5670f9 --- /dev/null +++ b/src/Core.Application/ChatCompletion/GetMyChatSessionsPaginatedQueryValidator.cs @@ -0,0 +1,22 @@ +namespace Goodtocode.SemanticKernel.Core.Application.ChatCompletion; + +public class GetMyChatSessionsPaginatedQueryValidator : Validator +{ + public GetMyChatSessionsPaginatedQueryValidator() + { + RuleFor(x => x.UserInfo).NotEmpty(); + + RuleFor(v => v.StartDate).NotEmpty() + .When(v => v.EndDate != null) + .LessThanOrEqualTo(v => v.EndDate); + + RuleFor(v => v.EndDate) + .NotEmpty() + .When(v => v.StartDate != null) + .GreaterThanOrEqualTo(v => v.StartDate); + + RuleFor(x => x.PageNumber).NotEqual(0); + + RuleFor(x => x.PageSize).NotEqual(0); + } +} \ No newline at end of file diff --git a/src/Core.Application/ChatCompletion/GetMyChatSessionsQuery.cs b/src/Core.Application/ChatCompletion/GetMyChatSessionsQuery.cs new file mode 100644 index 0000000..fcc45b3 --- /dev/null +++ b/src/Core.Application/ChatCompletion/GetMyChatSessionsQuery.cs @@ -0,0 +1,34 @@ +using Goodtocode.SemanticKernel.Core.Application.Abstractions; +using Goodtocode.SemanticKernel.Core.Domain.Auth; + +namespace Goodtocode.SemanticKernel.Core.Application.ChatCompletion; + +public class GetMyChatSessionsQuery : IRequest>, IUserInfoRequest +{ + public DateTime? StartDate { get; set; } + public DateTime? EndDate { get; set; } + public IUserEntity? UserInfo { get; set; } +} + +public class GetAuthorChatSessionsByOwnerIdQueryHandler(ISemanticKernelContext context) : IRequestHandler> +{ + private readonly ISemanticKernelContext _context = context; + + public async Task> Handle(GetMyChatSessionsQuery request, CancellationToken cancellationToken) + { + var returnData = await _context.ChatSessions + .OrderByDescending(x => x.Timestamp) + .Where(x => (request.StartDate == null || x.Timestamp > request.StartDate) + && (request.EndDate == null || x.Timestamp < request.EndDate)) + .Join(_context.Actors, + cs => cs.ActorId, + a => a.Id, + (cs, a) => new { ChatSession = cs, Actor = a }) + .Where(joined => joined.Actor.OwnerId == request.UserInfo!.OwnerId) + .Select(joined => joined.ChatSession) + .Select(x => ChatSessionDto.CreateFrom(x)) + .ToListAsync(cancellationToken); + + return returnData; + } +} \ No newline at end of file diff --git a/src/Core.Application/ChatCompletion/GetMyChatSessionsQueryValidator.cs b/src/Core.Application/ChatCompletion/GetMyChatSessionsQueryValidator.cs new file mode 100644 index 0000000..b86bd2c --- /dev/null +++ b/src/Core.Application/ChatCompletion/GetMyChatSessionsQueryValidator.cs @@ -0,0 +1,18 @@ +namespace Goodtocode.SemanticKernel.Core.Application.ChatCompletion; + +public class GetMyChatSessionsQueryValidator : Validator +{ + public GetMyChatSessionsQueryValidator() + { + RuleFor(x => x.UserInfo).NotEmpty(); + + RuleFor(v => v.StartDate).NotEmpty() + .When(v => v.EndDate != null) + .LessThanOrEqualTo(v => v.EndDate); + + RuleFor(v => v.EndDate) + .NotEmpty() + .When(v => v.StartDate != null) + .GreaterThanOrEqualTo(v => v.StartDate); + } +} \ No newline at end of file diff --git a/src/Core.Application/ChatCompletion/PatchChatSessionCommand.cs b/src/Core.Application/ChatCompletion/PatchChatSessionCommand.cs index 715fd47..053e64d 100644 --- a/src/Core.Application/ChatCompletion/PatchChatSessionCommand.cs +++ b/src/Core.Application/ChatCompletion/PatchChatSessionCommand.cs @@ -21,7 +21,7 @@ public async Task Handle(PatchChatSessionCommand request, CancellationToken canc GuardAgainstNotFound(chatSession); GuardAgainstEmptyTitle(request.Title); - chatSession!.Title = request.Title; + chatSession!.Update(request.Title); _context.ChatSessions.Update(chatSession); await _context.SaveChangesAsync(cancellationToken); diff --git a/src/Core.Application/Common/Behaviors/CustomLoggingBehavior.cs b/src/Core.Application/Common/Behaviors/CustomLoggingBehavior.cs index bce2a8b..e036164 100644 --- a/src/Core.Application/Common/Behaviors/CustomLoggingBehavior.cs +++ b/src/Core.Application/Common/Behaviors/CustomLoggingBehavior.cs @@ -10,7 +10,6 @@ public async Task Process(TRequest request, CancellationToken cancellationToken) { var requestName = typeof(TRequest).Name; - await Task.Run(() => _logger.LogInformation("Request: {Name}", - requestName), cancellationToken); + await Task.Run(() => _logger.LogRequest(requestName), cancellationToken); } } \ No newline at end of file diff --git a/src/Core.Application/Common/Behaviors/CustomPerformanceBehavior.cs b/src/Core.Application/Common/Behaviors/CustomPerformanceBehavior.cs index 6baf9bf..c2caa67 100644 --- a/src/Core.Application/Common/Behaviors/CustomPerformanceBehavior.cs +++ b/src/Core.Application/Common/Behaviors/CustomPerformanceBehavior.cs @@ -1,5 +1,5 @@ -using System.Diagnostics; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; +using System.Diagnostics; namespace Goodtocode.SemanticKernel.Core.Application.Common.Behaviors; @@ -9,12 +9,7 @@ public class CustomPerformanceBehavior( private readonly Stopwatch _timer = new(); private readonly ILogger _logger = logger; - public Task Handle(TRequest request, RequestDelegateInvoker nextInvoker, CancellationToken cancellationToken) - { - return Handle(request, nextInvoker, _logger, cancellationToken); - } - - public async Task Handle(TRequest request, RequestDelegateInvoker nextInvoker, ILogger logger, CancellationToken cancellationToken) + public async Task Handle(TRequest request, RequestDelegateInvoker nextInvoker, CancellationToken cancellationToken) { _timer.Start(); @@ -27,8 +22,7 @@ public async Task Handle(TRequest request, RequestDelegateInvoker 500) { var requestName = typeof(TRequest).Name; - await Task.Run(() => - logger.LogWarning("Long Running Request: {Name} ({ElapsedMilliseconds} milliseconds)", requestName, elapsedMilliseconds), cancellationToken); + await Task.Run(() => _logger.LogLongRunningRequest(requestName, elapsedMilliseconds), cancellationToken); } return response; @@ -41,12 +35,7 @@ public class CustomPerformanceBehavior( private readonly Stopwatch _timer = new(); private readonly ILogger _logger = logger; - public Task Handle(TRequest request, RequestDelegateInvoker nextInvoker, CancellationToken cancellationToken) - { - return Handle(request, nextInvoker, _logger, cancellationToken); - } - - public async Task Handle(TRequest request, RequestDelegateInvoker nextInvoker, ILogger logger, CancellationToken cancellationToken) + public async Task Handle(TRequest request, RequestDelegateInvoker nextInvoker, CancellationToken cancellationToken) { _timer.Start(); @@ -59,8 +48,7 @@ public async Task Handle(TRequest request, RequestDelegateInvoker nextInvoker, I if (elapsedMilliseconds > 500) { var requestName = typeof(TRequest).Name; - await Task.Run(() => - logger.LogWarning("Long Running Request: {Name} ({ElapsedMilliseconds} milliseconds)", requestName, elapsedMilliseconds), cancellationToken); + await Task.Run(() => _logger.LogLongRunningRequest(requestName, elapsedMilliseconds), cancellationToken); } } -} +} \ No newline at end of file diff --git a/src/Core.Application/Common/Behaviors/CustomUnhandledExceptionBehavior.cs b/src/Core.Application/Common/Behaviors/CustomUnhandledExceptionBehavior.cs index 5ba62a6..2658243 100644 --- a/src/Core.Application/Common/Behaviors/CustomUnhandledExceptionBehavior.cs +++ b/src/Core.Application/Common/Behaviors/CustomUnhandledExceptionBehavior.cs @@ -15,10 +15,7 @@ public async Task Handle(TRequest request, RequestDelegateInvoker logger.LogError(ex, "Request: Unhandled Exception for Request {Name}", requestName), cancellationToken); - + await Task.Run(() => logger.LogUnhandledException(ex, requestName), cancellationToken); throw; } } @@ -37,10 +34,7 @@ public async Task Handle(TRequest request, RequestDelegateInvoker nextInvoker, C catch (Exception ex) { var requestName = typeof(TRequest).Name; - - await Task.Run(() - => logger.LogError(ex, "Request: Unhandled Exception for Request {Name}", requestName), cancellationToken); - + await Task.Run(() => logger.LogUnhandledException(ex, requestName), cancellationToken); throw; } } diff --git a/src/Core.Application/Common/CustomLoggerExtensions.cs b/src/Core.Application/Common/CustomLoggerExtensions.cs new file mode 100644 index 0000000..2de4241 --- /dev/null +++ b/src/Core.Application/Common/CustomLoggerExtensions.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.Logging; + +namespace Goodtocode.SemanticKernel.Core.Application.Common +{ + public static partial class CustomLoggingBehaviorExtensions + { + [LoggerMessage(EventId = 100, Level = LogLevel.Information, Message = "Request: {Name}")] + public static partial void LogRequest(this ILogger logger, string name); + + [LoggerMessage(EventId = 101, Level = LogLevel.Warning, Message = "Long Running Request: {Name} ({ElapsedMilliseconds} milliseconds)")] + public static partial void LogLongRunningRequest(this ILogger logger, string name, long elapsedMilliseconds); + + [LoggerMessage(EventId = 102, Level = LogLevel.Error, Message = "Request: Unhandled Exception for Request {Name}")] + public static partial void LogUnhandledException(this ILogger logger, Exception exception, string name); + } +} diff --git a/src/Core.Application/Common/Exceptions/CustomConflictException.cs b/src/Core.Application/Common/Exceptions/CustomConflictException.cs new file mode 100644 index 0000000..04bec48 --- /dev/null +++ b/src/Core.Application/Common/Exceptions/CustomConflictException.cs @@ -0,0 +1,23 @@ +namespace Goodtocode.SemanticKernel.Core.Application.Common.Exceptions; + +public class CustomConflictException : Exception +{ + public CustomConflictException() + { + } + + public CustomConflictException(string message) + : base(message) + { + } + + public CustomConflictException(string message, Exception innerException) + : base(message, innerException) + { + } + + public CustomConflictException(string name, object id) + : base($"Entity \"{name}\" ({id}) conflicts with an existing entity.") + { + } +} \ No newline at end of file diff --git a/src/Core.Application/Common/Exceptions/CustomForbiddenAccessException.cs b/src/Core.Application/Common/Exceptions/CustomForbiddenAccessException.cs index 7aedb55..69c2510 100644 --- a/src/Core.Application/Common/Exceptions/CustomForbiddenAccessException.cs +++ b/src/Core.Application/Common/Exceptions/CustomForbiddenAccessException.cs @@ -2,5 +2,22 @@ public class CustomForbiddenAccessException : Exception { - public CustomForbiddenAccessException() : base() { } -} + public CustomForbiddenAccessException() + { + } + + public CustomForbiddenAccessException(string message) + : base(message) + { + } + + public CustomForbiddenAccessException(string message, Exception innerException) + : base(message, innerException) + { + } + + public CustomForbiddenAccessException(string name, object id) + : base($"Entity \"{name}\" ({id}) was not found.") + { + } +} \ No newline at end of file diff --git a/src/Core.Application/Core.Application.csproj b/src/Core.Application/Core.Application.csproj index b981abf..a357aaa 100644 --- a/src/Core.Application/Core.Application.csproj +++ b/src/Core.Application/Core.Application.csproj @@ -4,19 +4,18 @@ Goodtocode.SemanticKernel.Core.Application Goodtocode.SemanticKernel.Core.Application 1.0.0 - net9.0 + net10.0 false enable enable - - - + + + + - - \ No newline at end of file diff --git a/src/Core.Application/Image/CreateTextToImageCommand.cs b/src/Core.Application/Image/CreateTextToImageCommand.cs index dafc2e1..fbc6a2b 100644 --- a/src/Core.Application/Image/CreateTextToImageCommand.cs +++ b/src/Core.Application/Image/CreateTextToImageCommand.cs @@ -25,7 +25,7 @@ public class CreateTextToImageCommandHandler(Kernel kernel, ISemanticKernelConte public async Task Handle(CreateTextToImageCommand request, CancellationToken cancellationToken) { GuardAgainstEmptyPrompt(request?.Prompt); - GuardAgainstIdExsits(_context.TextImages, request!.Id); + GuardAgainstIdExists(_context.TextImages, request!.Id); var service = _kernel.GetRequiredService(); var response = await service.GenerateImageAsync(description: request.Prompt, width: request.Width, height: request.Height, cancellationToken: cancellationToken); @@ -48,13 +48,10 @@ private static void GuardAgainstEmptyPrompt(string? prompt) ]); } - private static void GuardAgainstIdExsits(DbSet dbSet, Guid id) + private static void GuardAgainstIdExists(DbSet dbSet, Guid id) { if (dbSet.Any(x => x.Id == id)) - throw new CustomValidationException( - [ - new("Id", "Id already exists") - ]); + throw new CustomConflictException("Id already exists"); } } #pragma warning restore SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. \ No newline at end of file diff --git a/src/Core.Application/Image/TextImageDto.cs b/src/Core.Application/Image/TextImageDto.cs index 39ddff0..115fd78 100644 --- a/src/Core.Application/Image/TextImageDto.cs +++ b/src/Core.Application/Image/TextImageDto.cs @@ -1,5 +1,4 @@ -using Goodtocode.SemanticKernel.Core.Application.Common.Mappings; -using Goodtocode.SemanticKernel.Core.Domain.Image; +using Goodtocode.SemanticKernel.Core.Domain.Image; namespace Goodtocode.SemanticKernel.Core.Application.Image; @@ -8,7 +7,7 @@ public class TextImageDto private int _width = 1024; private int _height = 1024; public Guid Id { get; set; } = Guid.Empty; - public Guid AuthorId { get; set; } = Guid.Empty; + public Guid ActorId { get; set; } = Guid.Empty; public string Description { get; set; } = string.Empty; public ReadOnlyMemory? ImageBytes { get; set; } public Uri? ImageUrl { get; set; } @@ -39,7 +38,7 @@ public static TextImageDto CreateFrom(TextImageEntity? entity) return new TextImageDto { Id = entity.Id, - AuthorId = entity.AuthorId, + ActorId = entity.ActorId, Description = entity.Description, ImageBytes = entity.ImageBytes, ImageUrl = entity.ImageUrl, diff --git a/src/Core.Application/TextGeneration/CreateTextPromptCommand.cs b/src/Core.Application/TextGeneration/CreateTextPromptCommand.cs index 75c45dd..64edf8a 100644 --- a/src/Core.Application/TextGeneration/CreateTextPromptCommand.cs +++ b/src/Core.Application/TextGeneration/CreateTextPromptCommand.cs @@ -20,7 +20,7 @@ public class CreateTextPromptCommandHandler(Kernel semanticKernel, ISemanticKern public async Task Handle(CreateTextPromptCommand request, CancellationToken cancellationToken) { GuardAgainstEmptyPrompt(request?.Prompt); - GuardAgainstIdExsits(_context.TextPrompts, request!.Id); + GuardAgainstIdExists(_context.TextPrompts, request!.Id); var service = _kernel.GetRequiredService(); var executionSettings = new PromptExecutionSettings @@ -32,7 +32,7 @@ public async Task Handle(CreateTextPromptCommand request, Cancell var textPrompt = TextPromptEntity.Create(request.Id, Guid.NewGuid(), request.Prompt!); foreach (var response in responses) { - textPrompt.TextResponses.Add(TextResponseEntity.Create(Guid.NewGuid(), textPrompt.Id, response.ToString())); + _context.TextResponses.Add(TextResponseEntity.Create(Guid.NewGuid(), textPrompt.Id, response.ToString())); } _context.TextPrompts.Add(textPrompt); await _context.SaveChangesAsync(cancellationToken); @@ -49,12 +49,9 @@ private static void GuardAgainstEmptyPrompt(string? prompt) ]); } - private static void GuardAgainstIdExsits(DbSet dbSet, Guid id) + private static void GuardAgainstIdExists(DbSet dbSet, Guid id) { if (dbSet.Any(x => x.Id == id)) - throw new CustomValidationException( - [ - new("Id", "Id already exists") - ]); + throw new CustomConflictException("Id already exists"); } } \ No newline at end of file diff --git a/src/Core.Application/TextGeneration/TextPromptDto.cs b/src/Core.Application/TextGeneration/TextPromptDto.cs index bdabdcd..cfcbf21 100644 --- a/src/Core.Application/TextGeneration/TextPromptDto.cs +++ b/src/Core.Application/TextGeneration/TextPromptDto.cs @@ -1,15 +1,13 @@ -using Goodtocode.SemanticKernel.Core.Application.Common.Mappings; -using Goodtocode.SemanticKernel.Core.Domain.TextGeneration; +using Goodtocode.SemanticKernel.Core.Domain.TextGeneration; namespace Goodtocode.SemanticKernel.Core.Application.TextGeneration; public class TextPromptDto { public Guid Id { get; set; } = Guid.Empty; - public Guid AuthorId { get; set; } = Guid.Empty; + public Guid ActorId { get; set; } = Guid.Empty; public string Prompt { get; set; } = string.Empty; public DateTimeOffset Timestamp { get; set; } - public ICollection? Responses { get; set; } public static TextPromptDto CreateFrom(TextPromptEntity? entity) { @@ -17,12 +15,9 @@ public static TextPromptDto CreateFrom(TextPromptEntity? entity) return new TextPromptDto { Id = entity.Id, - AuthorId = entity.AuthorId, + ActorId = entity.ActorId, Prompt = entity.Prompt, Timestamp = entity.Timestamp, - Responses = entity.TextResponses? - .Select(TextResponseDto.CreateFrom) - .ToList() }; } } diff --git a/src/Core.Application/TextGeneration/TextResponseDto.cs b/src/Core.Application/TextGeneration/TextResponseDto.cs index 6b3092d..9b65d9e 100644 --- a/src/Core.Application/TextGeneration/TextResponseDto.cs +++ b/src/Core.Application/TextGeneration/TextResponseDto.cs @@ -1,4 +1,3 @@ -using Goodtocode.SemanticKernel.Core.Application.Common.Mappings; using Goodtocode.SemanticKernel.Core.Domain.TextGeneration; namespace Goodtocode.SemanticKernel.Core.Application.TextGeneration; diff --git a/src/Core.Domain/Actor/ActorEntity.cs b/src/Core.Domain/Actor/ActorEntity.cs new file mode 100644 index 0000000..ea719e1 --- /dev/null +++ b/src/Core.Domain/Actor/ActorEntity.cs @@ -0,0 +1,46 @@ +using Goodtocode.SemanticKernel.Core.Domain.Auth; +using Goodtocode.Domain.Entities; + +namespace Goodtocode.SemanticKernel.Core.Domain.Actor; + +public class ActorEntity : SecuredEntity +{ + protected ActorEntity() { } + + public string? FirstName { get; private set; } = string.Empty; + public string? LastName { get; private set; } = string.Empty; + public string? Email { get; private set; } = string.Empty; + + public static ActorEntity Create(Guid id, Guid ownerId, Guid tenantId, string? firstName, string? lastName, string? email) + { + return new ActorEntity + { + Id = id == Guid.Empty ? Guid.NewGuid() : id, + OwnerId = ownerId, + TenantId = tenantId, + FirstName = firstName, + LastName = lastName, + Email = email + }; + } + + public static ActorEntity Create(IUserEntity userInfo) + { + return new ActorEntity + { + Id = Guid.NewGuid(), + OwnerId = userInfo.OwnerId, + TenantId = userInfo.TenantId, + FirstName = userInfo.FirstName, + LastName = userInfo.LastName, + Email = userInfo.Email + }; + } + + public void Update(string? firstName, string? lastName, string? email) + { + FirstName = firstName ?? FirstName; + LastName = lastName ?? LastName; + Email = email ?? Email; + } +} diff --git a/src/Core.Domain/Audio/TextAudioEntity.cs b/src/Core.Domain/Audio/TextAudioEntity.cs index d68b504..3710bc6 100644 --- a/src/Core.Domain/Audio/TextAudioEntity.cs +++ b/src/Core.Domain/Audio/TextAudioEntity.cs @@ -1,5 +1,5 @@ -using Goodtocode.Domain.Types.DomainEntity; -using Goodtocode.SemanticKernel.Core.Domain.Author; +using Goodtocode.Domain.Entities; +using Goodtocode.SemanticKernel.Core.Domain.Actor; namespace Goodtocode.SemanticKernel.Core.Domain.Audio; @@ -9,9 +9,9 @@ public class TextAudioEntity : DomainEntity protected TextAudioEntity() { } - public Guid AuthorId { get; set; } = Guid.Empty; + public Guid ActorId { get; private set; } = Guid.Empty; - public string Description { get; set; } = string.Empty; + public string Description { get; private set; } = string.Empty; public ReadOnlyMemory? AudioBytes { @@ -19,17 +19,17 @@ public ReadOnlyMemory? AudioBytes set => _audioBytes = value.HasValue ? value.Value.ToArray() : null; } - public Uri? AudioUrl { get; set; } + public Uri? AudioUrl { get; private set; } - public virtual AuthorEntity Author { get; set; } = default!; + public virtual ActorEntity Actor { get; private set; } = default!; public static TextAudioEntity Create(Guid id, Guid authorId, string description, ReadOnlyMemory? audioBytes) { - return TextAudioEntity.Create(id, authorId, description, audioBytes, null); + return Create(id, authorId, description, audioBytes, null); } public static TextAudioEntity Create(Guid id, Guid authorId, string description, Uri? audioUrl) { - return TextAudioEntity.Create(id, authorId, description, null, audioUrl); + return Create(id, authorId, description, null, audioUrl); } public static TextAudioEntity Create(Guid id, Guid authorId, string description, ReadOnlyMemory? audioBytes, Uri? audioUrl) @@ -37,7 +37,7 @@ public static TextAudioEntity Create(Guid id, Guid authorId, string description, return new TextAudioEntity { Id = id == Guid.Empty ? Guid.NewGuid() : id, - AuthorId = authorId, + ActorId = authorId, Description = description, AudioBytes = audioBytes, AudioUrl = audioUrl, diff --git a/src/Core.Domain/Auth/IUserEntity.cs b/src/Core.Domain/Auth/IUserEntity.cs new file mode 100644 index 0000000..1d5acb1 --- /dev/null +++ b/src/Core.Domain/Auth/IUserEntity.cs @@ -0,0 +1,14 @@ +namespace Goodtocode.SemanticKernel.Core.Domain.Auth; + +public interface IUserEntity +{ + Guid OwnerId { get; } + Guid TenantId { get; } + string FirstName { get; } + string LastName { get; } + string Email { get; } + IEnumerable Roles { get; } + bool CanView { get; } + bool CanEdit { get; } + bool CanDelete { get; } +} diff --git a/src/Core.Domain/Auth/UserEntity.cs b/src/Core.Domain/Auth/UserEntity.cs new file mode 100644 index 0000000..180d4a4 --- /dev/null +++ b/src/Core.Domain/Auth/UserEntity.cs @@ -0,0 +1,35 @@ +namespace Goodtocode.SemanticKernel.Core.Domain.Auth; + +public struct UserRoles +{ + public const string ChatOwner = "AssetOwner"; + public const string ChatEditor = "AssetEditor"; + public const string ChatViewer = "AssetViewer"; +} + +public class UserEntity() : IUserEntity +{ + public Guid OwnerId { get; private set; } + public Guid TenantId { get; private set; } + public string FirstName { get; private set; } = string.Empty; + public string LastName { get; private set; } = string.Empty; + public string Email { get; private set; } = string.Empty; + public IEnumerable Roles { get; private set; } = []; + public bool CanView => Roles.Contains(UserRoles.ChatOwner) || Roles.Contains(UserRoles.ChatEditor) || Roles.Contains(UserRoles.ChatViewer); + public bool CanEdit => Roles.Contains(UserRoles.ChatOwner) || Roles.Contains(UserRoles.ChatEditor); + public bool CanDelete => Roles.Contains(UserRoles.ChatOwner); + + public static UserEntity Create(Guid ownerId, Guid tenantId, string firstName, string lastName, string email, IEnumerable roles) + { + return new UserEntity + { + OwnerId = ownerId, + TenantId = tenantId, + FirstName = firstName, + LastName = lastName, + Email = email, + Roles = roles + }; + } + +} \ No newline at end of file diff --git a/src/Core.Domain/Author/AuthorEntity.cs b/src/Core.Domain/Author/AuthorEntity.cs deleted file mode 100644 index 83eecd9..0000000 --- a/src/Core.Domain/Author/AuthorEntity.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Goodtocode.Domain.Types.DomainEntity; -using Goodtocode.SemanticKernel.Core.Domain.Audio; -using Goodtocode.SemanticKernel.Core.Domain.ChatCompletion; -using Goodtocode.SemanticKernel.Core.Domain.Image; -using Goodtocode.SemanticKernel.Core.Domain.TextGeneration; - -namespace Goodtocode.SemanticKernel.Core.Domain.Author; - -public class AuthorEntity : DomainEntity -{ - protected AuthorEntity() { } - - public string? Name { get; set; } = string.Empty; - - public virtual ICollection ChatSessions { get; private set; } = []; - public virtual ICollection TextPrompts { get; private set; } = []; - public virtual ICollection TextImages { get; private set; } = []; - public virtual ICollection TextAudio { get; private set; } = []; - public static AuthorEntity Create(Guid id, string? name) - { - return new AuthorEntity - { - Id = id == Guid.Empty ? Guid.NewGuid() : id, - Name = name - }; - } -} diff --git a/src/Core.Domain/ChatCompletion/ChatMessageEntity.cs b/src/Core.Domain/ChatCompletion/ChatMessageEntity.cs index 924e2c9..9a7a036 100644 --- a/src/Core.Domain/ChatCompletion/ChatMessageEntity.cs +++ b/src/Core.Domain/ChatCompletion/ChatMessageEntity.cs @@ -1,14 +1,14 @@ -using Goodtocode.Domain.Types.DomainEntity; +using Goodtocode.Domain.Entities; namespace Goodtocode.SemanticKernel.Core.Domain.ChatCompletion; public class ChatMessageEntity : DomainEntity, IDomainEntity { protected ChatMessageEntity() { } - public Guid ChatSessionId { get; set; } - public ChatMessageRole Role { get; set; } - public string Content { get; set; } = string.Empty; - public virtual ChatSessionEntity? ChatSession { get; set; } + public Guid ChatSessionId { get; private set; } + public ChatMessageRole Role { get; private set; } + public string Content { get; private set; } = string.Empty; + public virtual ChatSessionEntity? ChatSession { get; private set; } public static ChatMessageEntity Create(Guid id, Guid chatSessionId, ChatMessageRole role, string content) { return new ChatMessageEntity diff --git a/src/Core.Domain/ChatCompletion/ChatMessageRole.cs b/src/Core.Domain/ChatCompletion/ChatMessageRoles.cs similarity index 100% rename from src/Core.Domain/ChatCompletion/ChatMessageRole.cs rename to src/Core.Domain/ChatCompletion/ChatMessageRoles.cs diff --git a/src/Core.Domain/ChatCompletion/ChatSessionEntity.cs b/src/Core.Domain/ChatCompletion/ChatSessionEntity.cs index b5726fc..143e0a1 100644 --- a/src/Core.Domain/ChatCompletion/ChatSessionEntity.cs +++ b/src/Core.Domain/ChatCompletion/ChatSessionEntity.cs @@ -1,5 +1,5 @@ -using Goodtocode.Domain.Types.DomainEntity; -using Goodtocode.SemanticKernel.Core.Domain.Author; +using Goodtocode.Domain.Entities; +using Goodtocode.SemanticKernel.Core.Domain.Actor; namespace Goodtocode.SemanticKernel.Core.Domain.ChatCompletion; @@ -7,17 +7,17 @@ public class ChatSessionEntity : DomainEntity { protected ChatSessionEntity() { } - public Guid AuthorId { get; set; } - public string? Title { get; set; } = string.Empty; - public virtual ICollection Messages { get; set; } = []; - public virtual AuthorEntity? Author { get; set; } + public Guid ActorId { get; private set; } + public string? Title { get; private set; } = string.Empty; + public virtual ICollection Messages { get; private set; } = []; + public virtual ActorEntity? Actor { get; private set; } public static ChatSessionEntity Create(Guid id, Guid authorId, string? title, ChatMessageRole responseRole, string initialMessage, string responseMessage) { var session = new ChatSessionEntity { Id = id == Guid.Empty ? Guid.NewGuid() : id, - AuthorId = authorId, + ActorId = authorId, Title = title, Timestamp = DateTime.UtcNow }; @@ -25,4 +25,9 @@ public static ChatSessionEntity Create(Guid id, Guid authorId, string? title, Ch session.Messages.Add(ChatMessageEntity.Create(Guid.NewGuid(), session.Id, responseRole, responseMessage)); return session; } + + public void Update(string? title) + { + Title = title ?? Title; + } } diff --git a/src/Core.Domain/Core.Domain.csproj b/src/Core.Domain/Core.Domain.csproj index 818f228..4267a16 100644 --- a/src/Core.Domain/Core.Domain.csproj +++ b/src/Core.Domain/Core.Domain.csproj @@ -4,7 +4,7 @@ Goodtocode.SemanticKernel.Core.Domain Goodtocode.SemanticKernel.Core.Domain 1.0.0 - net9.0 + net10.0 false enable enable @@ -12,9 +12,6 @@ - - - - + diff --git a/src/Core.Domain/GlobalUsings.cs b/src/Core.Domain/GlobalUsings.cs index c4557a2..e69de29 100644 --- a/src/Core.Domain/GlobalUsings.cs +++ b/src/Core.Domain/GlobalUsings.cs @@ -1 +0,0 @@ -global using CSharpFunctionalExtensions; \ No newline at end of file diff --git a/src/Core.Domain/Image/TextImageEntity.cs b/src/Core.Domain/Image/TextImageEntity.cs index 52a1b2c..77e0169 100644 --- a/src/Core.Domain/Image/TextImageEntity.cs +++ b/src/Core.Domain/Image/TextImageEntity.cs @@ -1,5 +1,5 @@ -using Goodtocode.Domain.Types.DomainEntity; -using Goodtocode.SemanticKernel.Core.Domain.Author; +using Goodtocode.Domain.Entities; +using Goodtocode.SemanticKernel.Core.Domain.Actor; namespace Goodtocode.SemanticKernel.Core.Domain.Image; @@ -10,10 +10,10 @@ public class TextImageEntity : DomainEntity protected TextImageEntity() { } - public Guid AuthorId { get; set; } = Guid.Empty; - public string Description { get; set; } = string.Empty; - public ReadOnlyMemory? ImageBytes { get; set; } - public Uri? ImageUrl { get; set; } + public Guid ActorId { get; private set; } = Guid.Empty; + public string Description { get; private set; } = string.Empty; + public ReadOnlyMemory? ImageBytes { get; private set; } + public Uri? ImageUrl { get; private set; } public int Height { get => _height; @@ -32,7 +32,7 @@ public int Width _ => throw new ArgumentOutOfRangeException("Width", "Must be 1024.") }; } - public virtual AuthorEntity? Author { get; set; } + public virtual ActorEntity? Actor { get; set; } public static TextImageEntity Create( Guid id, @@ -41,7 +41,7 @@ public static TextImageEntity Create( int height, ReadOnlyMemory? imageBytes) { - return TextImageEntity.Create(id, description, width, height, imageBytes, null); + return Create(id, description, width, height, imageBytes, null); } public static TextImageEntity Create( @@ -51,7 +51,7 @@ public static TextImageEntity Create( int height, Uri? imageUrl) { - return TextImageEntity.Create(id, description, width, height, null, imageUrl); + return Create(id, description, width, height, null, imageUrl); } public static TextImageEntity Create( diff --git a/src/Core.Domain/TextGeneration/TextPromptEntity.cs b/src/Core.Domain/TextGeneration/TextPromptEntity.cs index c07529b..28d7c80 100644 --- a/src/Core.Domain/TextGeneration/TextPromptEntity.cs +++ b/src/Core.Domain/TextGeneration/TextPromptEntity.cs @@ -1,5 +1,5 @@ -using Goodtocode.Domain.Types.DomainEntity; -using Goodtocode.SemanticKernel.Core.Domain.Author; +using Goodtocode.Domain.Entities; +using Goodtocode.SemanticKernel.Core.Domain.Actor; namespace Goodtocode.SemanticKernel.Core.Domain.TextGeneration; @@ -7,17 +7,16 @@ public class TextPromptEntity : DomainEntity { protected TextPromptEntity() { } - public Guid AuthorId { get; set; } = Guid.Empty; - public string Prompt { get; set; } = string.Empty; - public virtual ICollection TextResponses { get; set; } = []; - public virtual AuthorEntity? Author { get; set; } + public Guid ActorId { get; private set; } = Guid.Empty; + public string Prompt { get; private set; } = string.Empty; + public virtual ActorEntity? Actor { get; private set; } public static TextPromptEntity Create(Guid id, Guid authorId, string prompt) { return new TextPromptEntity { Id = id == Guid.Empty ? Guid.NewGuid() : id, - AuthorId = authorId, + ActorId = authorId, Prompt = prompt, Timestamp = DateTime.UtcNow }; diff --git a/src/Core.Domain/TextGeneration/TextResponseEntity.cs b/src/Core.Domain/TextGeneration/TextResponseEntity.cs index fe6e10b..68ad9c8 100644 --- a/src/Core.Domain/TextGeneration/TextResponseEntity.cs +++ b/src/Core.Domain/TextGeneration/TextResponseEntity.cs @@ -1,4 +1,4 @@ -using Goodtocode.Domain.Types.DomainEntity; +using Goodtocode.Domain.Entities; namespace Goodtocode.SemanticKernel.Core.Domain.TextGeneration; @@ -6,9 +6,9 @@ public class TextResponseEntity : DomainEntity { protected TextResponseEntity() { } - public Guid TextPromptId { get; set; } = Guid.Empty; - public string Response { get; set; } = string.Empty; - public virtual TextPromptEntity? TextPrompt { get; set; } + public Guid TextPromptId { get; private set; } = Guid.Empty; + public string Response { get; private set; } = string.Empty; + public virtual TextPromptEntity? TextPrompt { get; private set; } public static TextResponseEntity Create(Guid id, Guid textPromptId, string response) { diff --git a/src/Get-CodeCoverage.ps1 b/src/Get-CodeCoverage.ps1 index 9f7ec78..5df8c52 100644 --- a/src/Get-CodeCoverage.ps1 +++ b/src/Get-CodeCoverage.ps1 @@ -10,12 +10,12 @@ #################################################################################### Param( - [string]$TestProjectFilter = '*.Specs.*.csproj', + [string]$TestProjectFilter = 'Tests.*.csproj', [switch]$ProdPackagesOnly = $false, [string[]]$ProductionAssemblies = @( - "Goodtocode.SemanticKernel.Core.Application", - "Goodtocode.SemanticKernel.Presentation.WebApi", - "Goodtocode.SemanticKernel.Presentation.Blazor" + "Cannery.Insights.Core.Application", + "Cannery.Insights.Presentation.WebApi", + "Cannery.Insights.Presentation.Blazor" ) ) #################################################################################### @@ -34,20 +34,13 @@ $reportOutputPath = Join-Path $scriptPath "TestResults\Reports\$timestamp" New-Item -ItemType Directory -Force -Path $coverageOutputPath New-Item -ItemType Directory -Force -Path $reportOutputPath -$solutionFile = Get-ChildItem -Path $scriptPath -Filter *.sln -Recurse | Select-Object -First 1 -if ($null -eq $solutionFile) { - Write-Host "No solution file found. Exiting." - exit 1 -} -Write-Host "Building solution: $($solutionFile.FullName)" -dotnet build $solutionFile.FullName - +# Find tests for projects with 'Tests.*.csproj' $testProjects = Get-ChildItem $scriptPath -Filter $TestProjectFilter -Recurse Write-Host "Found $($testProjects.Count) test projects." foreach ($project in $testProjects) { $testProjectPath = $project.FullName Write-Host "Running tests for project: $($testProjectPath)" - dotnet test $testProjectPath --no-build + $buildOutput = Join-Path -Path $project.Directory.FullName -ChildPath "bin\Debug\net9.0\$($project.BaseName).dll" $coverageFile = Join-Path $coverageOutputPath "coverage.cobertura.xml" Write-Host "Analyzing code coverage for: $buildOutput" @@ -55,6 +48,7 @@ foreach ($project in $testProjects) { } +# Generate HTML report if ($ProdPackagesOnly) { $assemblyFilters = ($ProductionAssemblies | ForEach-Object { "+$_" }) -join ";" $assemblyFilters = ($ProductionAssemblies | ForEach-Object { "+$_" }) -join ";" diff --git a/src/Infrastructure.SemanticKernel/AiModels/OpenAiModels.cs b/src/Infrastructure.SemanticKernel/AiModels/OpenAiModels.cs index 8bc9262..bf6160c 100644 --- a/src/Infrastructure.SemanticKernel/AiModels/OpenAiModels.cs +++ b/src/Infrastructure.SemanticKernel/AiModels/OpenAiModels.cs @@ -4,10 +4,8 @@ public struct OpenAiModels { public struct ChatCompletion { - public const string ChatGpt4 = "gpt-4.1"; - public const string ChatGpt4o = "gpt-4o"; - public const string ChatGpt4oMini = "gpt-4.1-mini"; - public const string ChatGpt4oNano = "gpt-4.1-nano"; + public const string ChatGpt4 = "gpt-4"; + public const string ChatGpt4Turbo = "gpt-4-turbo"; public const string ChatGpt35Turbo = "gpt-3.5-turbo"; } public struct TextGeneration @@ -31,7 +29,6 @@ public struct Image public struct Audio { public const string TextToSpeech1 = "tts-1"; - public const string TextToSpeech1HD = "tts-1-hd"; public const string Whisper1 = "whisper-1"; public const string Whisper2 = "whisper-2"; } diff --git a/src/Infrastructure.SemanticKernel/ConfigureServices.cs b/src/Infrastructure.SemanticKernel/ConfigureServices.cs index 16c770d..7a38479 100644 --- a/src/Infrastructure.SemanticKernel/ConfigureServices.cs +++ b/src/Infrastructure.SemanticKernel/ConfigureServices.cs @@ -36,7 +36,7 @@ public static IServiceCollection AddSemanticKernelOpenAIServices(this IServiceCo .ValidateOnStart(); // Plugins - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -99,11 +99,11 @@ public static IServiceCollection AddSemanticKernelOpenAIServices(this IServiceCo var kernel = builder.Build(); - var authorsPlugin = sp.GetRequiredService(); + var authorsPlugin = sp.GetRequiredService(); var chatSessionsPlugin = sp.GetRequiredService(); var chatMessagesPlugin = sp.GetRequiredService(); - kernel.ImportPluginFromObject(authorsPlugin, nameof(AuthorsPlugin)); + kernel.ImportPluginFromObject(authorsPlugin, nameof(ActorsPlugin)); kernel.ImportPluginFromObject(chatSessionsPlugin, nameof(ChatSessionsPlugin)); kernel.ImportPluginFromObject(chatMessagesPlugin, nameof(ChatMessagesPlugin)); diff --git a/src/Infrastructure.SemanticKernel/Infrastructure.SemanticKernel.csproj b/src/Infrastructure.SemanticKernel/Infrastructure.SemanticKernel.csproj index 650ee4e..1144b5f 100644 --- a/src/Infrastructure.SemanticKernel/Infrastructure.SemanticKernel.csproj +++ b/src/Infrastructure.SemanticKernel/Infrastructure.SemanticKernel.csproj @@ -4,20 +4,20 @@ Goodtocode.SemanticKernel.Infrastructure.SemanticKernel Goodtocode.SemanticKernel.Infrastructure.SemanticKernel 1.0.0 - net9.0 + net10.0 false enable enable - - - - - - - + + + + + + + diff --git a/src/Infrastructure.SemanticKernel/Plugins/ActorsPlugin.cs b/src/Infrastructure.SemanticKernel/Plugins/ActorsPlugin.cs new file mode 100644 index 0000000..b3b7eb1 --- /dev/null +++ b/src/Infrastructure.SemanticKernel/Plugins/ActorsPlugin.cs @@ -0,0 +1,124 @@ +using Goodtocode.SemanticKernel.Core.Application.Abstractions; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.SemanticKernel; +using System.ComponentModel; + +namespace Goodtocode.SemanticKernel.Infrastructure.SemanticKernel.Plugins; + +public class ActorResponse : IActorResponse +{ + public Guid ActorId { get; set; } + public string? Name { get; set; } + public string Status { get; set; } = string.Empty; + public string? Message { get; set; } +} + + +public sealed class ActorsPlugin(IServiceProvider serviceProvider) : IActorsPlugin +{ + private readonly IServiceProvider _serviceProvider = serviceProvider; + + public string PluginName => "AuthorsPlugin"; + public string FunctionName => _currentFunctionName; + public Dictionary Parameters => _currentParameters; + + private string _currentFunctionName = string.Empty; + private Dictionary _currentParameters = []; + + [KernelFunction("get_actor_by_id")] + [Description("Returns structured actor info by ID including name, status, and explanation.")] + public async Task GetActorByIdAsync(Guid actorId, CancellationToken cancellationToken) + { + _currentFunctionName = "get_actor_by_id"; + _currentParameters = new() + { + { "actorId", actorId } + }; + + using var scope = _serviceProvider.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + var actor = await context.Actors.FindAsync([actorId, cancellationToken], cancellationToken: cancellationToken); + + if (actor == null) + { + return new ActorResponse + { + ActorId = actorId, + Name = null, + Status = "NotFound", + Message = "No actor found with the specified ID." + }; + } + + return new ActorResponse + { + ActorId = actorId, + Name = $"{actor.FirstName} {actor.LastName}", + Status = string.IsNullOrWhiteSpace($"{actor.FirstName} {actor.LastName}") ? "Partial" : "Found", + Message = string.IsNullOrWhiteSpace($"{actor.FirstName} {actor.LastName}") + ? "Actor exists but name is not yet linked to Entra External ID." + : "Actor found." + }; + } + + [KernelFunction("get_actors_by_name")] + [Description("Returns structured actor info by name including ID, status, and explanation.")] + public async Task> GetActorsByNameAsync(string name, CancellationToken cancellationToken) + { + _currentFunctionName = "get_actors_by_name"; + _currentParameters = new() + { + { "name", name } + }; + + using var scope = _serviceProvider.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + var nameTokens = name?.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) ?? []; + var normalizedInput = name?.Trim() ?? string.Empty; + + var authors = await context.Actors + .Where(a => + nameTokens.Any(token => + EF.Functions.Like(a.FirstName, $"%{token}%") || + EF.Functions.Like(a.LastName, $"%{token}%") + ) + || EF.Functions.Like( + (a.FirstName + " " + a.LastName).Trim(), $"%{normalizedInput}%" + ) + || EF.Functions.Like( + (a.LastName + " " + a.FirstName).Trim(), $"%{normalizedInput}%" + ) + || nameTokens.Any(token => + EF.Functions.Like(a.FirstName, $"{token}%") || + EF.Functions.Like(a.FirstName, $"%{token}") || + EF.Functions.Like(a.LastName, $"{token}%") || + EF.Functions.Like(a.LastName, $"%{token}") + ) + ) + .ToListAsync(cancellationToken); + + if (authors.Count == 0) + { + return [ new ActorResponse + { + ActorId = Guid.Empty, + Name = name, + Status = "NotFound", + Message = "No actor found with the specified name." + } ]; + } + else + { + return [.. authors.Select(a => new ActorResponse + { + ActorId = a.Id, + Name = $"{a.FirstName} {a.LastName}", + Status = string.IsNullOrWhiteSpace($"{a.FirstName} {a.LastName}") ? "Partial" : "Found", + Message = string.IsNullOrWhiteSpace($"{a.FirstName} {a.LastName}") + ? "Actor exists but name is not yet linked to Entra External ID." + : "Actor found." + })]; + } + } +} diff --git a/src/Infrastructure.SemanticKernel/Plugins/AuthorsPlugin.cs b/src/Infrastructure.SemanticKernel/Plugins/AuthorsPlugin.cs deleted file mode 100644 index 66b3564..0000000 --- a/src/Infrastructure.SemanticKernel/Plugins/AuthorsPlugin.cs +++ /dev/null @@ -1,104 +0,0 @@ -using Goodtocode.SemanticKernel.Core.Application.Abstractions; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.SemanticKernel; -using System.ComponentModel; - -namespace Goodtocode.SemanticKernel.Infrastructure.SemanticKernel.Plugins; - -public class AuthorResponse : IAuthorResponse -{ - public Guid AuthorId { get; set; } - public string? Name { get; set; } - public string? Status { get; set; } - public string? Message { get; set; } -} - - -public sealed class AuthorsPlugin(IServiceProvider serviceProvider) : IAuthorsPlugin -{ - private readonly IServiceProvider _serviceProvider = serviceProvider; - - public string PluginName => "AuthorsPlugin"; - public string FunctionName => _currentFunctionName; - public Dictionary Parameters => _currentParameters; - - private string _currentFunctionName = string.Empty; - private Dictionary _currentParameters = new(); - - [KernelFunction("get_author_by_id")] - [Description("Returns structured author info by ID including name, status, and explanation.")] - public async Task GetAuthorByIdAsync(Guid authorId, CancellationToken cancellationToken) - { - _currentFunctionName = "get_author_by_id"; - _currentParameters = new() - { - { "authorId", authorId } - }; - - using var scope = _serviceProvider.CreateScope(); - var context = scope.ServiceProvider.GetRequiredService(); - var author = await context.Authors.FindAsync([authorId, cancellationToken], cancellationToken: cancellationToken); - - if (author == null) - { - return new AuthorResponse - { - AuthorId = authorId, - Name = null, - Status = "NotFound", - Message = "No author found with the specified ID." - }; - } - - return new AuthorResponse - { - AuthorId = authorId, - Name = author.Name, - Status = string.IsNullOrWhiteSpace(author.Name) ? "Partial" : "Found", - Message = string.IsNullOrWhiteSpace(author.Name) - ? "Author exists but name is not yet linked to Entra External ID." - : "Author found." - }; - } - - [KernelFunction("get_authors_by_name")] - [Description("Returns structured author info by name including ID, status, and explanation.")] - public async Task> GetAuthorsByNameAsync(string name, CancellationToken cancellationToken) - { - _currentFunctionName = "get_authors_by_name"; - _currentParameters = new() - { - { "name", name } - }; - - using var scope = _serviceProvider.CreateScope(); - var context = scope.ServiceProvider.GetRequiredService(); - var authors = await context.Authors - .Where(x => x.Name != null && EF.Functions.Like(x.Name, $"%{name}%")) - .ToListAsync(cancellationToken); - - if (authors.Count == 0) - { - return [ new AuthorResponse - { - AuthorId = Guid.Empty, - Name = name, - Status = "NotFound", - Message = "No author found with the specified name." - } ]; - } - else - { - return [.. authors.Select(a => new AuthorResponse - { - AuthorId = a.Id, - Name = a.Name, - Status = string.IsNullOrWhiteSpace(a.Name) ? "Partial" : "Found", - Message = string.IsNullOrWhiteSpace(a.Name) - ? "Author exists but name is not yet linked to Entra External ID." - : "Author found." - })]; - } - } -} diff --git a/src/Infrastructure.SemanticKernel/Plugins/ChatMessagesPlugin.cs b/src/Infrastructure.SemanticKernel/Plugins/ChatMessagesPlugin.cs index 2708a6f..1746a86 100644 --- a/src/Infrastructure.SemanticKernel/Plugins/ChatMessagesPlugin.cs +++ b/src/Infrastructure.SemanticKernel/Plugins/ChatMessagesPlugin.cs @@ -6,8 +6,7 @@ namespace Goodtocode.SemanticKernel.Infrastructure.SemanticKernel.Plugins; -public sealed class ChatMessagesPlugin(IServiceProvider serviceProvider) - : IChatMessagesPlugin, ISemanticPluginCompatible +public sealed class ChatMessagesPlugin(IServiceProvider serviceProvider) : IChatMessagesPlugin { private readonly IServiceProvider _serviceProvider = serviceProvider; @@ -25,10 +24,10 @@ public async Task> ListRecentMessagesAsync(DateTime? startDa { _currentFunctionName = "list_messages"; _currentParameters = new() - { - { "startDate", startDate ?? DateTime.UtcNow.AddDays(-7) }, - { "endDate", endDate ?? DateTime.UtcNow.AddSeconds(1)} - }; + { + { "startDate", startDate ?? DateTime.UtcNow.AddDays(-7) }, + { "endDate", endDate ?? DateTime.UtcNow.AddSeconds(1)} + }; using var scope = _serviceProvider.CreateScope(); var context = scope.ServiceProvider.GetRequiredService(); diff --git a/src/Infrastructure.SemanticKernel/Plugins/ChatSessionsPlugin.cs b/src/Infrastructure.SemanticKernel/Plugins/ChatSessionsPlugin.cs index 6a00cca..35af556 100644 --- a/src/Infrastructure.SemanticKernel/Plugins/ChatSessionsPlugin.cs +++ b/src/Infrastructure.SemanticKernel/Plugins/ChatSessionsPlugin.cs @@ -6,14 +6,7 @@ namespace Goodtocode.SemanticKernel.Infrastructure.SemanticKernel.Plugins; -public interface ISemanticPluginCompatible -{ - string PluginName { get; } - string FunctionName { get; } - Dictionary Parameters { get; } -} - -public sealed class ChatSessionsPlugin(IServiceProvider serviceProvider) : IChatSessionsPlugin, ISemanticPluginCompatible +public sealed class ChatSessionsPlugin(IServiceProvider serviceProvider) : IChatSessionsPlugin { private readonly IServiceProvider _serviceProvider = serviceProvider; @@ -22,7 +15,7 @@ public sealed class ChatSessionsPlugin(IServiceProvider serviceProvider) : IChat public Dictionary Parameters => _currentParameters; private string _currentFunctionName = string.Empty; - private Dictionary _currentParameters = new(); + private Dictionary _currentParameters = []; [KernelFunction("list_sessions")] [Description("Retrieves a list of recent chat sessions. Optionally, filter results by start and/or end date to narrow the search.")] @@ -74,10 +67,10 @@ public async Task UpdateChatSessionTitleAsync(Guid sessionId, string new return $"Session {sessionId} not found."; } - chatSession.Title = newTitle; + chatSession.Update(newTitle); context.ChatSessions.Update(chatSession); await context.SaveChangesAsync(cancellationToken); - return $"{chatSession.Id}: {chatSession.Timestamp} - {chatSession.Title}: {chatSession.Author?.Name}"; + return $"{chatSession.Id}: {chatSession.Timestamp} - {chatSession.Title}: {chatSession.Actor?.FirstName} {chatSession.Actor?.LastName}"; } } \ No newline at end of file diff --git a/src/Infrastructure.SqlServer/ConfigureServices.cs b/src/Infrastructure.SqlServer/ConfigureServices.cs index 40982f7..968f630 100644 --- a/src/Infrastructure.SqlServer/ConfigureServices.cs +++ b/src/Infrastructure.SqlServer/ConfigureServices.cs @@ -10,14 +10,12 @@ public static class ConfigureServices public static IServiceCollection AddDbContextServices(this IServiceCollection services, IConfiguration configuration) { - services.AddDbContext(options => options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"), builder => builder.MigrationsAssembly(typeof(SemanticKernelContext).Assembly.FullName)) .UseLazyLoadingProxies()); services.AddScoped(); - services.AddScoped(); return services; } diff --git a/src/Infrastructure.SqlServer/Infrastructure.SqlServer.csproj b/src/Infrastructure.SqlServer/Infrastructure.SqlServer.csproj index 569f088..238d3cc 100644 --- a/src/Infrastructure.SqlServer/Infrastructure.SqlServer.csproj +++ b/src/Infrastructure.SqlServer/Infrastructure.SqlServer.csproj @@ -3,21 +3,21 @@ Goodtocode.SemanticKernel.Infrastructure.SqlServer Goodtocode.SemanticKernel.Infrastructure.SqlServer 1.0.0 - net9.0 + net10.0 false enable enable - - - - - - - - + + + + + + + + diff --git a/src/Infrastructure.SqlServer/JsonSerializerOptionsProvider.cs b/src/Infrastructure.SqlServer/JsonSerializerOptionsProvider.cs new file mode 100644 index 0000000..df1598d --- /dev/null +++ b/src/Infrastructure.SqlServer/JsonSerializerOptionsProvider.cs @@ -0,0 +1,12 @@ +using System.Text.Json; + +namespace Goodtocode.SemanticKernel.Infrastructure.SqlServer; + +public static class JsonSerializerOptionsProvider +{ + public static readonly JsonSerializerOptions Default = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = false + }; +} \ No newline at end of file diff --git a/src/Infrastructure.SqlServer/Migrations/20240707165858_InitialCreate.Designer.cs b/src/Infrastructure.SqlServer/Migrations/20240707165858_InitialCreate.Designer.cs deleted file mode 100644 index 57d5f40..0000000 --- a/src/Infrastructure.SqlServer/Migrations/20240707165858_InitialCreate.Designer.cs +++ /dev/null @@ -1,444 +0,0 @@ -// -using System; -using Goodtocode.SemanticKernel.Infrastructure.SqlServer.Persistence; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Goodtocode.SemanticKernel.Infrastructure.SqlServer.Migrations -{ - [DbContext(typeof(SemanticKernelContext))] - [Migration("20240707165858_InitialCreate")] - partial class InitialCreate - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.6") - .HasAnnotation("Proxies:ChangeTracking", false) - .HasAnnotation("Proxies:CheckEquality", false) - .HasAnnotation("Proxies:LazyLoading", true) - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Audio.TextAudioEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AudioBytes") - .HasColumnType("VARBINARY(MAX)"); - - b.Property("AudioUrl") - .HasColumnType("nvarchar(max)"); - - b.Property("AuthorId") - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedOn") - .HasColumnType("datetime2"); - - b.Property("DeletedOn") - .HasColumnType("datetime2"); - - b.Property("Description") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("ModifiedOn") - .HasColumnType("datetime2"); - - b.Property("Timestamp") - .HasColumnType("datetimeoffset"); - - b.HasKey("Id"); - - SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("Id"), false); - - b.HasIndex("AuthorId"); - - b.HasIndex("Id") - .IsUnique(); - - SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Id"), false); - - b.HasIndex("Timestamp") - .IsUnique(); - - SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Timestamp")); - - b.ToTable("TextAudio", (string)null); - }); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedOn") - .HasColumnType("datetime2"); - - b.Property("DeletedOn") - .HasColumnType("datetime2"); - - b.Property("ModifiedOn") - .HasColumnType("datetime2"); - - b.Property("Name") - .IsRequired() - .HasColumnType("NVARCHAR(200)"); - - b.Property("Timestamp") - .HasColumnType("datetimeoffset"); - - b.HasKey("Id"); - - SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("Id"), false); - - b.HasIndex("Id") - .IsUnique(); - - SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Id"), false); - - b.HasIndex("Timestamp") - .IsUnique(); - - SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Timestamp")); - - b.ToTable("Authors", (string)null); - }); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.ChatCompletion.ChatMessageEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ChatSessionId") - .HasColumnType("uniqueidentifier"); - - b.Property("Content") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("CreatedOn") - .HasColumnType("datetime2"); - - b.Property("DeletedOn") - .HasColumnType("datetime2"); - - b.Property("ModifiedOn") - .HasColumnType("datetime2"); - - b.Property("Role") - .HasColumnType("int"); - - b.Property("Timestamp") - .HasColumnType("datetimeoffset"); - - b.HasKey("Id"); - - SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("Id"), false); - - b.HasIndex("ChatSessionId"); - - b.HasIndex("Id") - .IsUnique(); - - SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Id"), false); - - b.HasIndex("Timestamp") - .IsUnique(); - - SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Timestamp")); - - b.ToTable("ChatMessages", (string)null); - }); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.ChatCompletion.ChatSessionEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AuthorId") - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedOn") - .HasColumnType("datetime2"); - - b.Property("DeletedOn") - .HasColumnType("datetime2"); - - b.Property("ModifiedOn") - .HasColumnType("datetime2"); - - b.Property("Timestamp") - .HasColumnType("datetimeoffset"); - - b.Property("Title") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("Id"), false); - - b.HasIndex("AuthorId"); - - b.HasIndex("Id") - .IsUnique(); - - SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Id"), false); - - b.HasIndex("Timestamp") - .IsUnique(); - - SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Timestamp")); - - b.ToTable("ChatSessions", (string)null); - }); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Image.TextImageEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AuthorId") - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedOn") - .HasColumnType("datetime2"); - - b.Property("DeletedOn") - .HasColumnType("datetime2"); - - b.Property("Description") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Height") - .HasColumnType("int"); - - b.Property("ImageBytes") - .HasColumnType("VARBINARY(MAX)"); - - b.Property("ImageUrl") - .HasColumnType("nvarchar(max)"); - - b.Property("ModifiedOn") - .HasColumnType("datetime2"); - - b.Property("Timestamp") - .HasColumnType("datetimeoffset"); - - b.Property("Width") - .HasColumnType("int"); - - b.HasKey("Id"); - - SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("Id"), false); - - b.HasIndex("AuthorId"); - - b.HasIndex("Id") - .IsUnique(); - - SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Id"), false); - - b.HasIndex("Timestamp") - .IsUnique(); - - SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Timestamp")); - - b.ToTable("TextImages", (string)null); - }); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.TextGeneration.TextPromptEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AuthorId") - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedOn") - .HasColumnType("datetime2"); - - b.Property("DeletedOn") - .HasColumnType("datetime2"); - - b.Property("ModifiedOn") - .HasColumnType("datetime2"); - - b.Property("Prompt") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Timestamp") - .HasColumnType("datetimeoffset"); - - b.HasKey("Id"); - - SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("Id"), false); - - b.HasIndex("AuthorId"); - - b.HasIndex("Id") - .IsUnique(); - - SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Id"), false); - - b.HasIndex("Timestamp") - .IsUnique(); - - SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Timestamp")); - - b.ToTable("TextPrompts", (string)null); - }); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.TextGeneration.TextResponseEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedOn") - .HasColumnType("datetime2"); - - b.Property("DeletedOn") - .HasColumnType("datetime2"); - - b.Property("ModifiedOn") - .HasColumnType("datetime2"); - - b.Property("Response") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("TextPromptId") - .HasColumnType("uniqueidentifier"); - - b.Property("Timestamp") - .HasColumnType("datetimeoffset"); - - b.HasKey("Id"); - - SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("Id"), false); - - b.HasIndex("Id") - .IsUnique(); - - SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Id"), false); - - b.HasIndex("TextPromptId"); - - b.HasIndex("Timestamp") - .IsUnique(); - - SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Timestamp")); - - b.ToTable("TextResponses", (string)null); - }); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Audio.TextAudioEntity", b => - { - b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", "Author") - .WithMany("TextAudio") - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Author"); - }); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.ChatCompletion.ChatMessageEntity", b => - { - b.HasOne("Goodtocode.SemanticKernel.Core.Domain.ChatCompletion.ChatSessionEntity", "ChatSession") - .WithMany("Messages") - .HasForeignKey("ChatSessionId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ChatSession"); - }); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.ChatCompletion.ChatSessionEntity", b => - { - b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", "Author") - .WithMany("ChatSessions") - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Author"); - }); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Image.TextImageEntity", b => - { - b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", "Author") - .WithMany("TextImages") - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Author"); - }); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.TextGeneration.TextPromptEntity", b => - { - b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", "Author") - .WithMany("TextPrompts") - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Author"); - }); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.TextGeneration.TextResponseEntity", b => - { - b.HasOne("Goodtocode.SemanticKernel.Core.Domain.TextGeneration.TextPromptEntity", "TextPrompt") - .WithMany("TextResponses") - .HasForeignKey("TextPromptId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("TextPrompt"); - }); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", b => - { - b.Navigation("ChatSessions"); - - b.Navigation("TextAudio"); - - b.Navigation("TextImages"); - - b.Navigation("TextPrompts"); - }); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.ChatCompletion.ChatSessionEntity", b => - { - b.Navigation("Messages"); - }); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.TextGeneration.TextPromptEntity", b => - { - b.Navigation("TextResponses"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/Infrastructure.SqlServer/Migrations/20250816225410_v1.1.1.cs b/src/Infrastructure.SqlServer/Migrations/20250816225410_v1.1.1.cs deleted file mode 100644 index c8b21c1..0000000 --- a/src/Infrastructure.SqlServer/Migrations/20250816225410_v1.1.1.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Goodtocode.SemanticKernel.Infrastructure.SqlServer.Migrations -{ - /// - public partial class v111 : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "Title", - table: "ChatSessions", - type: "nvarchar(max)", - nullable: true, - oldClrType: typeof(string), - oldType: "nvarchar(max)"); - - migrationBuilder.AlterColumn( - name: "Name", - table: "Authors", - type: "NVARCHAR(200)", - nullable: true, - oldClrType: typeof(string), - oldType: "NVARCHAR(200)"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "Title", - table: "ChatSessions", - type: "nvarchar(max)", - nullable: false, - defaultValue: "", - oldClrType: typeof(string), - oldType: "nvarchar(max)", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Name", - table: "Authors", - type: "NVARCHAR(200)", - nullable: false, - defaultValue: "", - oldClrType: typeof(string), - oldType: "NVARCHAR(200)", - oldNullable: true); - } - } -} diff --git a/src/Infrastructure.SqlServer/Migrations/20250816225410_v1.1.1.Designer.cs b/src/Infrastructure.SqlServer/Migrations/20260130074609_InitialCreate.Designer.cs similarity index 88% rename from src/Infrastructure.SqlServer/Migrations/20250816225410_v1.1.1.Designer.cs rename to src/Infrastructure.SqlServer/Migrations/20260130074609_InitialCreate.Designer.cs index 758d419..9124cbb 100644 --- a/src/Infrastructure.SqlServer/Migrations/20250816225410_v1.1.1.Designer.cs +++ b/src/Infrastructure.SqlServer/Migrations/20260130074609_InitialCreate.Designer.cs @@ -12,15 +12,15 @@ namespace Goodtocode.SemanticKernel.Infrastructure.SqlServer.Migrations { [DbContext(typeof(SemanticKernelContext))] - [Migration("20250816225410_v1.1.1")] - partial class v111 + [Migration("20260130074609_InitialCreate")] + partial class InitialCreate { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "9.0.5") + .HasAnnotation("ProductVersion", "10.0.2") .HasAnnotation("Proxies:ChangeTracking", false) .HasAnnotation("Proxies:CheckEquality", false) .HasAnnotation("Proxies:LazyLoading", true) @@ -28,34 +28,36 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Audio.TextAudioEntity", b => + modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Actor.ActorEntity", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); - b.Property("AudioBytes") - .HasColumnType("VARBINARY(MAX)"); - - b.Property("AudioUrl") - .HasColumnType("nvarchar(max)"); - - b.Property("AuthorId") - .HasColumnType("uniqueidentifier"); - b.Property("CreatedOn") .HasColumnType("datetime2"); b.Property("DeletedOn") .HasColumnType("datetime2"); - b.Property("Description") - .IsRequired() - .HasColumnType("nvarchar(max)"); + b.Property("Email") + .HasColumnType("NVARCHAR(200)"); + + b.Property("FirstName") + .HasColumnType("NVARCHAR(200)"); + + b.Property("LastName") + .HasColumnType("NVARCHAR(200)"); b.Property("ModifiedOn") .HasColumnType("datetime2"); + b.Property("OwnerId") + .HasColumnType("UNIQUEIDENTIFIER"); + + b.Property("TenantId") + .HasColumnType("UNIQUEIDENTIFIER"); + b.Property("Timestamp") .HasColumnType("datetimeoffset"); @@ -63,8 +65,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("Id"), false); - b.HasIndex("AuthorId"); - b.HasIndex("Id") .IsUnique(); @@ -75,27 +75,40 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Timestamp")); - b.ToTable("TextAudio", (string)null); + b.HasIndex("TenantId", "OwnerId") + .IsUnique(); + + b.ToTable("Actors", (string)null); }); - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", b => + modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Audio.TextAudioEntity", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); + b.Property("ActorId") + .HasColumnType("uniqueidentifier"); + + b.Property("AudioBytes") + .HasColumnType("VARBINARY(MAX)"); + + b.Property("AudioUrl") + .HasColumnType("nvarchar(max)"); + b.Property("CreatedOn") .HasColumnType("datetime2"); b.Property("DeletedOn") .HasColumnType("datetime2"); + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + b.Property("ModifiedOn") .HasColumnType("datetime2"); - b.Property("Name") - .HasColumnType("NVARCHAR(200)"); - b.Property("Timestamp") .HasColumnType("datetimeoffset"); @@ -103,6 +116,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("Id"), false); + b.HasIndex("ActorId"); + b.HasIndex("Id") .IsUnique(); @@ -113,7 +128,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Timestamp")); - b.ToTable("Authors", (string)null); + b.ToTable("TextAudio", (string)null); }); modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.ChatCompletion.ChatMessageEntity", b => @@ -169,7 +184,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); - b.Property("AuthorId") + b.Property("ActorId") .HasColumnType("uniqueidentifier"); b.Property("CreatedOn") @@ -191,7 +206,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("Id"), false); - b.HasIndex("AuthorId"); + b.HasIndex("ActorId"); b.HasIndex("Id") .IsUnique(); @@ -212,7 +227,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); - b.Property("AuthorId") + b.Property("ActorId") .HasColumnType("uniqueidentifier"); b.Property("CreatedOn") @@ -247,7 +262,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("Id"), false); - b.HasIndex("AuthorId"); + b.HasIndex("ActorId"); b.HasIndex("Id") .IsUnique(); @@ -268,7 +283,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); - b.Property("AuthorId") + b.Property("ActorId") .HasColumnType("uniqueidentifier"); b.Property("CreatedOn") @@ -291,7 +306,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("Id"), false); - b.HasIndex("AuthorId"); + b.HasIndex("ActorId"); b.HasIndex("Id") .IsUnique(); @@ -352,13 +367,13 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Audio.TextAudioEntity", b => { - b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", "Author") - .WithMany("TextAudio") - .HasForeignKey("AuthorId") + b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Actor.ActorEntity", "Actor") + .WithMany() + .HasForeignKey("ActorId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("Author"); + b.Navigation("Actor"); }); modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.ChatCompletion.ChatMessageEntity", b => @@ -374,41 +389,41 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.ChatCompletion.ChatSessionEntity", b => { - b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", "Author") - .WithMany("ChatSessions") - .HasForeignKey("AuthorId") + b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Actor.ActorEntity", "Actor") + .WithMany() + .HasForeignKey("ActorId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("Author"); + b.Navigation("Actor"); }); modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Image.TextImageEntity", b => { - b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", "Author") - .WithMany("TextImages") - .HasForeignKey("AuthorId") + b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Actor.ActorEntity", "Actor") + .WithMany() + .HasForeignKey("ActorId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("Author"); + b.Navigation("Actor"); }); modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.TextGeneration.TextPromptEntity", b => { - b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", "Author") - .WithMany("TextPrompts") - .HasForeignKey("AuthorId") + b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Actor.ActorEntity", "Actor") + .WithMany() + .HasForeignKey("ActorId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("Author"); + b.Navigation("Actor"); }); modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.TextGeneration.TextResponseEntity", b => { b.HasOne("Goodtocode.SemanticKernel.Core.Domain.TextGeneration.TextPromptEntity", "TextPrompt") - .WithMany("TextResponses") + .WithMany() .HasForeignKey("TextPromptId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -416,26 +431,10 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Navigation("TextPrompt"); }); - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", b => - { - b.Navigation("ChatSessions"); - - b.Navigation("TextAudio"); - - b.Navigation("TextImages"); - - b.Navigation("TextPrompts"); - }); - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.ChatCompletion.ChatSessionEntity", b => { b.Navigation("Messages"); }); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.TextGeneration.TextPromptEntity", b => - { - b.Navigation("TextResponses"); - }); #pragma warning restore 612, 618 } } diff --git a/src/Infrastructure.SqlServer/Migrations/20240707165858_InitialCreate.cs b/src/Infrastructure.SqlServer/Migrations/20260130074609_InitialCreate.cs similarity index 84% rename from src/Infrastructure.SqlServer/Migrations/20240707165858_InitialCreate.cs rename to src/Infrastructure.SqlServer/Migrations/20260130074609_InitialCreate.cs index ae6e903..d07ec0d 100644 --- a/src/Infrastructure.SqlServer/Migrations/20240707165858_InitialCreate.cs +++ b/src/Infrastructure.SqlServer/Migrations/20260130074609_InitialCreate.cs @@ -12,19 +12,23 @@ public partial class InitialCreate : Migration protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( - name: "Authors", + name: "Actors", columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "NVARCHAR(200)", nullable: false), + FirstName = table.Column(type: "NVARCHAR(200)", nullable: true), + LastName = table.Column(type: "NVARCHAR(200)", nullable: true), + Email = table.Column(type: "NVARCHAR(200)", nullable: true), CreatedOn = table.Column(type: "datetime2", nullable: false), ModifiedOn = table.Column(type: "datetime2", nullable: true), DeletedOn = table.Column(type: "datetime2", nullable: true), - Timestamp = table.Column(type: "datetimeoffset", nullable: false) + Timestamp = table.Column(type: "datetimeoffset", nullable: false), + OwnerId = table.Column(type: "UNIQUEIDENTIFIER", nullable: false), + TenantId = table.Column(type: "UNIQUEIDENTIFIER", nullable: false) }, constraints: table => { - table.PrimaryKey("PK_Authors", x => x.Id) + table.PrimaryKey("PK_Actors", x => x.Id) .Annotation("SqlServer:Clustered", false); }); @@ -33,8 +37,8 @@ protected override void Up(MigrationBuilder migrationBuilder) columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), - AuthorId = table.Column(type: "uniqueidentifier", nullable: false), - Title = table.Column(type: "nvarchar(max)", nullable: false), + ActorId = table.Column(type: "uniqueidentifier", nullable: false), + Title = table.Column(type: "nvarchar(max)", nullable: true), CreatedOn = table.Column(type: "datetime2", nullable: false), ModifiedOn = table.Column(type: "datetime2", nullable: true), DeletedOn = table.Column(type: "datetime2", nullable: true), @@ -45,9 +49,9 @@ protected override void Up(MigrationBuilder migrationBuilder) table.PrimaryKey("PK_ChatSessions", x => x.Id) .Annotation("SqlServer:Clustered", false); table.ForeignKey( - name: "FK_ChatSessions_Authors_AuthorId", - column: x => x.AuthorId, - principalTable: "Authors", + name: "FK_ChatSessions_Actors_ActorId", + column: x => x.ActorId, + principalTable: "Actors", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); @@ -57,7 +61,7 @@ protected override void Up(MigrationBuilder migrationBuilder) columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), - AuthorId = table.Column(type: "uniqueidentifier", nullable: false), + ActorId = table.Column(type: "uniqueidentifier", nullable: false), Description = table.Column(type: "nvarchar(max)", nullable: false), AudioBytes = table.Column(type: "VARBINARY(MAX)", nullable: true), AudioUrl = table.Column(type: "nvarchar(max)", nullable: true), @@ -71,9 +75,9 @@ protected override void Up(MigrationBuilder migrationBuilder) table.PrimaryKey("PK_TextAudio", x => x.Id) .Annotation("SqlServer:Clustered", false); table.ForeignKey( - name: "FK_TextAudio_Authors_AuthorId", - column: x => x.AuthorId, - principalTable: "Authors", + name: "FK_TextAudio_Actors_ActorId", + column: x => x.ActorId, + principalTable: "Actors", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); @@ -83,7 +87,7 @@ protected override void Up(MigrationBuilder migrationBuilder) columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), - AuthorId = table.Column(type: "uniqueidentifier", nullable: false), + ActorId = table.Column(type: "uniqueidentifier", nullable: false), Description = table.Column(type: "nvarchar(max)", nullable: false), ImageBytes = table.Column(type: "VARBINARY(MAX)", nullable: true), ImageUrl = table.Column(type: "nvarchar(max)", nullable: true), @@ -99,9 +103,9 @@ protected override void Up(MigrationBuilder migrationBuilder) table.PrimaryKey("PK_TextImages", x => x.Id) .Annotation("SqlServer:Clustered", false); table.ForeignKey( - name: "FK_TextImages_Authors_AuthorId", - column: x => x.AuthorId, - principalTable: "Authors", + name: "FK_TextImages_Actors_ActorId", + column: x => x.ActorId, + principalTable: "Actors", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); @@ -111,7 +115,7 @@ protected override void Up(MigrationBuilder migrationBuilder) columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), - AuthorId = table.Column(type: "uniqueidentifier", nullable: false), + ActorId = table.Column(type: "uniqueidentifier", nullable: false), Prompt = table.Column(type: "nvarchar(max)", nullable: false), CreatedOn = table.Column(type: "datetime2", nullable: false), ModifiedOn = table.Column(type: "datetime2", nullable: true), @@ -123,9 +127,9 @@ protected override void Up(MigrationBuilder migrationBuilder) table.PrimaryKey("PK_TextPrompts", x => x.Id) .Annotation("SqlServer:Clustered", false); table.ForeignKey( - name: "FK_TextPrompts_Authors_AuthorId", - column: x => x.AuthorId, - principalTable: "Authors", + name: "FK_TextPrompts_Actors_ActorId", + column: x => x.ActorId, + principalTable: "Actors", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); @@ -180,15 +184,21 @@ protected override void Up(MigrationBuilder migrationBuilder) }); migrationBuilder.CreateIndex( - name: "IX_Authors_Id", - table: "Authors", + name: "IX_Actors_Id", + table: "Actors", column: "Id", unique: true) .Annotation("SqlServer:Clustered", false); migrationBuilder.CreateIndex( - name: "IX_Authors_Timestamp", - table: "Authors", + name: "IX_Actors_TenantId_OwnerId", + table: "Actors", + columns: new[] { "TenantId", "OwnerId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Actors_Timestamp", + table: "Actors", column: "Timestamp", unique: true) .Annotation("SqlServer:Clustered", true); @@ -213,9 +223,9 @@ protected override void Up(MigrationBuilder migrationBuilder) .Annotation("SqlServer:Clustered", true); migrationBuilder.CreateIndex( - name: "IX_ChatSessions_AuthorId", + name: "IX_ChatSessions_ActorId", table: "ChatSessions", - column: "AuthorId"); + column: "ActorId"); migrationBuilder.CreateIndex( name: "IX_ChatSessions_Id", @@ -232,9 +242,9 @@ protected override void Up(MigrationBuilder migrationBuilder) .Annotation("SqlServer:Clustered", true); migrationBuilder.CreateIndex( - name: "IX_TextAudio_AuthorId", + name: "IX_TextAudio_ActorId", table: "TextAudio", - column: "AuthorId"); + column: "ActorId"); migrationBuilder.CreateIndex( name: "IX_TextAudio_Id", @@ -251,9 +261,9 @@ protected override void Up(MigrationBuilder migrationBuilder) .Annotation("SqlServer:Clustered", true); migrationBuilder.CreateIndex( - name: "IX_TextImages_AuthorId", + name: "IX_TextImages_ActorId", table: "TextImages", - column: "AuthorId"); + column: "ActorId"); migrationBuilder.CreateIndex( name: "IX_TextImages_Id", @@ -270,9 +280,9 @@ protected override void Up(MigrationBuilder migrationBuilder) .Annotation("SqlServer:Clustered", true); migrationBuilder.CreateIndex( - name: "IX_TextPrompts_AuthorId", + name: "IX_TextPrompts_ActorId", table: "TextPrompts", - column: "AuthorId"); + column: "ActorId"); migrationBuilder.CreateIndex( name: "IX_TextPrompts_Id", @@ -330,7 +340,7 @@ protected override void Down(MigrationBuilder migrationBuilder) name: "TextPrompts"); migrationBuilder.DropTable( - name: "Authors"); + name: "Actors"); } } } diff --git a/src/Infrastructure.SqlServer/Migrations/SemanticKernelContextModelSnapshot.cs b/src/Infrastructure.SqlServer/Migrations/SemanticKernelContextModelSnapshot.cs index 82c73ab..49432c7 100644 --- a/src/Infrastructure.SqlServer/Migrations/SemanticKernelContextModelSnapshot.cs +++ b/src/Infrastructure.SqlServer/Migrations/SemanticKernelContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "9.0.5") + .HasAnnotation("ProductVersion", "10.0.2") .HasAnnotation("Proxies:ChangeTracking", false) .HasAnnotation("Proxies:CheckEquality", false) .HasAnnotation("Proxies:LazyLoading", true) @@ -25,34 +25,36 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Audio.TextAudioEntity", b => + modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Actor.ActorEntity", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); - b.Property("AudioBytes") - .HasColumnType("VARBINARY(MAX)"); - - b.Property("AudioUrl") - .HasColumnType("nvarchar(max)"); - - b.Property("AuthorId") - .HasColumnType("uniqueidentifier"); - b.Property("CreatedOn") .HasColumnType("datetime2"); b.Property("DeletedOn") .HasColumnType("datetime2"); - b.Property("Description") - .IsRequired() - .HasColumnType("nvarchar(max)"); + b.Property("Email") + .HasColumnType("NVARCHAR(200)"); + + b.Property("FirstName") + .HasColumnType("NVARCHAR(200)"); + + b.Property("LastName") + .HasColumnType("NVARCHAR(200)"); b.Property("ModifiedOn") .HasColumnType("datetime2"); + b.Property("OwnerId") + .HasColumnType("UNIQUEIDENTIFIER"); + + b.Property("TenantId") + .HasColumnType("UNIQUEIDENTIFIER"); + b.Property("Timestamp") .HasColumnType("datetimeoffset"); @@ -60,8 +62,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("Id"), false); - b.HasIndex("AuthorId"); - b.HasIndex("Id") .IsUnique(); @@ -72,27 +72,40 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Timestamp")); - b.ToTable("TextAudio", (string)null); + b.HasIndex("TenantId", "OwnerId") + .IsUnique(); + + b.ToTable("Actors", (string)null); }); - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", b => + modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Audio.TextAudioEntity", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); + b.Property("ActorId") + .HasColumnType("uniqueidentifier"); + + b.Property("AudioBytes") + .HasColumnType("VARBINARY(MAX)"); + + b.Property("AudioUrl") + .HasColumnType("nvarchar(max)"); + b.Property("CreatedOn") .HasColumnType("datetime2"); b.Property("DeletedOn") .HasColumnType("datetime2"); + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + b.Property("ModifiedOn") .HasColumnType("datetime2"); - b.Property("Name") - .HasColumnType("NVARCHAR(200)"); - b.Property("Timestamp") .HasColumnType("datetimeoffset"); @@ -100,6 +113,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("Id"), false); + b.HasIndex("ActorId"); + b.HasIndex("Id") .IsUnique(); @@ -110,7 +125,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("Timestamp")); - b.ToTable("Authors", (string)null); + b.ToTable("TextAudio", (string)null); }); modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.ChatCompletion.ChatMessageEntity", b => @@ -166,7 +181,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); - b.Property("AuthorId") + b.Property("ActorId") .HasColumnType("uniqueidentifier"); b.Property("CreatedOn") @@ -188,7 +203,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("Id"), false); - b.HasIndex("AuthorId"); + b.HasIndex("ActorId"); b.HasIndex("Id") .IsUnique(); @@ -209,7 +224,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); - b.Property("AuthorId") + b.Property("ActorId") .HasColumnType("uniqueidentifier"); b.Property("CreatedOn") @@ -244,7 +259,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("Id"), false); - b.HasIndex("AuthorId"); + b.HasIndex("ActorId"); b.HasIndex("Id") .IsUnique(); @@ -265,7 +280,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("uniqueidentifier"); - b.Property("AuthorId") + b.Property("ActorId") .HasColumnType("uniqueidentifier"); b.Property("CreatedOn") @@ -288,7 +303,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("Id"), false); - b.HasIndex("AuthorId"); + b.HasIndex("ActorId"); b.HasIndex("Id") .IsUnique(); @@ -349,13 +364,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Audio.TextAudioEntity", b => { - b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", "Author") - .WithMany("TextAudio") - .HasForeignKey("AuthorId") + b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Actor.ActorEntity", "Actor") + .WithMany() + .HasForeignKey("ActorId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("Author"); + b.Navigation("Actor"); }); modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.ChatCompletion.ChatMessageEntity", b => @@ -371,41 +386,41 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.ChatCompletion.ChatSessionEntity", b => { - b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", "Author") - .WithMany("ChatSessions") - .HasForeignKey("AuthorId") + b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Actor.ActorEntity", "Actor") + .WithMany() + .HasForeignKey("ActorId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("Author"); + b.Navigation("Actor"); }); modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Image.TextImageEntity", b => { - b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", "Author") - .WithMany("TextImages") - .HasForeignKey("AuthorId") + b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Actor.ActorEntity", "Actor") + .WithMany() + .HasForeignKey("ActorId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("Author"); + b.Navigation("Actor"); }); modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.TextGeneration.TextPromptEntity", b => { - b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", "Author") - .WithMany("TextPrompts") - .HasForeignKey("AuthorId") + b.HasOne("Goodtocode.SemanticKernel.Core.Domain.Actor.ActorEntity", "Actor") + .WithMany() + .HasForeignKey("ActorId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("Author"); + b.Navigation("Actor"); }); modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.TextGeneration.TextResponseEntity", b => { b.HasOne("Goodtocode.SemanticKernel.Core.Domain.TextGeneration.TextPromptEntity", "TextPrompt") - .WithMany("TextResponses") + .WithMany() .HasForeignKey("TextPromptId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -413,26 +428,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("TextPrompt"); }); - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.Author.AuthorEntity", b => - { - b.Navigation("ChatSessions"); - - b.Navigation("TextAudio"); - - b.Navigation("TextImages"); - - b.Navigation("TextPrompts"); - }); - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.ChatCompletion.ChatSessionEntity", b => { b.Navigation("Messages"); }); - - modelBuilder.Entity("Goodtocode.SemanticKernel.Core.Domain.TextGeneration.TextPromptEntity", b => - { - b.Navigation("TextResponses"); - }); #pragma warning restore 612, 618 } } diff --git a/src/Infrastructure.SqlServer/Persistence/Configurations/ActorsConfig.cs b/src/Infrastructure.SqlServer/Persistence/Configurations/ActorsConfig.cs new file mode 100644 index 0000000..cbd9b41 --- /dev/null +++ b/src/Infrastructure.SqlServer/Persistence/Configurations/ActorsConfig.cs @@ -0,0 +1,49 @@ +using Goodtocode.SemanticKernel.Core.Domain.Actor; + +namespace Goodtocode.SemanticKernel.Infrastructure.SqlServer.Persistence.Configurations; + +public class ActorsConfig : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + builder.ToTable("Actors"); + + builder.HasKey(x => x.Id) + .IsClustered(false); + + builder.HasIndex(x => x.Id) + .IsClustered(false) + .IsUnique(); + + builder.HasIndex(x => x.Timestamp) + .IsClustered() + .IsUnique(); + + builder.Property(x => x.Id) + .ValueGeneratedOnAdd(); + + builder.Ignore(x => x.PartitionKey); + + builder.Property(x => x.FirstName) + .HasColumnType(ColumnTypes.Nvarchar200); + + builder.Property(x => x.LastName) + .HasColumnType(ColumnTypes.Nvarchar200); + + builder.Property(x => x.Email) + .HasColumnType(ColumnTypes.Nvarchar200); + + builder.Property(x => x.OwnerId) + .HasColumnType(ColumnTypes.Uniqueidentifier) + .IsRequired(); + + builder.Property(x => x.TenantId) + .HasColumnType(ColumnTypes.Uniqueidentifier) + .IsRequired(); + + builder.HasIndex(x => new { x.TenantId, x.OwnerId }) + .IsUnique(); + } +} \ No newline at end of file diff --git a/src/Infrastructure.SqlServer/Persistence/Configurations/AuthorsConfig.cs b/src/Infrastructure.SqlServer/Persistence/Configurations/AuthorsConfig.cs deleted file mode 100644 index 605c053..0000000 --- a/src/Infrastructure.SqlServer/Persistence/Configurations/AuthorsConfig.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Goodtocode.SemanticKernel.Core.Domain.Author; - -namespace Goodtocode.SemanticKernel.Infrastructure.SqlServer.Persistence.Configurations; - -public class AuthorsConfig : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - builder.ToTable("Authors"); - builder.HasKey(x => x.Id) - .IsClustered(false); - builder.HasIndex(x => x.Id) - .IsClustered(false) - .IsUnique(); - builder.HasIndex(x => x.Timestamp) - .IsClustered() - .IsUnique(); - builder.Property(x => x.Id) - .ValueGeneratedOnAdd(); - builder.Ignore(x => x.PartitionKey); - builder.Property(x => x.Name) - .HasColumnType(ColumnTypes.Nvarchar200); - } -} \ No newline at end of file diff --git a/src/Infrastructure.SqlServer/Persistence/Configurations/ChatSessionsConfig.cs b/src/Infrastructure.SqlServer/Persistence/Configurations/ChatSessionsConfig.cs index 8e4a3a5..8e83874 100644 --- a/src/Infrastructure.SqlServer/Persistence/Configurations/ChatSessionsConfig.cs +++ b/src/Infrastructure.SqlServer/Persistence/Configurations/ChatSessionsConfig.cs @@ -20,10 +20,6 @@ public void Configure(EntityTypeBuilder builder) builder.Property(x => x.Id) .ValueGeneratedOnAdd(); builder.Ignore(x => x.PartitionKey); - builder - .HasOne(a => a.Author) - .WithMany(a => a.ChatSessions) - .HasForeignKey(a => a.AuthorId); builder .HasMany(cs => cs.Messages) .WithOne(cm => cm.ChatSession) diff --git a/src/Infrastructure.SqlServer/Persistence/Configurations/TextAudioConfig.cs b/src/Infrastructure.SqlServer/Persistence/Configurations/TextAudioConfig.cs index 3a2f478..debad80 100644 --- a/src/Infrastructure.SqlServer/Persistence/Configurations/TextAudioConfig.cs +++ b/src/Infrastructure.SqlServer/Persistence/Configurations/TextAudioConfig.cs @@ -1,8 +1,4 @@ using Goodtocode.SemanticKernel.Core.Domain.Audio; -using Goodtocode.SemanticKernel.Core.Domain.Author; -using Goodtocode.SemanticKernel.Core.Domain.ChatCompletion; -using Goodtocode.SemanticKernel.Core.Domain.Image; -using Goodtocode.SemanticKernel.Core.Domain.TextGeneration; namespace Goodtocode.SemanticKernel.Infrastructure.SqlServer.Persistence.Configurations; @@ -24,10 +20,6 @@ public void Configure(EntityTypeBuilder builder) builder.Property(x => x.Id) .ValueGeneratedOnAdd(); builder.Ignore(x => x.PartitionKey); - builder - .HasOne(a => a.Author) - .WithMany(a => a.TextAudio) - .HasForeignKey(a => a.AuthorId); builder.Property(x => x.AudioBytes) .HasColumnType(ColumnTypes.VarbinaryMax) .HasConversion( diff --git a/src/Infrastructure.SqlServer/Persistence/Configurations/TextImagesConfig.cs b/src/Infrastructure.SqlServer/Persistence/Configurations/TextImagesConfig.cs index 9e0355c..e4e2dd1 100644 --- a/src/Infrastructure.SqlServer/Persistence/Configurations/TextImagesConfig.cs +++ b/src/Infrastructure.SqlServer/Persistence/Configurations/TextImagesConfig.cs @@ -20,10 +20,6 @@ public void Configure(EntityTypeBuilder builder) builder.Property(x => x.Id) .ValueGeneratedOnAdd(); builder.Ignore(x => x.PartitionKey); - builder - .HasOne(a => a.Author) - .WithMany(a => a.TextImages) - .HasForeignKey(a => a.AuthorId); builder.Property(x => x.ImageBytes) .HasColumnType(ColumnTypes.VarbinaryMax) .HasConversion( diff --git a/src/Infrastructure.SqlServer/Persistence/Configurations/TextPromptsConfig.cs b/src/Infrastructure.SqlServer/Persistence/Configurations/TextPromptsConfig.cs index 902cbe1..f750a6e 100644 --- a/src/Infrastructure.SqlServer/Persistence/Configurations/TextPromptsConfig.cs +++ b/src/Infrastructure.SqlServer/Persistence/Configurations/TextPromptsConfig.cs @@ -20,13 +20,5 @@ public void Configure(EntityTypeBuilder builder) builder.Property(x => x.Id) .ValueGeneratedOnAdd(); builder.Ignore(x => x.PartitionKey); - builder - .HasOne(a => a.Author) - .WithMany(a => a.TextPrompts) - .HasForeignKey(a => a.AuthorId); - builder - .HasMany(cs => cs.TextResponses) - .WithOne(cm => cm.TextPrompt) - .HasForeignKey(cm => cm.TextPromptId); } } \ No newline at end of file diff --git a/src/Infrastructure.SqlServer/Persistence/SemanticKernelContext.cs b/src/Infrastructure.SqlServer/Persistence/SemanticKernelContext.cs index b49e8bc..55d4195 100644 --- a/src/Infrastructure.SqlServer/Persistence/SemanticKernelContext.cs +++ b/src/Infrastructure.SqlServer/Persistence/SemanticKernelContext.cs @@ -1,23 +1,23 @@ -using System.Reflection; +using Goodtocode.Domain.Entities; using Goodtocode.SemanticKernel.Core.Application.Abstractions; +using Goodtocode.SemanticKernel.Core.Domain.Actor; using Goodtocode.SemanticKernel.Core.Domain.Audio; -using Goodtocode.SemanticKernel.Core.Domain.Author; using Goodtocode.SemanticKernel.Core.Domain.ChatCompletion; using Goodtocode.SemanticKernel.Core.Domain.Image; using Goodtocode.SemanticKernel.Core.Domain.TextGeneration; +using System.Reflection; namespace Goodtocode.SemanticKernel.Infrastructure.SqlServer.Persistence; public class SemanticKernelContext : DbContext, ISemanticKernelContext { - // Roles: User, Assistant, System - public DbSet Authors => Set(); public DbSet ChatMessages => Set(); public DbSet ChatSessions => Set(); public DbSet TextPrompts => Set(); public DbSet TextResponses => Set(); public DbSet TextImages => Set(); public DbSet TextAudio => Set(); + public DbSet Actors => Set(); protected SemanticKernelContext() { } @@ -30,4 +30,50 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly(), x => x.Namespace == $"{GetType().Namespace}.Configurations"); } + + public override Task SaveChangesAsync(CancellationToken cancellationToken = default) + { + SetAuditFields(); + return base.SaveChangesAsync(cancellationToken); + } + + private void SetAuditFields() + { + var entries = ChangeTracker.Entries() + .Where(e => IsDomainEntity(e.Entity) && + (e.State == EntityState.Modified || e.State == EntityState.Added || e.State == EntityState.Deleted)); + + foreach (var entry in entries) + { + dynamic entity = entry.Entity; + if (entry.State == EntityState.Added) + { + entity.SetCreatedOn(DateTime.UtcNow); + entity.SetModifiedOn(null); + entity.SetDeletedOn(null); + } + else if (entry.State == EntityState.Modified) + { + entity.SetModifiedOn(DateTime.UtcNow); + entity.SetDeletedOn(null); + } + else if (entry.State == EntityState.Deleted) + { + entity.SetDeletedOn(DateTime.UtcNow); + entry.State = EntityState.Modified; + } + } + } + + private static bool IsDomainEntity(object entity) + { + var type = entity.GetType(); + while (type != null) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(DomainEntity<>)) + return true; + type = type.BaseType; + } + return false; + } } diff --git a/src/Infrastructure.SqlServer/Persistence/SemanticKernelContextInitializer.cs b/src/Infrastructure.SqlServer/Persistence/SemanticKernelContextInitializer.cs deleted file mode 100644 index cc027a9..0000000 --- a/src/Infrastructure.SqlServer/Persistence/SemanticKernelContextInitializer.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Goodtocode.SemanticKernel.Core.Domain.Author; -using Microsoft.Extensions.Logging; - -namespace Goodtocode.SemanticKernel.Infrastructure.SqlServer.Persistence; - -public class SemanticKernelContextInitializer(ILogger logger, SemanticKernelContext context) -{ - private readonly SemanticKernelContext _context = context; - private readonly ILogger _logger = logger; - - public async Task InitialiseAsync() - { - try - { - if (_context.Database.IsSqlServer()) await _context.Database.MigrateAsync(); - } - catch (Exception ex) - { - _logger.LogError(ex, "An error occurred while initializing the database."); - throw; - } - } - - public async Task SeedAsync() - { - try - { - await TrySeedAsync(); - } - catch (Exception ex) - { - _logger.LogError(ex, "An error occurred while seeding the database."); - throw; - } - } - - public async Task TrySeedAsync() - { - if (!_context.Authors.Any()) - { - var author = AuthorEntity.Create(Guid.NewGuid(), "John Doe"); - _context.Authors.Add(author); - await _context.SaveChangesAsync(CancellationToken.None); - } - } -} \ No newline at end of file diff --git a/src/Presentation.Blazor/Clients/WebApiClient.g.cs b/src/Presentation.Blazor/Clients/BackendApiClient.g.cs similarity index 86% rename from src/Presentation.Blazor/Clients/WebApiClient.g.cs rename to src/Presentation.Blazor/Clients/BackendApiClient.g.cs index 588b670..565445c 100644 --- a/src/Presentation.Blazor/Clients/WebApiClient.g.cs +++ b/src/Presentation.Blazor/Clients/BackendApiClient.g.cs @@ -1,6 +1,6 @@ //---------------------- // -// Generated using the NSwag toolchain v14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org) +// Generated using the NSwag toolchain v14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org) // //---------------------- @@ -24,8 +24,8 @@ namespace Goodtocode.SemanticKernel.Presentation.WebApi.Client { using System = global::System; - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class WebApiClient + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class BackendApiClient { #pragma warning disable 8618 private string _baseUrl; @@ -36,7 +36,7 @@ public partial class WebApiClient private System.Text.Json.JsonSerializerOptions _instanceSettings; #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public WebApiClient(string baseUrl, System.Net.Http.HttpClient httpClient) + public BackendApiClient(string baseUrl, System.Net.Http.HttpClient httpClient) #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. { BaseUrl = baseUrl; @@ -116,8 +116,8 @@ public virtual async System.Threading.Tasks.Task GetTextAudioQuery var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/Audio/{id}" - urlBuilder_.Append("api/v1/Audio/"); + // Operation Path: "api/v1/admin/audio/{id}" + urlBuilder_.Append("api/v1/admin/audio/"); urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); PrepareRequest(client_, request_, urlBuilder_); @@ -153,6 +153,16 @@ public virtual async System.Threading.Tasks.Task GetTextAudioQuery return objectResponse_.Object; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 404) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); @@ -235,8 +245,8 @@ public virtual async System.Threading.Tasks.Task RemoveTextAudioCommandAsync(Sys var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/Audio/{id}" - urlBuilder_.Append("api/v1/Audio/"); + // Operation Path: "api/v1/admin/audio/{id}" + urlBuilder_.Append("api/v1/admin/audio/"); urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); PrepareRequest(client_, request_, urlBuilder_); @@ -267,6 +277,16 @@ public virtual async System.Threading.Tasks.Task RemoveTextAudioCommandAsync(Sys return; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 404) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); @@ -349,8 +369,8 @@ public virtual async System.Threading.Tasks.Task RemoveTextAudioCommandAsync(Sys var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/Audio" - urlBuilder_.Append("api/v1/Audio"); + // Operation Path: "api/v1/admin/audio" + urlBuilder_.Append("api/v1/admin/audio"); PrepareRequest(client_, request_, urlBuilder_); @@ -385,6 +405,16 @@ public virtual async System.Threading.Tasks.Task RemoveTextAudioCommandAsync(Sys return objectResponse_.Object; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 404) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); @@ -479,8 +509,8 @@ public virtual async System.Threading.Tasks.Task CreateTextToAudio var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/Audio" - urlBuilder_.Append("api/v1/Audio"); + // Operation Path: "api/v1/admin/audio" + urlBuilder_.Append("api/v1/admin/audio"); PrepareRequest(client_, request_, urlBuilder_); @@ -525,6 +555,16 @@ public virtual async System.Threading.Tasks.Task CreateTextToAudio throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 500) { string responseText_ = ( response_.Content == null ) ? string.Empty : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); @@ -601,8 +641,8 @@ public virtual async System.Threading.Tasks.Task GetT var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/Audio/Paginated" - urlBuilder_.Append("api/v1/Audio/Paginated"); + // Operation Path: "api/v1/admin/audio/Paginated" + urlBuilder_.Append("api/v1/admin/audio/Paginated"); urlBuilder_.Append('?'); if (startDate != null) { @@ -655,6 +695,16 @@ public virtual async System.Threading.Tasks.Task GetT return objectResponse_.Object; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 500) { string responseText_ = ( response_.Content == null ) ? string.Empty : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); @@ -685,7 +735,7 @@ public virtual async System.Threading.Tasks.Task GetT } /// - /// Get Author session with history + /// Get Chat Message /// /// /// Sample request: @@ -695,14 +745,14 @@ public virtual async System.Threading.Tasks.Task GetT /// /// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetAuthorQueryAsync(System.Guid id) + public virtual System.Threading.Tasks.Task GetChatMessageQueryAsync(System.Guid id) { - return GetAuthorQueryAsync(id, System.Threading.CancellationToken.None); + return GetChatMessageQueryAsync(id, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Get Author session with history + /// Get Chat Message /// /// /// Sample request: @@ -712,7 +762,7 @@ public virtual System.Threading.Tasks.Task GetAuthorQueryAsync(System /// /// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetAuthorQueryAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task GetChatMessageQueryAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) { if (id == null) throw new System.ArgumentNullException("id"); @@ -728,8 +778,8 @@ public virtual async System.Threading.Tasks.Task GetAuthorQueryAsync( var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/Author/{id}" - urlBuilder_.Append("api/v1/Author/"); + // Operation Path: "api/v1/admin/messages/{id}" + urlBuilder_.Append("api/v1/admin/messages/"); urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); PrepareRequest(client_, request_, urlBuilder_); @@ -757,7 +807,7 @@ public virtual async System.Threading.Tasks.Task GetAuthorQueryAsync( var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -765,6 +815,16 @@ public virtual async System.Threading.Tasks.Task GetAuthorQueryAsync( return objectResponse_.Object; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 404) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); @@ -805,51 +865,50 @@ public virtual async System.Threading.Tasks.Task GetAuthorQueryAsync( } /// - /// Remove Author Command + /// Get All Chat Messages for a session Query /// /// /// Sample request: ///
- ///
"Id": 60fb5e99-3a78-43df-a512-7d8ff498499e - ///
"api-version": 1.0 + ///
"StartDate": "2024-06-01T00:00:00Z" + ///
"EndDate": "2024-12-01T00:00:00Z" + ///
"api-version": 1.0 ///
- /// No Content + /// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task RemoveAuthorCommandAsync(System.Guid id) + public virtual System.Threading.Tasks.Task> GetChatMessagesQueryAsync() { - return RemoveAuthorCommandAsync(id, System.Threading.CancellationToken.None); + return GetChatMessagesQueryAsync(System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Remove Author Command + /// Get All Chat Messages for a session Query /// /// /// Sample request: ///
- ///
"Id": 60fb5e99-3a78-43df-a512-7d8ff498499e - ///
"api-version": 1.0 + ///
"StartDate": "2024-06-01T00:00:00Z" + ///
"EndDate": "2024-12-01T00:00:00Z" + ///
"api-version": 1.0 ///
- /// No Content + /// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task RemoveAuthorCommandAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task> GetChatMessagesQueryAsync(System.Threading.CancellationToken cancellationToken) { - if (id == null) - throw new System.ArgumentNullException("id"); - var client_ = _httpClient; var disposeClient_ = false; try { using (var request_ = new System.Net.Http.HttpRequestMessage()) { - request_.Method = new System.Net.Http.HttpMethod("DELETE"); + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/Author/{id}" - urlBuilder_.Append("api/v1/Author/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); + // Operation Path: "api/v1/admin/messages" + urlBuilder_.Append("api/v1/admin/messages"); PrepareRequest(client_, request_, urlBuilder_); @@ -874,9 +933,24 @@ public virtual async System.Threading.Tasks.Task RemoveAuthorCommandAsync(System ProcessResponse(client_, response_); var status_ = (int)response_.StatusCode; - if (status_ == 204) + if (status_ == 200) { - return; + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 404) @@ -919,57 +993,76 @@ public virtual async System.Threading.Tasks.Task RemoveAuthorCommandAsync(System } /// - /// Get All Author Chat Sessions Query + /// Creates new Chat Message with initial message prompt/response history /// /// - /// Sample request: + /// Types of Chat Completion are: + ///
1. Informational Prompt: A prompt requesting information + ///
- Example Prompt: "What's the capital of France?" + ///
- Example Response: "The capital of France is Paris." + ///
2. Multiple Choice Prompt: A prompt with instructions for multiple-choice responses. + ///
- Example Prompt: “Choose an activity for the weekend: a) Hiking b) Movie night c) Cooking class d) Board games” + ///
- Example Response: “I'd recommend hiking! It's a great way to enjoy nature and get some exercise.” + ///
Sample request: ///
- ///
"AuthorId": 60fb5e99-3a78-43df-a512-7d8ff498499e - ///
"StartDate": "2024-06-01T00:00:00Z" - ///
"EndDate": "2024-12-01T00:00:00Z" - ///
"api-version": 1.0 + ///
HttpPost Body + ///
{ + ///
"Id": 00000000-0000-0000-0000-000000000000, + ///
"Message": "Hi, I am interested in learning about Semantic Kernel." + ///
} + ///
+ ///
"version": 1.0 ///
- /// OK + /// Created /// A server side error occurred. - public virtual System.Threading.Tasks.Task> GetAuthorChatSessionsQueryAsync(System.Guid id) + public virtual System.Threading.Tasks.Task CreateChatMessageCommandAsync(CreateChatMessageCommand body) { - return GetAuthorChatSessionsQueryAsync(id, System.Threading.CancellationToken.None); + return CreateChatMessageCommandAsync(body, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Get All Author Chat Sessions Query + /// Creates new Chat Message with initial message prompt/response history /// /// - /// Sample request: + /// Types of Chat Completion are: + ///
1. Informational Prompt: A prompt requesting information + ///
- Example Prompt: "What's the capital of France?" + ///
- Example Response: "The capital of France is Paris." + ///
2. Multiple Choice Prompt: A prompt with instructions for multiple-choice responses. + ///
- Example Prompt: “Choose an activity for the weekend: a) Hiking b) Movie night c) Cooking class d) Board games” + ///
- Example Response: “I'd recommend hiking! It's a great way to enjoy nature and get some exercise.” + ///
Sample request: ///
- ///
"AuthorId": 60fb5e99-3a78-43df-a512-7d8ff498499e - ///
"StartDate": "2024-06-01T00:00:00Z" - ///
"EndDate": "2024-12-01T00:00:00Z" - ///
"api-version": 1.0 + ///
HttpPost Body + ///
{ + ///
"Id": 00000000-0000-0000-0000-000000000000, + ///
"Message": "Hi, I am interested in learning about Semantic Kernel." + ///
} + ///
+ ///
"version": 1.0 ///
- /// OK + /// Created /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> GetAuthorChatSessionsQueryAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task CreateChatMessageCommandAsync(CreateChatMessageCommand body, System.Threading.CancellationToken cancellationToken) { - if (id == null) - throw new System.ArgumentNullException("id"); - var client_ = _httpClient; var disposeClient_ = false; try { using (var request_ = new System.Net.Http.HttpRequestMessage()) { - request_.Method = new System.Net.Http.HttpMethod("GET"); + var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); + var content_ = new System.Net.Http.ByteArrayContent(json_); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/Author/{id}/ChatSessions" - urlBuilder_.Append("api/v1/Author/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/ChatSessions"); + // Operation Path: "api/v1/admin/messages" + urlBuilder_.Append("api/v1/admin/messages"); PrepareRequest(client_, request_, urlBuilder_); @@ -994,9 +1087,9 @@ public virtual async System.Threading.Tasks.Task RemoveAuthorCommandAsync(System ProcessResponse(client_, response_); var status_ = (int)response_.StatusCode; - if (status_ == 200) + if (status_ == 201) { - var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -1004,14 +1097,24 @@ public virtual async System.Threading.Tasks.Task RemoveAuthorCommandAsync(System return objectResponse_.Object; } else - if (status_ == 404) + if (status_ == 400) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); } - throw new ApiException("Not Found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 500) @@ -1044,7 +1147,7 @@ public virtual async System.Threading.Tasks.Task RemoveAuthorCommandAsync(System } /// - /// Get Author Chat Sessions Paginated Query + /// Get Chat Messages Paginated Query /// /// /// Sample request: @@ -1057,14 +1160,14 @@ public virtual async System.Threading.Tasks.Task RemoveAuthorCommandAsync(System /// /// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetAuthorChatSessionsPaginatedQueryAsync(System.Guid id, System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize) + public virtual System.Threading.Tasks.Task GetChatMessagesPaginatedQueryAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize) { - return GetAuthorChatSessionsPaginatedQueryAsync(id, startDate, endDate, pageNumber, pageSize, System.Threading.CancellationToken.None); + return GetChatMessagesPaginatedQueryAsync(startDate, endDate, pageNumber, pageSize, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Get Author Chat Sessions Paginated Query + /// Get Chat Messages Paginated Query /// /// /// Sample request: @@ -1077,11 +1180,8 @@ public virtual System.Threading.Tasks.Task GetAutho /// /// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetAuthorChatSessionsPaginatedQueryAsync(System.Guid id, System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task GetChatMessagesPaginatedQueryAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize, System.Threading.CancellationToken cancellationToken) { - if (id == null) - throw new System.ArgumentNullException("id"); - var client_ = _httpClient; var disposeClient_ = false; try @@ -1093,26 +1193,24 @@ public virtual async System.Threading.Tasks.Task Ge var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/Author/{id}/ChatSessions/Paginated" - urlBuilder_.Append("api/v1/Author/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/ChatSessions/Paginated"); + // Operation Path: "api/v1/admin/messages/Paginated" + urlBuilder_.Append("api/v1/admin/messages/Paginated"); urlBuilder_.Append('?'); if (startDate != null) { - urlBuilder_.Append(System.Uri.EscapeDataString("startDate")).Append('=').Append(System.Uri.EscapeDataString(startDate.Value.ToString("s", System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + urlBuilder_.Append(System.Uri.EscapeDataString("StartDate")).Append('=').Append(System.Uri.EscapeDataString(startDate.Value.ToString("s", System.Globalization.CultureInfo.InvariantCulture))).Append('&'); } if (endDate != null) { - urlBuilder_.Append(System.Uri.EscapeDataString("endDate")).Append('=').Append(System.Uri.EscapeDataString(endDate.Value.ToString("s", System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + urlBuilder_.Append(System.Uri.EscapeDataString("EndDate")).Append('=').Append(System.Uri.EscapeDataString(endDate.Value.ToString("s", System.Globalization.CultureInfo.InvariantCulture))).Append('&'); } if (pageNumber != null) { - urlBuilder_.Append(System.Uri.EscapeDataString("pageNumber")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(pageNumber, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + urlBuilder_.Append(System.Uri.EscapeDataString("PageNumber")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(pageNumber, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); } if (pageSize != null) { - urlBuilder_.Append(System.Uri.EscapeDataString("pageSize")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(pageSize, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + urlBuilder_.Append(System.Uri.EscapeDataString("PageSize")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(pageSize, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); } urlBuilder_.Length--; @@ -1141,7 +1239,7 @@ public virtual async System.Threading.Tasks.Task Ge var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -1149,6 +1247,16 @@ public virtual async System.Threading.Tasks.Task Ge return objectResponse_.Object; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 500) { string responseText_ = ( response_.Content == null ) ? string.Empty : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); @@ -1179,43 +1287,38 @@ public virtual async System.Threading.Tasks.Task Ge } /// - /// Get All Author Chat Session Query + /// Get Chat Session with history /// /// /// Sample request: ///
- ///
"AuthorId": 60fb5e99-3a78-43df-a512-7d8ff498499e - ///
"ChatSessionId": 1efb5e99-3a78-43df-a512-7d8ff498499e - ///
"api-version": 1.0 + ///
"Id": 60fb5e99-3a78-43df-a512-7d8ff498499e + ///
"api-version": 1.0 ///
/// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetAuthorChatSessionQueryAsync(System.Guid id, System.Guid chatSessionId) + public virtual System.Threading.Tasks.Task GetChatSessionQueryAsync(System.Guid id) { - return GetAuthorChatSessionQueryAsync(id, chatSessionId, System.Threading.CancellationToken.None); + return GetChatSessionQueryAsync(id, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Get All Author Chat Session Query + /// Get Chat Session with history /// /// /// Sample request: ///
- ///
"AuthorId": 60fb5e99-3a78-43df-a512-7d8ff498499e - ///
"ChatSessionId": 1efb5e99-3a78-43df-a512-7d8ff498499e - ///
"api-version": 1.0 + ///
"Id": 60fb5e99-3a78-43df-a512-7d8ff498499e + ///
"api-version": 1.0 ///
/// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetAuthorChatSessionQueryAsync(System.Guid id, System.Guid chatSessionId, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task GetChatSessionQueryAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) { if (id == null) throw new System.ArgumentNullException("id"); - if (chatSessionId == null) - throw new System.ArgumentNullException("chatSessionId"); - var client_ = _httpClient; var disposeClient_ = false; try @@ -1227,11 +1330,9 @@ public virtual async System.Threading.Tasks.Task GetAuthorChatSe var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/Author/{id}/ChatSessions/{chatSessionId}" - urlBuilder_.Append("api/v1/Author/"); + // Operation Path: "api/v1/admin/sessions/{id}" + urlBuilder_.Append("api/v1/admin/sessions/"); urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Append("/ChatSessions/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(chatSessionId, System.Globalization.CultureInfo.InvariantCulture))); PrepareRequest(client_, request_, urlBuilder_); @@ -1266,6 +1367,16 @@ public virtual async System.Threading.Tasks.Task GetAuthorChatSe return objectResponse_.Object; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 404) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); @@ -1306,45 +1417,48 @@ public virtual async System.Threading.Tasks.Task GetAuthorChatSe } /// - /// Creates new Author session with empty history + /// Patch Chat Session Command /// /// /// Sample request: ///
- ///
HttpPost Body + ///
HttpPatch Body ///
{ - ///
"Id": 00000000-0000-0000-0000-000000000000, - ///
"Name": "John Doe" + ///
"Id": "60fb5e99-3a78-43df-a512-7d8ff498499e", + ///
"Title": "Semantic Kernel Chat Session" ///
} ///
///
"version": 1.0 ///
- /// Created + /// No Content /// A server side error occurred. - public virtual System.Threading.Tasks.Task CreateAuthorCommandAsync(CreateAuthorCommand body) + public virtual System.Threading.Tasks.Task PatchChatSessionCommandAsync(System.Guid id, PatchChatSessionCommand body) { - return CreateAuthorCommandAsync(body, System.Threading.CancellationToken.None); + return PatchChatSessionCommandAsync(id, body, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Creates new Author session with empty history + /// Patch Chat Session Command /// /// /// Sample request: ///
- ///
HttpPost Body + ///
HttpPatch Body ///
{ - ///
"Id": 00000000-0000-0000-0000-000000000000, - ///
"Name": "John Doe" + ///
"Id": "60fb5e99-3a78-43df-a512-7d8ff498499e", + ///
"Title": "Semantic Kernel Chat Session" ///
} ///
///
"version": 1.0 ///
- /// Created + /// No Content /// A server side error occurred. - public virtual async System.Threading.Tasks.Task CreateAuthorCommandAsync(CreateAuthorCommand body, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task PatchChatSessionCommandAsync(System.Guid id, PatchChatSessionCommand body, System.Threading.CancellationToken cancellationToken) { + if (id == null) + throw new System.ArgumentNullException("id"); + var client_ = _httpClient; var disposeClient_ = false; try @@ -1355,13 +1469,13 @@ public virtual async System.Threading.Tasks.Task CreateAuthorCommandA var content_ = new System.Net.Http.ByteArrayContent(json_); content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + request_.Method = new System.Net.Http.HttpMethod("PATCH"); var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/Author" - urlBuilder_.Append("api/v1/Author"); + // Operation Path: "api/v1/admin/sessions/{id}" + urlBuilder_.Append("api/v1/admin/sessions/"); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); PrepareRequest(client_, request_, urlBuilder_); @@ -1386,24 +1500,29 @@ public virtual async System.Threading.Tasks.Task CreateAuthorCommandA ProcessResponse(client_, response_); var status_ = (int)response_.StatusCode; - if (status_ == 201) + if (status_ == 204) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; + return; } else - if (status_ == 400) + if (status_ == 401) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); } - throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not Found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 500) @@ -1436,61 +1555,51 @@ public virtual async System.Threading.Tasks.Task CreateAuthorCommandA } /// - /// Update Author Command + /// Remove ChatSession Command /// /// /// Sample request: ///
- ///
HttpPut Body - ///
{ - ///
"Id": "60fb5e99-3a78-43df-a512-7d8ff498499e", - ///
"Name": "John Doe", - ///
} - ///
- ///
"version": 1.0 + ///
"Id": 60fb5e99-3a78-43df-a512-7d8ff498499e + ///
"api-version": 1.0 ///
/// No Content /// A server side error occurred. - public virtual System.Threading.Tasks.Task UpdateAuthorCommandAsync(UpdateAuthorCommand body) + public virtual System.Threading.Tasks.Task RemoveChatSessionCommandAsync(System.Guid id) { - return UpdateAuthorCommandAsync(body, System.Threading.CancellationToken.None); + return RemoveChatSessionCommandAsync(id, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Update Author Command + /// Remove ChatSession Command /// /// /// Sample request: ///
- ///
HttpPut Body - ///
{ - ///
"Id": "60fb5e99-3a78-43df-a512-7d8ff498499e", - ///
"Name": "John Doe", - ///
} - ///
- ///
"version": 1.0 + ///
"Id": 60fb5e99-3a78-43df-a512-7d8ff498499e + ///
"api-version": 1.0 ///
/// No Content /// A server side error occurred. - public virtual async System.Threading.Tasks.Task UpdateAuthorCommandAsync(UpdateAuthorCommand body, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task RemoveChatSessionCommandAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) { + if (id == null) + throw new System.ArgumentNullException("id"); + var client_ = _httpClient; var disposeClient_ = false; try { using (var request_ = new System.Net.Http.HttpRequestMessage()) { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("PUT"); + request_.Method = new System.Net.Http.HttpMethod("DELETE"); var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/Author" - urlBuilder_.Append("api/v1/Author"); + // Operation Path: "api/v1/admin/sessions/{id}" + urlBuilder_.Append("api/v1/admin/sessions/"); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); PrepareRequest(client_, request_, urlBuilder_); @@ -1520,6 +1629,16 @@ public virtual async System.Threading.Tasks.Task UpdateAuthorCommandAsync(Update return; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 404) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); @@ -1560,38 +1679,37 @@ public virtual async System.Threading.Tasks.Task UpdateAuthorCommandAsync(Update } /// - /// Get Chat Message + /// Get All Chat Sessions Query /// /// /// Sample request: ///
- ///
"Id": 60fb5e99-3a78-43df-a512-7d8ff498499e - ///
"api-version": 1.0 + ///
"StartDate": "2024-06-01T00:00:00Z" + ///
"EndDate": "2024-12-01T00:00:00Z" + ///
"api-version": 1.0 ///
/// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetChatMessageQueryAsync(System.Guid id) + public virtual System.Threading.Tasks.Task> GetChatSessionsQueryAsync() { - return GetChatMessageQueryAsync(id, System.Threading.CancellationToken.None); + return GetChatSessionsQueryAsync(System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Get Chat Message + /// Get All Chat Sessions Query /// /// /// Sample request: ///
- ///
"Id": 60fb5e99-3a78-43df-a512-7d8ff498499e - ///
"api-version": 1.0 + ///
"StartDate": "2024-06-01T00:00:00Z" + ///
"EndDate": "2024-12-01T00:00:00Z" + ///
"api-version": 1.0 ///
/// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetChatMessageQueryAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task> GetChatSessionsQueryAsync(System.Threading.CancellationToken cancellationToken) { - if (id == null) - throw new System.ArgumentNullException("id"); - var client_ = _httpClient; var disposeClient_ = false; try @@ -1603,9 +1721,8 @@ public virtual async System.Threading.Tasks.Task GetChatMessageQ var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/ChatMessage/{id}" - urlBuilder_.Append("api/v1/ChatMessage/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); + // Operation Path: "api/v1/admin/sessions" + urlBuilder_.Append("api/v1/admin/sessions"); PrepareRequest(client_, request_, urlBuilder_); @@ -1632,7 +1749,7 @@ public virtual async System.Threading.Tasks.Task GetChatMessageQ var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -1640,6 +1757,16 @@ public virtual async System.Threading.Tasks.Task GetChatMessageQ return objectResponse_.Object; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 404) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); @@ -1680,36 +1807,58 @@ public virtual async System.Threading.Tasks.Task GetChatMessageQ } /// - /// Get All Chat Messages for a session Query + /// Creates new Chat Session with initial message prompt/response history /// /// - /// Sample request: + /// Types of Chat Completion are: + ///
1. Informational Prompt: A prompt requesting information + ///
- Example Prompt: "What's the capital of France?" + ///
- Example Response: "The capital of France is Paris." + ///
2. Multiple Choice Prompt: A prompt with instructions for multiple-choice responses. + ///
- Example Prompt: “Choose an activity for the weekend: a) Hiking b) Movie night c) Cooking class d) Board games” + ///
- Example Response: “I'd recommend hiking! It's a great way to enjoy nature and get some exercise.” + ///
Sample request: ///
- ///
"StartDate": "2024-06-01T00:00:00Z" - ///
"EndDate": "2024-12-01T00:00:00Z" - ///
"api-version": 1.0 + ///
HttpPost Body + ///
{ + ///
"Id": 00000000-0000-0000-0000-000000000000, + ///
"Message": "Hi, I am interested in learning about Semantic Kernel." + ///
} + ///
+ ///
"version": 1.0 ///
- /// OK + /// Created /// A server side error occurred. - public virtual System.Threading.Tasks.Task> GetChatMessagesQueryAsync() + public virtual System.Threading.Tasks.Task CreateChatSessionCommandAsync(CreateChatSessionCommand body) { - return GetChatMessagesQueryAsync(System.Threading.CancellationToken.None); + return CreateChatSessionCommandAsync(body, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Get All Chat Messages for a session Query + /// Creates new Chat Session with initial message prompt/response history /// /// - /// Sample request: + /// Types of Chat Completion are: + ///
1. Informational Prompt: A prompt requesting information + ///
- Example Prompt: "What's the capital of France?" + ///
- Example Response: "The capital of France is Paris." + ///
2. Multiple Choice Prompt: A prompt with instructions for multiple-choice responses. + ///
- Example Prompt: “Choose an activity for the weekend: a) Hiking b) Movie night c) Cooking class d) Board games” + ///
- Example Response: “I'd recommend hiking! It's a great way to enjoy nature and get some exercise.” + ///
Sample request: ///
- ///
"StartDate": "2024-06-01T00:00:00Z" - ///
"EndDate": "2024-12-01T00:00:00Z" - ///
"api-version": 1.0 + ///
HttpPost Body + ///
{ + ///
"Id": 00000000-0000-0000-0000-000000000000, + ///
"Message": "Hi, I am interested in learning about Semantic Kernel." + ///
} + ///
+ ///
"version": 1.0 ///
- /// OK + /// Created /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> GetChatMessagesQueryAsync(System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task CreateChatSessionCommandAsync(CreateChatSessionCommand body, System.Threading.CancellationToken cancellationToken) { var client_ = _httpClient; var disposeClient_ = false; @@ -1717,13 +1866,17 @@ public virtual async System.Threading.Tasks.Task GetChatMessageQ { using (var request_ = new System.Net.Http.HttpRequestMessage()) { - request_.Method = new System.Net.Http.HttpMethod("GET"); + var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); + var content_ = new System.Net.Http.ByteArrayContent(json_); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/ChatMessage" - urlBuilder_.Append("api/v1/ChatMessage"); + // Operation Path: "api/v1/admin/sessions" + urlBuilder_.Append("api/v1/admin/sessions"); PrepareRequest(client_, request_, urlBuilder_); @@ -1748,9 +1901,9 @@ public virtual async System.Threading.Tasks.Task GetChatMessageQ ProcessResponse(client_, response_); var status_ = (int)response_.StatusCode; - if (status_ == 200) + if (status_ == 201) { - var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -1758,14 +1911,24 @@ public virtual async System.Threading.Tasks.Task GetChatMessageQ return objectResponse_.Object; } else - if (status_ == 404) + if (status_ == 400) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); } - throw new ApiException("Not Found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 500) @@ -1798,58 +1961,46 @@ public virtual async System.Threading.Tasks.Task GetChatMessageQ } /// - /// Creates new Chat Message with initial message prompt/response history + /// Update ChatSession Command, typically with changing the title or adding a new message /// /// - /// Types of Chat Completion are: - ///
1. Informational Prompt: A prompt requesting information - ///
- Example Prompt: "What's the capital of France?" - ///
- Example Response: "The capital of France is Paris." - ///
2. Multiple Choice Prompt: A prompt with instructions for multiple-choice responses. - ///
- Example Prompt: “Choose an activity for the weekend: a) Hiking b) Movie night c) Cooking class d) Board games” - ///
- Example Response: “I'd recommend hiking! It's a great way to enjoy nature and get some exercise.” - ///
Sample request: + /// Sample request: ///
- ///
HttpPost Body + ///
HttpPut Body ///
{ - ///
"Id": 00000000-0000-0000-0000-000000000000, - ///
"Message": "Hi, I am interested in learning about Semantic Kernel." + ///
"Id": "60fb5e99-3a78-43df-a512-7d8ff498499e", + ///
"Message": "Hi, I am interested in learning about Semantic Kernel.", + ///
"Content": "Certainly! Semantic Kernel is a great framework for AI.", ///
} ///
///
"version": 1.0 ///
- /// Created + /// No Content /// A server side error occurred. - public virtual System.Threading.Tasks.Task CreateChatMessageCommandAsync(CreateChatMessageCommand body) + public virtual System.Threading.Tasks.Task UpdateChatSessionCommandAsync(UpdateChatSessionCommand body) { - return CreateChatMessageCommandAsync(body, System.Threading.CancellationToken.None); + return UpdateChatSessionCommandAsync(body, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Creates new Chat Message with initial message prompt/response history + /// Update ChatSession Command, typically with changing the title or adding a new message /// /// - /// Types of Chat Completion are: - ///
1. Informational Prompt: A prompt requesting information - ///
- Example Prompt: "What's the capital of France?" - ///
- Example Response: "The capital of France is Paris." - ///
2. Multiple Choice Prompt: A prompt with instructions for multiple-choice responses. - ///
- Example Prompt: “Choose an activity for the weekend: a) Hiking b) Movie night c) Cooking class d) Board games” - ///
- Example Response: “I'd recommend hiking! It's a great way to enjoy nature and get some exercise.” - ///
Sample request: + /// Sample request: ///
- ///
HttpPost Body + ///
HttpPut Body ///
{ - ///
"Id": 00000000-0000-0000-0000-000000000000, - ///
"Message": "Hi, I am interested in learning about Semantic Kernel." + ///
"Id": "60fb5e99-3a78-43df-a512-7d8ff498499e", + ///
"Message": "Hi, I am interested in learning about Semantic Kernel.", + ///
"Content": "Certainly! Semantic Kernel is a great framework for AI.", ///
} ///
///
"version": 1.0 ///
- /// Created + /// No Content /// A server side error occurred. - public virtual async System.Threading.Tasks.Task CreateChatMessageCommandAsync(CreateChatMessageCommand body, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task UpdateChatSessionCommandAsync(UpdateChatSessionCommand body, System.Threading.CancellationToken cancellationToken) { var client_ = _httpClient; var disposeClient_ = false; @@ -1861,13 +2012,12 @@ public virtual async System.Threading.Tasks.Task CreateChatMessa var content_ = new System.Net.Http.ByteArrayContent(json_); content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + request_.Method = new System.Net.Http.HttpMethod("PUT"); var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/ChatMessage" - urlBuilder_.Append("api/v1/ChatMessage"); + // Operation Path: "api/v1/admin/sessions" + urlBuilder_.Append("api/v1/admin/sessions"); PrepareRequest(client_, request_, urlBuilder_); @@ -1892,24 +2042,29 @@ public virtual async System.Threading.Tasks.Task CreateChatMessa ProcessResponse(client_, response_); var status_ = (int)response_.StatusCode; - if (status_ == 201) + if (status_ == 204) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + return; + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); } - return objectResponse_.Object; + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else - if (status_ == 400) + if (status_ == 404) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); } - throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + throw new ApiException("Not Found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 500) @@ -1942,7 +2097,7 @@ public virtual async System.Threading.Tasks.Task CreateChatMessa } /// - /// Get Chat Messages Paginated Query + /// Get Chat Sessions Paginated Query /// /// /// Sample request: @@ -1955,14 +2110,14 @@ public virtual async System.Threading.Tasks.Task CreateChatMessa /// /// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetChatMessagesPaginatedQueryAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize) + public virtual System.Threading.Tasks.Task GetChatSessionsPaginatedQueryAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize) { - return GetChatMessagesPaginatedQueryAsync(startDate, endDate, pageNumber, pageSize, System.Threading.CancellationToken.None); + return GetChatSessionsPaginatedQueryAsync(startDate, endDate, pageNumber, pageSize, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Get Chat Messages Paginated Query + /// Get Chat Sessions Paginated Query /// /// /// Sample request: @@ -1975,7 +2130,7 @@ public virtual System.Threading.Tasks.Task GetChatM /// /// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetChatMessagesPaginatedQueryAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task GetChatSessionsPaginatedQueryAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize, System.Threading.CancellationToken cancellationToken) { var client_ = _httpClient; var disposeClient_ = false; @@ -1988,8 +2143,8 @@ public virtual async System.Threading.Tasks.Task Ge var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/ChatMessage/Paginated" - urlBuilder_.Append("api/v1/ChatMessage/Paginated"); + // Operation Path: "api/v1/admin/sessions/Paginated" + urlBuilder_.Append("api/v1/admin/sessions/Paginated"); urlBuilder_.Append('?'); if (startDate != null) { @@ -2034,7 +2189,7 @@ public virtual async System.Threading.Tasks.Task Ge var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -2042,6 +2197,16 @@ public virtual async System.Threading.Tasks.Task Ge return objectResponse_.Object; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 500) { string responseText_ = ( response_.Content == null ) ? string.Empty : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); @@ -2072,7 +2237,7 @@ public virtual async System.Threading.Tasks.Task Ge } /// - /// Get Chat Session with history + /// Get Text Image with history /// /// /// Sample request: @@ -2082,14 +2247,14 @@ public virtual async System.Threading.Tasks.Task Ge /// /// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetChatSessionQueryAsync(System.Guid id) + public virtual System.Threading.Tasks.Task GetTextImageQueryAsync(System.Guid id) { - return GetChatSessionQueryAsync(id, System.Threading.CancellationToken.None); + return GetTextImageQueryAsync(id, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Get Chat Session with history + /// Get Text Image with history /// /// /// Sample request: @@ -2099,7 +2264,7 @@ public virtual System.Threading.Tasks.Task GetChatSessionQueryAs /// /// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetChatSessionQueryAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task GetTextImageQueryAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) { if (id == null) throw new System.ArgumentNullException("id"); @@ -2115,8 +2280,8 @@ public virtual async System.Threading.Tasks.Task GetChatSessionQ var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/ChatSession/{id}" - urlBuilder_.Append("api/v1/ChatSession/"); + // Operation Path: "api/v1/admin/images/{id}" + urlBuilder_.Append("api/v1/admin/images/"); urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); PrepareRequest(client_, request_, urlBuilder_); @@ -2144,7 +2309,7 @@ public virtual async System.Threading.Tasks.Task GetChatSessionQ var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -2152,132 +2317,14 @@ public virtual async System.Threading.Tasks.Task GetChatSessionQ return objectResponse_.Object; } else - if (status_ == 404) + if (status_ == 401) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); } - throw new ApiException("Not Found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); - } - else - if (status_ == 500) - { - string responseText_ = ( response_.Content == null ) ? string.Empty : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); - throw new ApiException("Internal Server Error", status_, responseText_, headers_, null); - } - else - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Patch Chat Session Command - /// - /// - /// Sample request: - ///
- ///
HttpPatch Body - ///
{ - ///
"Id": "60fb5e99-3a78-43df-a512-7d8ff498499e", - ///
"Title": "Semantic Kernel Chat Session" - ///
} - ///
- ///
"version": 1.0 - ///
- /// No Content - /// A server side error occurred. - public virtual System.Threading.Tasks.Task PatchChatSessionCommandAsync(System.Guid id, PatchChatSessionCommand body) - { - return PatchChatSessionCommandAsync(id, body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Patch Chat Session Command - /// - /// - /// Sample request: - ///
- ///
HttpPatch Body - ///
{ - ///
"Id": "60fb5e99-3a78-43df-a512-7d8ff498499e", - ///
"Title": "Semantic Kernel Chat Session" - ///
} - ///
- ///
"version": 1.0 - ///
- /// No Content - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task PatchChatSessionCommandAsync(System.Guid id, PatchChatSessionCommand body, System.Threading.CancellationToken cancellationToken) - { - if (id == null) - throw new System.ArgumentNullException("id"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("PATCH"); - - var urlBuilder_ = new System.Text.StringBuilder(); - if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/ChatSession/{id}" - urlBuilder_.Append("api/v1/ChatSession/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 204) - { - return; + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 404) @@ -2320,7 +2367,7 @@ public virtual async System.Threading.Tasks.Task PatchChatSessionCommandAsync(Sy } /// - /// Remove ChatSession Command + /// Remove Image Command /// /// /// Sample request: @@ -2330,14 +2377,14 @@ public virtual async System.Threading.Tasks.Task PatchChatSessionCommandAsync(Sy /// /// No Content /// A server side error occurred. - public virtual System.Threading.Tasks.Task RemoveChatSessionCommandAsync(System.Guid id) + public virtual System.Threading.Tasks.Task RemoveTextImageCommandAsync(System.Guid id) { - return RemoveChatSessionCommandAsync(id, System.Threading.CancellationToken.None); + return RemoveTextImageCommandAsync(id, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Remove ChatSession Command + /// Remove Image Command /// /// /// Sample request: @@ -2347,7 +2394,7 @@ public virtual System.Threading.Tasks.Task RemoveChatSessionCommandAsync(System. /// /// No Content /// A server side error occurred. - public virtual async System.Threading.Tasks.Task RemoveChatSessionCommandAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task RemoveTextImageCommandAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) { if (id == null) throw new System.ArgumentNullException("id"); @@ -2362,8 +2409,8 @@ public virtual async System.Threading.Tasks.Task RemoveChatSessionCommandAsync(S var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/ChatSession/{id}" - urlBuilder_.Append("api/v1/ChatSession/"); + // Operation Path: "api/v1/admin/images/{id}" + urlBuilder_.Append("api/v1/admin/images/"); urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); PrepareRequest(client_, request_, urlBuilder_); @@ -2394,6 +2441,16 @@ public virtual async System.Threading.Tasks.Task RemoveChatSessionCommandAsync(S return; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 404) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); @@ -2434,7 +2491,7 @@ public virtual async System.Threading.Tasks.Task RemoveChatSessionCommandAsync(S } /// - /// Get All Chat Sessions Query + /// Get All Text Images Query /// /// /// Sample request: @@ -2445,14 +2502,14 @@ public virtual async System.Threading.Tasks.Task RemoveChatSessionCommandAsync(S /// /// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task> GetChatSessionsQueryAsync() + public virtual System.Threading.Tasks.Task> GetTextImagesQueryAsync() { - return GetChatSessionsQueryAsync(System.Threading.CancellationToken.None); + return GetTextImagesQueryAsync(System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Get All Chat Sessions Query + /// Get All Text Images Query /// /// /// Sample request: @@ -2463,7 +2520,7 @@ public virtual async System.Threading.Tasks.Task RemoveChatSessionCommandAsync(S /// /// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> GetChatSessionsQueryAsync(System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task> GetTextImagesQueryAsync(System.Threading.CancellationToken cancellationToken) { var client_ = _httpClient; var disposeClient_ = false; @@ -2476,8 +2533,8 @@ public virtual async System.Threading.Tasks.Task RemoveChatSessionCommandAsync(S var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/ChatSession" - urlBuilder_.Append("api/v1/ChatSession"); + // Operation Path: "api/v1/admin/images" + urlBuilder_.Append("api/v1/admin/images"); PrepareRequest(client_, request_, urlBuilder_); @@ -2504,7 +2561,7 @@ public virtual async System.Threading.Tasks.Task RemoveChatSessionCommandAsync(S var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -2512,158 +2569,24 @@ public virtual async System.Threading.Tasks.Task RemoveChatSessionCommandAsync(S return objectResponse_.Object; } else - if (status_ == 404) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - throw new ApiException("Not Found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); - } - else - if (status_ == 500) - { - string responseText_ = ( response_.Content == null ) ? string.Empty : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); - throw new ApiException("Internal Server Error", status_, responseText_, headers_, null); - } - else + if (status_ == 401) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); } - throw new ApiException("Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - /// - /// Creates new Chat Session with initial message prompt/response history - /// - /// - /// Types of Chat Completion are: - ///
1. Informational Prompt: A prompt requesting information - ///
- Example Prompt: "What's the capital of France?" - ///
- Example Response: "The capital of France is Paris." - ///
2. Multiple Choice Prompt: A prompt with instructions for multiple-choice responses. - ///
- Example Prompt: “Choose an activity for the weekend: a) Hiking b) Movie night c) Cooking class d) Board games” - ///
- Example Response: “I'd recommend hiking! It's a great way to enjoy nature and get some exercise.” - ///
Sample request: - ///
- ///
HttpPost Body - ///
{ - ///
"Id": 00000000-0000-0000-0000-000000000000, - ///
"Message": "Hi, I am interested in learning about Semantic Kernel." - ///
} - ///
- ///
"version": 1.0 - ///
- /// Created - /// A server side error occurred. - public virtual System.Threading.Tasks.Task CreateChatSessionCommandAsync(CreateChatSessionCommand body) - { - return CreateChatSessionCommandAsync(body, System.Threading.CancellationToken.None); - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Creates new Chat Session with initial message prompt/response history - /// - /// - /// Types of Chat Completion are: - ///
1. Informational Prompt: A prompt requesting information - ///
- Example Prompt: "What's the capital of France?" - ///
- Example Response: "The capital of France is Paris." - ///
2. Multiple Choice Prompt: A prompt with instructions for multiple-choice responses. - ///
- Example Prompt: “Choose an activity for the weekend: a) Hiking b) Movie night c) Cooking class d) Board games” - ///
- Example Response: “I'd recommend hiking! It's a great way to enjoy nature and get some exercise.” - ///
Sample request: - ///
- ///
HttpPost Body - ///
{ - ///
"Id": 00000000-0000-0000-0000-000000000000, - ///
"Message": "Hi, I am interested in learning about Semantic Kernel." - ///
} - ///
- ///
"version": 1.0 - ///
- /// Created - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task CreateChatSessionCommandAsync(CreateChatSessionCommand body, System.Threading.CancellationToken cancellationToken) - { - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/ChatSession" - urlBuilder_.Append("api/v1/ChatSession"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 201) - { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else - if (status_ == 400) + if (status_ == 404) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); } - throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + throw new ApiException("Not Found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 500) @@ -2696,46 +2619,44 @@ public virtual async System.Threading.Tasks.Task CreateChatSessi } /// - /// Update ChatSession Command, typically with changing the title or adding a new message + /// Creates new Image from Text /// /// /// Sample request: ///
- ///
HttpPut Body + ///
HttpPost Body ///
{ - ///
"Id": "60fb5e99-3a78-43df-a512-7d8ff498499e", - ///
"Message": "Hi, I am interested in learning about Semantic Kernel.", - ///
"Content": "Certainly! Semantic Kernel is a great framework for AI.", + ///
"Id": 00000000-0000-0000-0000-000000000000, + ///
"Message": "Hi, I am interested in learning about Semantic Kernel." ///
} ///
///
"version": 1.0 ///
- /// No Content + /// Created /// A server side error occurred. - public virtual System.Threading.Tasks.Task UpdateChatSessionCommandAsync(UpdateChatSessionCommand body) + public virtual System.Threading.Tasks.Task CreateTextToImageCommandAsync(CreateTextToImageCommand body) { - return UpdateChatSessionCommandAsync(body, System.Threading.CancellationToken.None); + return CreateTextToImageCommandAsync(body, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Update ChatSession Command, typically with changing the title or adding a new message + /// Creates new Image from Text /// /// /// Sample request: ///
- ///
HttpPut Body + ///
HttpPost Body ///
{ - ///
"Id": "60fb5e99-3a78-43df-a512-7d8ff498499e", - ///
"Message": "Hi, I am interested in learning about Semantic Kernel.", - ///
"Content": "Certainly! Semantic Kernel is a great framework for AI.", + ///
"Id": 00000000-0000-0000-0000-000000000000, + ///
"Message": "Hi, I am interested in learning about Semantic Kernel." ///
} ///
///
"version": 1.0 ///
- /// No Content + /// Created /// A server side error occurred. - public virtual async System.Threading.Tasks.Task UpdateChatSessionCommandAsync(UpdateChatSessionCommand body, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task CreateTextToImageCommandAsync(CreateTextToImageCommand body, System.Threading.CancellationToken cancellationToken) { var client_ = _httpClient; var disposeClient_ = false; @@ -2747,12 +2668,13 @@ public virtual async System.Threading.Tasks.Task UpdateChatSessionCommandAsync(U var content_ = new System.Net.Http.ByteArrayContent(json_); content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("PUT"); + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/ChatSession" - urlBuilder_.Append("api/v1/ChatSession"); + // Operation Path: "api/v1/admin/images" + urlBuilder_.Append("api/v1/admin/images"); PrepareRequest(client_, request_, urlBuilder_); @@ -2777,19 +2699,34 @@ public virtual async System.Threading.Tasks.Task UpdateChatSessionCommandAsync(U ProcessResponse(client_, response_); var status_ = (int)response_.StatusCode; - if (status_ == 204) + if (status_ == 201) { - return; + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; } else - if (status_ == 404) + if (status_ == 400) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); } - throw new ApiException("Not Found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 500) @@ -2822,7 +2759,7 @@ public virtual async System.Threading.Tasks.Task UpdateChatSessionCommandAsync(U } /// - /// Get Chat Sessions Paginated Query + /// Get Text Images Paginated Query /// /// /// Sample request: @@ -2835,14 +2772,14 @@ public virtual async System.Threading.Tasks.Task UpdateChatSessionCommandAsync(U /// /// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetChatSessionsPaginatedQueryAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize) + public virtual System.Threading.Tasks.Task GetTextImagesPaginatedQueryAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize) { - return GetChatSessionsPaginatedQueryAsync(startDate, endDate, pageNumber, pageSize, System.Threading.CancellationToken.None); + return GetTextImagesPaginatedQueryAsync(startDate, endDate, pageNumber, pageSize, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Get Chat Sessions Paginated Query + /// Get Text Images Paginated Query /// /// /// Sample request: @@ -2855,7 +2792,7 @@ public virtual System.Threading.Tasks.Task GetChatS /// /// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetChatSessionsPaginatedQueryAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task GetTextImagesPaginatedQueryAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize, System.Threading.CancellationToken cancellationToken) { var client_ = _httpClient; var disposeClient_ = false; @@ -2868,8 +2805,8 @@ public virtual async System.Threading.Tasks.Task Ge var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/ChatSession/Paginated" - urlBuilder_.Append("api/v1/ChatSession/Paginated"); + // Operation Path: "api/v1/admin/images/Paginated" + urlBuilder_.Append("api/v1/admin/images/Paginated"); urlBuilder_.Append('?'); if (startDate != null) { @@ -2914,7 +2851,7 @@ public virtual async System.Threading.Tasks.Task Ge var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -2922,6 +2859,16 @@ public virtual async System.Threading.Tasks.Task Ge return objectResponse_.Object; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 500) { string responseText_ = ( response_.Content == null ) ? string.Empty : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); @@ -2952,7 +2899,7 @@ public virtual async System.Threading.Tasks.Task Ge } /// - /// Get Text Image with history + /// Get Text Generation session with history /// /// /// Sample request: @@ -2962,14 +2909,14 @@ public virtual async System.Threading.Tasks.Task Ge /// /// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetTextImageQueryAsync(System.Guid id) + public virtual System.Threading.Tasks.Task GetTextPromptQueryAsync(System.Guid id) { - return GetTextImageQueryAsync(id, System.Threading.CancellationToken.None); + return GetTextPromptQueryAsync(id, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Get Text Image with history + /// Get Text Generation session with history /// /// /// Sample request: @@ -2979,7 +2926,7 @@ public virtual System.Threading.Tasks.Task GetTextImageQueryAsync( /// /// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetTextImageQueryAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task GetTextPromptQueryAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) { if (id == null) throw new System.ArgumentNullException("id"); @@ -2995,8 +2942,8 @@ public virtual async System.Threading.Tasks.Task GetTextImageQuery var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/Image/{id}" - urlBuilder_.Append("api/v1/Image/"); + // Operation Path: "api/v1/admin/text/{id}" + urlBuilder_.Append("api/v1/admin/text/"); urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); PrepareRequest(client_, request_, urlBuilder_); @@ -3024,7 +2971,7 @@ public virtual async System.Threading.Tasks.Task GetTextImageQuery var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -3032,6 +2979,16 @@ public virtual async System.Threading.Tasks.Task GetTextImageQuery return objectResponse_.Object; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 404) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); @@ -3072,7 +3029,7 @@ public virtual async System.Threading.Tasks.Task GetTextImageQuery } /// - /// Remove Image Command + /// Remove TextGeneration Command /// /// /// Sample request: @@ -3082,14 +3039,14 @@ public virtual async System.Threading.Tasks.Task GetTextImageQuery /// /// No Content /// A server side error occurred. - public virtual System.Threading.Tasks.Task RemoveTextImageCommandAsync(System.Guid id) + public virtual System.Threading.Tasks.Task RemoveTextPromptCommandAsync(System.Guid id) { - return RemoveTextImageCommandAsync(id, System.Threading.CancellationToken.None); + return RemoveTextPromptCommandAsync(id, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Remove Image Command + /// Remove TextGeneration Command /// /// /// Sample request: @@ -3099,7 +3056,7 @@ public virtual System.Threading.Tasks.Task RemoveTextImageCommandAsync(System.Gu /// /// No Content /// A server side error occurred. - public virtual async System.Threading.Tasks.Task RemoveTextImageCommandAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task RemoveTextPromptCommandAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) { if (id == null) throw new System.ArgumentNullException("id"); @@ -3114,8 +3071,8 @@ public virtual async System.Threading.Tasks.Task RemoveTextImageCommandAsync(Sys var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/Image/{id}" - urlBuilder_.Append("api/v1/Image/"); + // Operation Path: "api/v1/admin/text/{id}" + urlBuilder_.Append("api/v1/admin/text/"); urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); PrepareRequest(client_, request_, urlBuilder_); @@ -3146,6 +3103,16 @@ public virtual async System.Threading.Tasks.Task RemoveTextImageCommandAsync(Sys return; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 404) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); @@ -3186,7 +3153,7 @@ public virtual async System.Threading.Tasks.Task RemoveTextImageCommandAsync(Sys } /// - /// Get All Text Images Query + /// Get All Text Generations Query /// /// /// Sample request: @@ -3197,14 +3164,14 @@ public virtual async System.Threading.Tasks.Task RemoveTextImageCommandAsync(Sys /// /// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task> GetTextImagesQueryAsync() + public virtual System.Threading.Tasks.Task> GetTextPromptsQueryAsync() { - return GetTextImagesQueryAsync(System.Threading.CancellationToken.None); + return GetTextPromptsQueryAsync(System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Get All Text Images Query + /// Get All Text Generations Query /// /// /// Sample request: @@ -3215,7 +3182,7 @@ public virtual async System.Threading.Tasks.Task RemoveTextImageCommandAsync(Sys /// /// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> GetTextImagesQueryAsync(System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task> GetTextPromptsQueryAsync(System.Threading.CancellationToken cancellationToken) { var client_ = _httpClient; var disposeClient_ = false; @@ -3228,8 +3195,8 @@ public virtual async System.Threading.Tasks.Task RemoveTextImageCommandAsync(Sys var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/Image" - urlBuilder_.Append("api/v1/Image"); + // Operation Path: "api/v1/admin/text" + urlBuilder_.Append("api/v1/admin/text"); PrepareRequest(client_, request_, urlBuilder_); @@ -3256,7 +3223,7 @@ public virtual async System.Threading.Tasks.Task RemoveTextImageCommandAsync(Sys var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -3264,6 +3231,16 @@ public virtual async System.Threading.Tasks.Task RemoveTextImageCommandAsync(Sys return objectResponse_.Object; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 404) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); @@ -3304,10 +3281,26 @@ public virtual async System.Threading.Tasks.Task RemoveTextImageCommandAsync(Sys } /// - /// Creates new Image from Text + /// Creates new Text Generation session /// /// - /// Sample request: + /// Types of Text Generation are: + ///
1. Generic Prompt: A broad or open-ended request for content. + ///
- Example Prompt: “Write a short story.” + ///
- Example Response: “Once upon a time, in a quaint village, there lived a curious cat named Whiskers…” + ///
2. Specific Prompt: A prompt with clear instructions or a specific topic. + ///
- Example Prompt: “Describe the process of photosynthesis.” + ///
- Example Response: “Photosynthesis is the process by which plants convert sunlight into energy, using chlorophyll in their leaves…” + ///
3. Visual Prompt: A prompt related to an image or visual content. + ///
- Example Prompt: “Describe this image: ‘A serene sunset over the ocean.’” + ///
- Example Response: “The sun dipped below the horizon, casting hues of orange and pink across the calm waters…” + ///
4. Role-Based Prompt: A prompt where you assume a specific role or context. + ///
- Example Prompt: “Act as a travel guide.Describe the beauty of the Swiss Alps.” + ///
- Example Response: “Welcome to the majestic Swiss Alps! Snow-capped peaks, pristine lakes, and charming villages await…” + ///
5. Output Format Specification: A prompt specifying the desired output format. + ///
- Example Prompt: “Summarize the key findings of the research paper.” + ///
- Example Response: “The paper discusses novel algorithms for optimizing neural network training, achieving faster convergence…” + ///
Sample request: ///
///
HttpPost Body ///
{ @@ -3319,17 +3312,33 @@ public virtual async System.Threading.Tasks.Task RemoveTextImageCommandAsync(Sys ///
/// Created /// A server side error occurred. - public virtual System.Threading.Tasks.Task CreateTextToImageCommandAsync(CreateTextToImageCommand body) + public virtual System.Threading.Tasks.Task CreateTextPromptCommandAsync(CreateTextPromptCommand body) { - return CreateTextToImageCommandAsync(body, System.Threading.CancellationToken.None); + return CreateTextPromptCommandAsync(body, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Creates new Image from Text + /// Creates new Text Generation session /// /// - /// Sample request: + /// Types of Text Generation are: + ///
1. Generic Prompt: A broad or open-ended request for content. + ///
- Example Prompt: “Write a short story.” + ///
- Example Response: “Once upon a time, in a quaint village, there lived a curious cat named Whiskers…” + ///
2. Specific Prompt: A prompt with clear instructions or a specific topic. + ///
- Example Prompt: “Describe the process of photosynthesis.” + ///
- Example Response: “Photosynthesis is the process by which plants convert sunlight into energy, using chlorophyll in their leaves…” + ///
3. Visual Prompt: A prompt related to an image or visual content. + ///
- Example Prompt: “Describe this image: ‘A serene sunset over the ocean.’” + ///
- Example Response: “The sun dipped below the horizon, casting hues of orange and pink across the calm waters…” + ///
4. Role-Based Prompt: A prompt where you assume a specific role or context. + ///
- Example Prompt: “Act as a travel guide.Describe the beauty of the Swiss Alps.” + ///
- Example Response: “Welcome to the majestic Swiss Alps! Snow-capped peaks, pristine lakes, and charming villages await…” + ///
5. Output Format Specification: A prompt specifying the desired output format. + ///
- Example Prompt: “Summarize the key findings of the research paper.” + ///
- Example Response: “The paper discusses novel algorithms for optimizing neural network training, achieving faster convergence…” + ///
Sample request: ///
///
HttpPost Body ///
{ @@ -3341,7 +3350,7 @@ public virtual System.Threading.Tasks.Task CreateTextToImageComman ///
/// Created /// A server side error occurred. - public virtual async System.Threading.Tasks.Task CreateTextToImageCommandAsync(CreateTextToImageCommand body, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task CreateTextPromptCommandAsync(CreateTextPromptCommand body, System.Threading.CancellationToken cancellationToken) { var client_ = _httpClient; var disposeClient_ = false; @@ -3358,8 +3367,8 @@ public virtual async System.Threading.Tasks.Task CreateTextToImage var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/Image" - urlBuilder_.Append("api/v1/Image"); + // Operation Path: "api/v1/admin/text" + urlBuilder_.Append("api/v1/admin/text"); PrepareRequest(client_, request_, urlBuilder_); @@ -3386,7 +3395,7 @@ public virtual async System.Threading.Tasks.Task CreateTextToImage var status_ = (int)response_.StatusCode; if (status_ == 201) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -3404,6 +3413,16 @@ public virtual async System.Threading.Tasks.Task CreateTextToImage throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 500) { string responseText_ = ( response_.Content == null ) ? string.Empty : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); @@ -3434,7 +3453,7 @@ public virtual async System.Threading.Tasks.Task CreateTextToImage } /// - /// Get Text Images Paginated Query + /// Get Text Generations Paginated Query /// /// /// Sample request: @@ -3447,14 +3466,14 @@ public virtual async System.Threading.Tasks.Task CreateTextToImage /// /// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetTextImagesPaginatedQueryAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize) + public virtual System.Threading.Tasks.Task GetTextPromptsPaginatedQueryAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize) { - return GetTextImagesPaginatedQueryAsync(startDate, endDate, pageNumber, pageSize, System.Threading.CancellationToken.None); + return GetTextPromptsPaginatedQueryAsync(startDate, endDate, pageNumber, pageSize, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Get Text Images Paginated Query + /// Get Text Generations Paginated Query /// /// /// Sample request: @@ -3467,7 +3486,7 @@ public virtual System.Threading.Tasks.Task GetTextIma /// /// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetTextImagesPaginatedQueryAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task GetTextPromptsPaginatedQueryAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize, System.Threading.CancellationToken cancellationToken) { var client_ = _httpClient; var disposeClient_ = false; @@ -3480,8 +3499,8 @@ public virtual async System.Threading.Tasks.Task GetT var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/Image/Paginated" - urlBuilder_.Append("api/v1/Image/Paginated"); + // Operation Path: "api/v1/admin/text/Paginated" + urlBuilder_.Append("api/v1/admin/text/Paginated"); urlBuilder_.Append('?'); if (startDate != null) { @@ -3526,7 +3545,7 @@ public virtual async System.Threading.Tasks.Task GetT var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -3534,6 +3553,16 @@ public virtual async System.Threading.Tasks.Task GetT return objectResponse_.Object; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 500) { string responseText_ = ( response_.Content == null ) ? string.Empty : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); @@ -3564,7 +3593,7 @@ public virtual async System.Threading.Tasks.Task GetT } /// - /// Get Text Generation session with history + /// Retrieves the actor profile by external ID, including session history. /// /// /// Sample request: @@ -3574,14 +3603,14 @@ public virtual async System.Threading.Tasks.Task GetT /// /// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetTextPromptQueryAsync(System.Guid id) + public virtual System.Threading.Tasks.Task GetMyActorProfileAsync() { - return GetTextPromptQueryAsync(id, System.Threading.CancellationToken.None); + return GetMyActorProfileAsync(System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Get Text Generation session with history + /// Retrieves the actor profile by external ID, including session history. /// /// /// Sample request: @@ -3591,11 +3620,8 @@ public virtual System.Threading.Tasks.Task GetTextPromptQueryAsyn /// /// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetTextPromptQueryAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task GetMyActorProfileAsync(System.Threading.CancellationToken cancellationToken) { - if (id == null) - throw new System.ArgumentNullException("id"); - var client_ = _httpClient; var disposeClient_ = false; try @@ -3607,9 +3633,8 @@ public virtual async System.Threading.Tasks.Task GetTextPromptQue var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/TextGeneration/{id}" - urlBuilder_.Append("api/v1/TextGeneration/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); + // Operation Path: "api/v1my/actors" + urlBuilder_.Append("api/v1my/actors"); PrepareRequest(client_, request_, urlBuilder_); @@ -3636,7 +3661,7 @@ public virtual async System.Threading.Tasks.Task GetTextPromptQue var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -3644,6 +3669,16 @@ public virtual async System.Threading.Tasks.Task GetTextPromptQue return objectResponse_.Object; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 404) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); @@ -3684,51 +3719,64 @@ public virtual async System.Threading.Tasks.Task GetTextPromptQue } /// - /// Remove TextGeneration Command + /// Creates a new actor session with empty history. /// /// /// Sample request: ///
- ///
"Id": 60fb5e99-3a78-43df-a512-7d8ff498499e - ///
"api-version": 1.0 + ///
HttpPost Body + ///
{ + ///
"Id": 00000000-0000-0000-0000-000000000000, + ///
"Name": "John Doe" + ///
} + ///
+ ///
"version": 1.0 ///
- /// No Content + /// The command containing actor creation details. + /// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task RemoveTextPromptCommandAsync(System.Guid id) + public virtual System.Threading.Tasks.Task SaveMyActorAsync(SaveMyActorCommand body) { - return RemoveTextPromptCommandAsync(id, System.Threading.CancellationToken.None); + return SaveMyActorAsync(body, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Remove TextGeneration Command + /// Creates a new actor session with empty history. /// /// /// Sample request: ///
- ///
"Id": 60fb5e99-3a78-43df-a512-7d8ff498499e - ///
"api-version": 1.0 + ///
HttpPost Body + ///
{ + ///
"Id": 00000000-0000-0000-0000-000000000000, + ///
"Name": "John Doe" + ///
} + ///
+ ///
"version": 1.0 ///
- /// No Content + /// The command containing actor creation details. + /// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task RemoveTextPromptCommandAsync(System.Guid id, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task SaveMyActorAsync(SaveMyActorCommand body, System.Threading.CancellationToken cancellationToken) { - if (id == null) - throw new System.ArgumentNullException("id"); - var client_ = _httpClient; var disposeClient_ = false; try { using (var request_ = new System.Net.Http.HttpRequestMessage()) { - request_.Method = new System.Net.Http.HttpMethod("DELETE"); + var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); + var content_ = new System.Net.Http.ByteArrayContent(json_); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/TextGeneration/{id}" - urlBuilder_.Append("api/v1/TextGeneration/"); - urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); + // Operation Path: "api/v1my/actors" + urlBuilder_.Append("api/v1my/actors"); PrepareRequest(client_, request_, urlBuilder_); @@ -3749,23 +3797,48 @@ public virtual async System.Threading.Tasks.Task RemoveTextPromptCommandAsync(Sy foreach (var item_ in response_.Content.Headers) headers_[item_.Key] = item_.Value; } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 204) + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 201) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) { - return; + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else - if (status_ == 404) + if (status_ == 401) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); } - throw new ApiException("Not Found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 500) @@ -3798,36 +3871,38 @@ public virtual async System.Threading.Tasks.Task RemoveTextPromptCommandAsync(Sy } /// - /// Get All Text Generations Query + /// Retrieves all chat sessions for the specified actor. /// /// /// Sample request: ///
- ///
"StartDate": "2024-06-01T00:00:00Z" + ///
"ActorId": 60fb5e99-3a78-43df-a512-7d8ff498499e + ///
"StartDate": "2024-06-01T00:00:00Z"s ///
"EndDate": "2024-12-01T00:00:00Z" ///
"api-version": 1.0 ///
/// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task> GetTextPromptsQueryAsync() + public virtual System.Threading.Tasks.Task> GetMyChatSessionsAsync() { - return GetTextPromptsQueryAsync(System.Threading.CancellationToken.None); + return GetMyChatSessionsAsync(System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Get All Text Generations Query + /// Retrieves all chat sessions for the specified actor. /// /// /// Sample request: ///
- ///
"StartDate": "2024-06-01T00:00:00Z" + ///
"ActorId": 60fb5e99-3a78-43df-a512-7d8ff498499e + ///
"StartDate": "2024-06-01T00:00:00Z"s ///
"EndDate": "2024-12-01T00:00:00Z" ///
"api-version": 1.0 ///
/// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> GetTextPromptsQueryAsync(System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task> GetMyChatSessionsAsync(System.Threading.CancellationToken cancellationToken) { var client_ = _httpClient; var disposeClient_ = false; @@ -3840,8 +3915,8 @@ public virtual async System.Threading.Tasks.Task RemoveTextPromptCommandAsync(Sy var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/TextGeneration" - urlBuilder_.Append("api/v1/TextGeneration"); + // Operation Path: "api/v1/my/chat/ChatSessions" + urlBuilder_.Append("api/v1/my/chat/ChatSessions"); PrepareRequest(client_, request_, urlBuilder_); @@ -3868,7 +3943,7 @@ public virtual async System.Threading.Tasks.Task RemoveTextPromptCommandAsync(Sy var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -3876,6 +3951,16 @@ public virtual async System.Threading.Tasks.Task RemoveTextPromptCommandAsync(Sy return objectResponse_.Object; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 404) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); @@ -3916,76 +4001,48 @@ public virtual async System.Threading.Tasks.Task RemoveTextPromptCommandAsync(Sy } /// - /// Creates new Text Generation session + /// Retrieves paginated chat sessions for the specified actor within an optional date range. /// /// - /// Types of Text Generation are: - ///
1. Generic Prompt: A broad or open-ended request for content. - ///
- Example Prompt: “Write a short story.” - ///
- Example Response: “Once upon a time, in a quaint village, there lived a curious cat named Whiskers…” - ///
2. Specific Prompt: A prompt with clear instructions or a specific topic. - ///
- Example Prompt: “Describe the process of photosynthesis.” - ///
- Example Response: “Photosynthesis is the process by which plants convert sunlight into energy, using chlorophyll in their leaves…” - ///
3. Visual Prompt: A prompt related to an image or visual content. - ///
- Example Prompt: “Describe this image: ‘A serene sunset over the ocean.’” - ///
- Example Response: “The sun dipped below the horizon, casting hues of orange and pink across the calm waters…” - ///
4. Role-Based Prompt: A prompt where you assume a specific role or context. - ///
- Example Prompt: “Act as a travel guide.Describe the beauty of the Swiss Alps.” - ///
- Example Response: “Welcome to the majestic Swiss Alps! Snow-capped peaks, pristine lakes, and charming villages await…” - ///
5. Output Format Specification: A prompt specifying the desired output format. - ///
- Example Prompt: “Summarize the key findings of the research paper.” - ///
- Example Response: “The paper discusses novel algorithms for optimizing neural network training, achieving faster convergence…” - ///
Sample request: - ///
- ///
HttpPost Body - ///
{ - ///
"Id": 00000000-0000-0000-0000-000000000000, - ///
"Message": "Hi, I am interested in learning about Semantic Kernel." - ///
} + /// Sample request: ///
- ///
"version": 1.0 + ///
"StartDate": "2024-06-01T00:00:00Z" + ///
"EndDate": "2024-12-01T00:00:00Z" + ///
"PageNumber": 1 + ///
"PageSize" : 10 + ///
"api-version": 1.0 ///
- /// Created + /// The start date for filtering sessions (optional). + /// The end date for filtering sessions (optional). + /// The page number for pagination (default is 1). + /// The page size for pagination (default is 10). + /// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task CreateTextPromptCommandAsync(CreateTextPromptCommand body) + public virtual System.Threading.Tasks.Task GetMyChatSessionsPaginatedAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize) { - return CreateTextPromptCommandAsync(body, System.Threading.CancellationToken.None); + return GetMyChatSessionsPaginatedAsync(startDate, endDate, pageNumber, pageSize, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Creates new Text Generation session + /// Retrieves paginated chat sessions for the specified actor within an optional date range. /// /// - /// Types of Text Generation are: - ///
1. Generic Prompt: A broad or open-ended request for content. - ///
- Example Prompt: “Write a short story.” - ///
- Example Response: “Once upon a time, in a quaint village, there lived a curious cat named Whiskers…” - ///
2. Specific Prompt: A prompt with clear instructions or a specific topic. - ///
- Example Prompt: “Describe the process of photosynthesis.” - ///
- Example Response: “Photosynthesis is the process by which plants convert sunlight into energy, using chlorophyll in their leaves…” - ///
3. Visual Prompt: A prompt related to an image or visual content. - ///
- Example Prompt: “Describe this image: ‘A serene sunset over the ocean.’” - ///
- Example Response: “The sun dipped below the horizon, casting hues of orange and pink across the calm waters…” - ///
4. Role-Based Prompt: A prompt where you assume a specific role or context. - ///
- Example Prompt: “Act as a travel guide.Describe the beauty of the Swiss Alps.” - ///
- Example Response: “Welcome to the majestic Swiss Alps! Snow-capped peaks, pristine lakes, and charming villages await…” - ///
5. Output Format Specification: A prompt specifying the desired output format. - ///
- Example Prompt: “Summarize the key findings of the research paper.” - ///
- Example Response: “The paper discusses novel algorithms for optimizing neural network training, achieving faster convergence…” - ///
Sample request: - ///
- ///
HttpPost Body - ///
{ - ///
"Id": 00000000-0000-0000-0000-000000000000, - ///
"Message": "Hi, I am interested in learning about Semantic Kernel." - ///
} + /// Sample request: ///
- ///
"version": 1.0 + ///
"StartDate": "2024-06-01T00:00:00Z" + ///
"EndDate": "2024-12-01T00:00:00Z" + ///
"PageNumber": 1 + ///
"PageSize" : 10 + ///
"api-version": 1.0 ///
- /// Created + /// The start date for filtering sessions (optional). + /// The end date for filtering sessions (optional). + /// The page number for pagination (default is 1). + /// The page size for pagination (default is 10). + /// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task CreateTextPromptCommandAsync(CreateTextPromptCommand body, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task GetMyChatSessionsPaginatedAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize, System.Threading.CancellationToken cancellationToken) { var client_ = _httpClient; var disposeClient_ = false; @@ -3993,17 +4050,31 @@ public virtual async System.Threading.Tasks.Task CreateTextPrompt { using (var request_ = new System.Net.Http.HttpRequestMessage()) { - var json_ = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(body, JsonSerializerSettings); - var content_ = new System.Net.Http.ByteArrayContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Method = new System.Net.Http.HttpMethod("GET"); request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/TextGeneration" - urlBuilder_.Append("api/v1/TextGeneration"); + // Operation Path: "api/v1/my/chat/Paginated" + urlBuilder_.Append("api/v1/my/chat/Paginated"); + urlBuilder_.Append('?'); + if (startDate != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("startDate")).Append('=').Append(System.Uri.EscapeDataString(startDate.Value.ToString("s", System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } + if (endDate != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("endDate")).Append('=').Append(System.Uri.EscapeDataString(endDate.Value.ToString("s", System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } + if (pageNumber != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("pageNumber")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(pageNumber, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } + if (pageSize != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("pageSize")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(pageSize, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } + urlBuilder_.Length--; PrepareRequest(client_, request_, urlBuilder_); @@ -4028,9 +4099,9 @@ public virtual async System.Threading.Tasks.Task CreateTextPrompt ProcessResponse(client_, response_); var status_ = (int)response_.StatusCode; - if (status_ == 201) + if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -4038,14 +4109,14 @@ public virtual async System.Threading.Tasks.Task CreateTextPrompt return objectResponse_.Object; } else - if (status_ == 400) + if (status_ == 401) { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); } - throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 500) @@ -4078,41 +4149,42 @@ public virtual async System.Threading.Tasks.Task CreateTextPrompt } /// - /// Get Text Generations Paginated Query + /// Retrieves a specific chat session for the actor by session ID. /// /// /// Sample request: ///
- ///
"StartDate": "2024-06-01T00:00:00Z" - ///
"EndDate": "2024-12-01T00:00:00Z" - ///
"PageNumber": 1 - ///
"PageSize" : 10 + ///
"ActorId": 60fb5e99-3a78-43df-a512-7d8ff498499e + ///
"ChatSessionId": 1efb5e99-3a78-43df-a512-7d8ff498499e ///
"api-version": 1.0 ///
+ /// The identifier of the chat session. /// OK /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetTextPromptsPaginatedQueryAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize) + public virtual System.Threading.Tasks.Task GetMyChatSessionAsync(System.Guid chatSessionId) { - return GetTextPromptsPaginatedQueryAsync(startDate, endDate, pageNumber, pageSize, System.Threading.CancellationToken.None); + return GetMyChatSessionAsync(chatSessionId, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// - /// Get Text Generations Paginated Query + /// Retrieves a specific chat session for the actor by session ID. /// /// /// Sample request: ///
- ///
"StartDate": "2024-06-01T00:00:00Z" - ///
"EndDate": "2024-12-01T00:00:00Z" - ///
"PageNumber": 1 - ///
"PageSize" : 10 + ///
"ActorId": 60fb5e99-3a78-43df-a512-7d8ff498499e + ///
"ChatSessionId": 1efb5e99-3a78-43df-a512-7d8ff498499e ///
"api-version": 1.0 ///
+ /// The identifier of the chat session. /// OK /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetTextPromptsPaginatedQueryAsync(System.DateTimeOffset? startDate, System.DateTimeOffset? endDate, int? pageNumber, int? pageSize, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task GetMyChatSessionAsync(System.Guid chatSessionId, System.Threading.CancellationToken cancellationToken) { + if (chatSessionId == null) + throw new System.ArgumentNullException("chatSessionId"); + var client_ = _httpClient; var disposeClient_ = false; try @@ -4124,26 +4196,9 @@ public virtual async System.Threading.Tasks.Task Get var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "api/v1/TextGeneration/Paginated" - urlBuilder_.Append("api/v1/TextGeneration/Paginated"); - urlBuilder_.Append('?'); - if (startDate != null) - { - urlBuilder_.Append(System.Uri.EscapeDataString("StartDate")).Append('=').Append(System.Uri.EscapeDataString(startDate.Value.ToString("s", System.Globalization.CultureInfo.InvariantCulture))).Append('&'); - } - if (endDate != null) - { - urlBuilder_.Append(System.Uri.EscapeDataString("EndDate")).Append('=').Append(System.Uri.EscapeDataString(endDate.Value.ToString("s", System.Globalization.CultureInfo.InvariantCulture))).Append('&'); - } - if (pageNumber != null) - { - urlBuilder_.Append(System.Uri.EscapeDataString("PageNumber")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(pageNumber, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); - } - if (pageSize != null) - { - urlBuilder_.Append(System.Uri.EscapeDataString("PageSize")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(pageSize, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); - } - urlBuilder_.Length--; + // Operation Path: "api/v1/my/chat/{chatSessionId}" + urlBuilder_.Append("api/v1/my/chat/"); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(chatSessionId, System.Globalization.CultureInfo.InvariantCulture))); PrepareRequest(client_, request_, urlBuilder_); @@ -4170,7 +4225,7 @@ public virtual async System.Threading.Tasks.Task Get var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -4178,6 +4233,26 @@ public virtual async System.Threading.Tasks.Task Get return objectResponse_.Object; } else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not Found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else if (status_ == 500) { string responseText_ = ( response_.Content == null ) ? string.Empty : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); @@ -4293,10 +4368,10 @@ private string ConvertToString(object value, System.Globalization.CultureInfo cu var name = System.Enum.GetName(value.GetType(), value); if (name != null) { - var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); - if (field != null) + var field_ = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); + if (field_ != null) { - var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) + var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field_, typeof(System.Runtime.Serialization.EnumMemberAttribute)) as System.Runtime.Serialization.EnumMemberAttribute; if (attribute != null) { @@ -4336,8 +4411,8 @@ private string ConvertToString(object value, System.Globalization.CultureInfo cu } } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class AuthorDto + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class ActorDto { [System.Text.Json.Serialization.JsonPropertyName("Id")] @@ -4346,6 +4421,21 @@ public partial class AuthorDto [System.Text.Json.Serialization.JsonPropertyName("Name")] public string Name { get; set; } + [System.Text.Json.Serialization.JsonPropertyName("FirstName")] + public string FirstName { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("LastName")] + public string LastName { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("Email")] + public string Email { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("OwnerId")] + public System.Guid OwnerId { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("TenantId")] + public System.Guid TenantId { get; set; } + [System.Text.Json.Serialization.JsonPropertyName("CreatedOn")] public System.DateTimeOffset CreatedOn { get; set; } @@ -4357,7 +4447,7 @@ public partial class AuthorDto } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class ByteReadOnlyMemory { @@ -4372,7 +4462,7 @@ public partial class ByteReadOnlyMemory } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class ByteReadOnlySpan { @@ -4384,7 +4474,7 @@ public partial class ByteReadOnlySpan } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class ChatMessageDto { @@ -4405,7 +4495,7 @@ public partial class ChatMessageDto } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class ChatMessageDtoPaginatedList { @@ -4429,7 +4519,7 @@ public partial class ChatMessageDtoPaginatedList } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class ChatSessionDto { @@ -4439,8 +4529,8 @@ public partial class ChatSessionDto [System.Text.Json.Serialization.JsonPropertyName("Title")] public string Title { get; set; } - [System.Text.Json.Serialization.JsonPropertyName("AuthorId")] - public System.Guid AuthorId { get; set; } + [System.Text.Json.Serialization.JsonPropertyName("ActorId")] + public System.Guid ActorId { get; set; } [System.Text.Json.Serialization.JsonPropertyName("Timestamp")] public System.DateTimeOffset Timestamp { get; set; } @@ -4450,7 +4540,7 @@ public partial class ChatSessionDto } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class ChatSessionDtoPaginatedList { @@ -4474,19 +4564,7 @@ public partial class ChatSessionDtoPaginatedList } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class CreateAuthorCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("Id")] - public System.Guid Id { get; set; } - - [System.Text.Json.Serialization.JsonPropertyName("Name")] - public string Name { get; set; } - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class CreateChatMessageCommand { @@ -4499,20 +4577,20 @@ public partial class CreateChatMessageCommand [System.Text.Json.Serialization.JsonPropertyName("Message")] public string Message { get; set; } + [System.Text.Json.Serialization.JsonPropertyName("UserInfo")] + public IUserEntity UserInfo { get; set; } + } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class CreateChatSessionCommand { [System.Text.Json.Serialization.JsonPropertyName("Id")] public System.Guid Id { get; set; } - [System.Text.Json.Serialization.JsonPropertyName("AuthorId")] - public System.Guid AuthorId { get; set; } - - [System.Text.Json.Serialization.JsonPropertyName("AuthorName")] - public string AuthorName { get; set; } + [System.Text.Json.Serialization.JsonPropertyName("ActorId")] + public System.Guid ActorId { get; set; } [System.Text.Json.Serialization.JsonPropertyName("Title")] public string Title { get; set; } @@ -4522,7 +4600,7 @@ public partial class CreateChatSessionCommand } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class CreateTextPromptCommand { @@ -4534,22 +4612,22 @@ public partial class CreateTextPromptCommand } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class CreateTextToAudioCommand { [System.Text.Json.Serialization.JsonPropertyName("Id")] public System.Guid Id { get; set; } - [System.Text.Json.Serialization.JsonPropertyName("AuthorId")] - public System.Guid AuthorId { get; set; } + [System.Text.Json.Serialization.JsonPropertyName("ActorId")] + public System.Guid ActorId { get; set; } [System.Text.Json.Serialization.JsonPropertyName("Prompt")] public string Prompt { get; set; } } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class CreateTextToImageCommand { @@ -4567,7 +4645,43 @@ public partial class CreateTextToImageCommand } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class IUserEntity + { + + [System.Text.Json.Serialization.JsonPropertyName("OwnerId")] + public System.Guid OwnerId { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("TenantId")] + public System.Guid TenantId { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("FirstName")] + public string FirstName { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("LastName")] + public string LastName { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("Email")] + public string Email { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("Roles")] + public System.Collections.Generic.ICollection Roles { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("CanDefine")] + public bool CanDefine { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("CanMonitor")] + public bool CanMonitor { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("CanClassify")] + public bool CanClassify { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("CanMitigate")] + public bool CanMitigate { get; set; } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class PatchChatSessionCommand { @@ -4579,7 +4693,7 @@ public partial class PatchChatSessionCommand } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class ProblemDetails { @@ -4609,15 +4723,36 @@ public System.Collections.Generic.IDictionary AdditionalProperti } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class SaveMyActorCommand + { + + [System.Text.Json.Serialization.JsonPropertyName("FirstName")] + public string FirstName { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("LastName")] + public string LastName { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("Email")] + public string Email { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("TenantId")] + public System.Guid TenantId { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("UserInfo")] + public IUserEntity UserInfo { get; set; } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class TextAudioDto { [System.Text.Json.Serialization.JsonPropertyName("Id")] public System.Guid Id { get; set; } - [System.Text.Json.Serialization.JsonPropertyName("AuthorId")] - public System.Guid AuthorId { get; set; } + [System.Text.Json.Serialization.JsonPropertyName("ActorId")] + public System.Guid ActorId { get; set; } [System.Text.Json.Serialization.JsonPropertyName("Description")] public string Description { get; set; } @@ -4633,7 +4768,7 @@ public partial class TextAudioDto } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class TextAudioDtoPaginatedList { @@ -4657,15 +4792,15 @@ public partial class TextAudioDtoPaginatedList } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class TextImageDto { [System.Text.Json.Serialization.JsonPropertyName("Id")] public System.Guid Id { get; set; } - [System.Text.Json.Serialization.JsonPropertyName("AuthorId")] - public System.Guid AuthorId { get; set; } + [System.Text.Json.Serialization.JsonPropertyName("ActorId")] + public System.Guid ActorId { get; set; } [System.Text.Json.Serialization.JsonPropertyName("Description")] public string Description { get; set; } @@ -4687,7 +4822,7 @@ public partial class TextImageDto } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class TextImageDtoPaginatedList { @@ -4711,15 +4846,15 @@ public partial class TextImageDtoPaginatedList } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class TextPromptDto { [System.Text.Json.Serialization.JsonPropertyName("Id")] public System.Guid Id { get; set; } - [System.Text.Json.Serialization.JsonPropertyName("AuthorId")] - public System.Guid AuthorId { get; set; } + [System.Text.Json.Serialization.JsonPropertyName("ActorId")] + public System.Guid ActorId { get; set; } [System.Text.Json.Serialization.JsonPropertyName("Prompt")] public string Prompt { get; set; } @@ -4727,12 +4862,9 @@ public partial class TextPromptDto [System.Text.Json.Serialization.JsonPropertyName("Timestamp")] public System.DateTimeOffset Timestamp { get; set; } - [System.Text.Json.Serialization.JsonPropertyName("Responses")] - public System.Collections.Generic.ICollection Responses { get; set; } - } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class TextPromptDtoPaginatedList { @@ -4756,37 +4888,7 @@ public partial class TextPromptDtoPaginatedList } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class TextResponseDto - { - - [System.Text.Json.Serialization.JsonPropertyName("Id")] - public System.Guid Id { get; set; } - - [System.Text.Json.Serialization.JsonPropertyName("TextPromptId")] - public System.Guid TextPromptId { get; set; } - - [System.Text.Json.Serialization.JsonPropertyName("Response")] - public string Response { get; set; } - - [System.Text.Json.Serialization.JsonPropertyName("Timestamp")] - public System.DateTimeOffset Timestamp { get; set; } - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class UpdateAuthorCommand - { - - [System.Text.Json.Serialization.JsonPropertyName("Id")] - public System.Guid Id { get; set; } - - [System.Text.Json.Serialization.JsonPropertyName("Name")] - public string Name { get; set; } - - } - - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class UpdateChatSessionCommand { @@ -4803,7 +4905,7 @@ public partial class UpdateChatSessionCommand - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class ApiException : System.Exception { public int StatusCode { get; private set; } @@ -4826,7 +4928,7 @@ public override string ToString() } } - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.5.0.0 (NJsonSchema v11.4.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.6.3.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class ApiException : ApiException { public TResult Result { get; private set; } diff --git a/src/Presentation.Blazor/Components/App.razor b/src/Presentation.Blazor/Components/App.razor index 4c57fb6..7203064 100644 --- a/src/Presentation.Blazor/Components/App.razor +++ b/src/Presentation.Blazor/Components/App.razor @@ -1,6 +1,4 @@ -@using Goodtocode.SemanticKernel.Presentation.Blazor.Components.Analytics - -@namespace Goodtocode.SemanticKernel.Presentation.Blazor.Components +@using Cannery.Aspects.Components.Analytics @inject IConfiguration Configuration @@ -10,10 +8,9 @@ - - + - + @@ -21,6 +18,7 @@ + diff --git a/src/Presentation.Blazor/Components/Layout/Error.razor b/src/Presentation.Blazor/Components/Layout/Error.razor new file mode 100644 index 0000000..91db7df --- /dev/null +++ b/src/Presentation.Blazor/Components/Layout/Error.razor @@ -0,0 +1,40 @@ +@page "/Error" +@using System.Diagnostics + +Error + + + + Error. + An error occurred while processing your request. + + @if (ShowRequestId) + { + + Request ID: @RequestId + + } + + Development Mode + + Swapping to Development environment will display more detailed information about the error that occurred. + + + The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users.
+ For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +
+
+
+ +@code { + [CascadingParameter] + private HttpContext? HttpContext { get; set; } + + private string? RequestId { get; set; } + private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + + protected override void OnInitialized() => + RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; +} diff --git a/src/Presentation.Blazor/Components/Layout/MainLayout.razor b/src/Presentation.Blazor/Components/Layout/MainLayout.razor index 2328c56..0fe930c 100644 --- a/src/Presentation.Blazor/Components/Layout/MainLayout.razor +++ b/src/Presentation.Blazor/Components/Layout/MainLayout.razor @@ -1,38 +1,92 @@ -@namespace Goodtocode.SemanticKernel.Presentation.Blazor.Components.Layout +@using Cannery.Aspects.Components.Auth.Components +@using Cannery.Aspects.Components.Auth.Routing +@using Cannery.Aspects.Components.Auth.Services +@using Microsoft.AspNetCore.Components.Authorization +@using Microsoft.Identity.Web @inherits LayoutComponentBase +@inject AuthenticationStateProvider AuthStateProvider @inject NavigationManager Navigation +@inject IUserSyncService UserSyncService - - - -
-
-
+ +
+ + + Cloud in a Can + + + Semantic Kernel + + + +
+ + + + @Body -
-
-
-
- -
-
-
- Oops! Something went wrong.
- @ex.Message -
- -
-
-
-
+ + + + + + + + Oops! Something went wrong. + + + @ex.Message + + + + Go Home + + + + + + + + + + Home + + Chat Session + + + @code { private ErrorBoundary? errorBoundaryRef; - private void OnRecoverAndNavigate() + private void OnRecoverAndNavigate(Exception ex) { errorBoundaryRef?.Recover(); Navigation.NavigateTo("/", forceLoad: true); } -} + + private bool _synced = false; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + try + { + if (firstRender && !_synced) + { + var authState = await AuthStateProvider.GetAuthenticationStateAsync(); + var user = authState.User; + + if (user.Identity?.IsAuthenticated == true) + { + await UserSyncService.SyncUserAsync(user); + _synced = true; + } + } + } + catch (MicrosoftIdentityWebChallengeUserException) + { + Navigation.NavigateTo(RouteConstants.SignIn, forceLoad: true); + } + } +} \ No newline at end of file diff --git a/src/Presentation.Blazor/Components/Layout/MainLayout.razor.css b/src/Presentation.Blazor/Components/Layout/MainLayout.razor.css deleted file mode 100644 index a4b0fb0..0000000 --- a/src/Presentation.Blazor/Components/Layout/MainLayout.razor.css +++ /dev/null @@ -1,39 +0,0 @@ -@media (max-width: 640.98px) { - .top-row { - justify-content: space-between; - } -} - -main { - flex: 1; -} - -.page { - position: relative; - display: flex; - flex-direction: column; - height: 100vh; -} - -.error-dialog-backdrop { - position: fixed; - top: 10vh; - left: 0; - width: 100vw; - height: 90vh; - background: rgba(0,0,0,0.3); - display: flex; - align-items: center; - justify-content: center; - z-index: 1050; -} - -.error-dialog { - background: #fff; - border-radius: 8px; - box-shadow: 0 4px 32px rgba(0,0,0,0.2); - padding: 2rem 2.5rem; - min-width: 320px; - max-width: 90vw; - text-align: center; -} diff --git a/src/Presentation.Blazor/Components/Layout/NavMenu.razor b/src/Presentation.Blazor/Components/Layout/NavMenu.razor deleted file mode 100644 index 71543ed..0000000 --- a/src/Presentation.Blazor/Components/Layout/NavMenu.razor +++ /dev/null @@ -1,75 +0,0 @@ -@using Microsoft.AspNetCore.Components.Web - -@namespace Goodtocode.SemanticKernel.Presentation.Blazor.Components.Layout - -
- - @if (navMenuOpened) - { - - } -
- -@code { - private bool navMenuOpened = false; - - private void ToggleNavMenu() - { - navMenuOpened = !navMenuOpened; - } - - private void CloseNavMenu() - { - navMenuOpened = false; - } -} \ No newline at end of file diff --git a/src/Presentation.Blazor/Components/Layout/NavMenu.razor.css b/src/Presentation.Blazor/Components/Layout/NavMenu.razor.css deleted file mode 100644 index 2ac7ce3..0000000 --- a/src/Presentation.Blazor/Components/Layout/NavMenu.razor.css +++ /dev/null @@ -1,129 +0,0 @@ -.navbar-brand { - font-size: 1.1rem; -} - -.mobile-nav { - position: absolute; - top: 56px; - left: 0; - right: 0; - background: #fff; - z-index: 1050; - display: none; - box-shadow: 0 2px 8px rgba(0,0,0,0.1); - transition: top 0.3s; -} - - .mobile-nav.show { - display: block; - } - -.nav-item { - font-size: 0.9rem; - text-decoration: none; -} - .nav-item ::deep .nav-link { - color: var(--bs-navbar-link-color); - border: none; - border-radius: 4px; - display: flex; - align-items: center; - width: 100%; - } - - .nav-item ::deep a.active { - color: var(--gtc-primary-bg); - text-decoration: none; - } - - .nav-item ::deep .nav-link:hover { - color: var(--gtc-primary-bg); - text-decoration: none; - } - -.nav-scrollable { - display: none; -} - -.nav-backdrop { - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - background: rgba(0,0,0,0.3); - z-index: 1040; -} - -.bi { - display: inline-block; - position: relative; - width: 1.25rem; - height: 1.25rem; - margin-top: 0.2rem; - margin-right: 0.75rem; - background-size: cover; -} - -.bi-house-door-fill-nav-menu { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E"); -} - -.bi-plus-square-fill-nav-menu { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E"); -} - -.bi-list-nested-nav-menu { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E"); -} - -.bi-chat-dots-fill-nav-menu { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' class='bi bi-chat-dots' viewBox='0 0 16 16'%3E%3Cpath d='M8 3a5 5 0 1 0 4.546 2.914.5.5 0 1 1 .898-.442A6 6 0 1 1 8 2v1zm2.528 3.529a.5.5 0 0 1 0 .707l-5.25 5.25a.5.5 0 0 1-.732-.681l.086-.087 5.25-5.25a.5.5 0 0 1 .707 0zM6 8c-.601 0-1-.398-1-1s.399-1 1-1 1 .398 1 1-.399 1-1 1zm3 1c-.601 0-1-.398-1-1s.399-1 1-1 1 .398 1 1-.399 1-1 1z'/%3E%3C/svg%3E"); -} - -@media (max-width: 768px) { - .navbar-collapse.d-md-flex - -{ - display: none !important; -} - -.mobile-nav { - display: none; - position: absolute; - left: 0; - width: 100%; - background: #f8f9fa; - z-index: 1050; - border-bottom: 1px solid #dee2e6; -} - - .mobile-nav.show { - display: block; - } - - .mobile-nav .navbar-nav { - flex-direction: column; - align-items: flex-start; - } - - .mobile-nav .nav-link { - width: 100%; - text-align: left; - } - -} - -@media (min-width: 769px) { - .mobile-nav { - display: none !important; - } - - .navbar-collapse.d-md-flex { - display: flex !important; - } - - .navbar-nav.flex-row { - flex-direction: row !important; - } -} \ No newline at end of file diff --git a/src/Presentation.Blazor/Components/Layout/NotFound.razor b/src/Presentation.Blazor/Components/Layout/NotFound.razor new file mode 100644 index 0000000..5e15fcd --- /dev/null +++ b/src/Presentation.Blazor/Components/Layout/NotFound.razor @@ -0,0 +1,26 @@ +@page "/NotFound" + +
+
+

404: Resource Not Found

+

+ Error: The page you are looking for is either in another castle,
+ or someone forgot to update the DNS records. +

+ 404 Cat +

+ + // TODO: Check if the URL is spelled correctly
+ // TODO: Blame the network team
+ // TODO: Clear your cache (it never helps, but we have to say it) +
+

+

+ Return to Dashboard +

+ + If you think this is a bug, please submit a ticket.
+ (But first, have you tried turning it off and on again?) +
+
+
diff --git a/src/Presentation.Blazor/Components/Routes.razor b/src/Presentation.Blazor/Components/Routes.razor index 4b367bd..334ebe2 100644 --- a/src/Presentation.Blazor/Components/Routes.razor +++ b/src/Presentation.Blazor/Components/Routes.razor @@ -1,8 +1,27 @@ -@namespace Goodtocode.SemanticKernel.Presentation.Blazor.Components +@using Cannery.Aspects.Components.Auth.Routing +@using Goodtocode.SemanticKernel.Presentation.Blazor.Components.Layout +@using Microsoft.AspNetCore.Components.Authorization - - - - - - + + + + + + @if (!(context.User.Identity?.IsAuthenticated ?? false)) + { + + } + else + { + + } + + + + + + + + + + diff --git a/src/Presentation.Blazor/Components/Skeleton/SkeletonList.razor.css b/src/Presentation.Blazor/Components/Skeleton/SkeletonList.razor.css deleted file mode 100644 index 1814f90..0000000 --- a/src/Presentation.Blazor/Components/Skeleton/SkeletonList.razor.css +++ /dev/null @@ -1,23 +0,0 @@ -.skeleton-loader { - display: flex; - flex-direction: column; - gap: 10px; -} - -.skeleton-box { - width: 100%; - height: 40px; - background: linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%); - background-size: 200% 100%; - animation: loading 1.5s infinite; -} - -@keyframes loading { - 0% { - background-position: 200% 0; - } - - 100% { - background-position: -200% 0; - } -} diff --git a/src/Presentation.Blazor/Components/_Imports.razor b/src/Presentation.Blazor/Components/_Imports.razor index e7b30e0..abc971c 100644 --- a/src/Presentation.Blazor/Components/_Imports.razor +++ b/src/Presentation.Blazor/Components/_Imports.razor @@ -1,10 +1,12 @@ -@using System.Net.Http -@using System.Net.Http.Json -@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Components.Web @using static Microsoft.AspNetCore.Components.Web.RenderMode @using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.FluentUI.AspNetCore.Components +@using Icons = Microsoft.FluentUI.AspNetCore.Components.Icons @using Microsoft.JSInterop -@using Goodtocode.SemanticKernel.Presentation.Blazor -@using Goodtocode.SemanticKernel.Presentation.Blazor.Components \ No newline at end of file +@using System.ComponentModel.DataAnnotations +@using Cannery.Aspects.Components.Auth +@using Cannery.Aspects.Components.Typography \ No newline at end of file diff --git a/src/Presentation.Blazor/ConfigureServices.cs b/src/Presentation.Blazor/ConfigureServices.cs index 2e11bf9..b6df0f0 100644 --- a/src/Presentation.Blazor/ConfigureServices.cs +++ b/src/Presentation.Blazor/ConfigureServices.cs @@ -1,21 +1,26 @@ -using Goodtocode.SemanticKernel.Infrastructure.SemanticKernel.Options; +using Cannery.Aspects.Components.Auth; +using Cannery.Aspects.Components.Auth.Services; +using Goodtocode.SemanticKernel.Presentation.Blazor.Options; using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Services; using Goodtocode.SemanticKernel.Presentation.Blazor.Services; using Goodtocode.SemanticKernel.Presentation.WebApi.Client; using Microsoft.Extensions.Options; +using Microsoft.FluentUI.AspNetCore.Components; using System.Reflection; namespace Goodtocode.SemanticKernel.Presentation.Blazor; public static class ConfigureServices { + public static bool IsLocal(this IWebHostEnvironment environment) { return environment.EnvironmentName.Equals("Local", StringComparison.OrdinalIgnoreCase); } + public static void AddLocalEnvironment(this WebApplicationBuilder builder) { - if (builder.Environment.IsEnvironment("Local")) + if (builder.Environment.IsLocal()) { builder.Configuration .AddUserSecrets(Assembly.GetExecutingAssembly()) @@ -24,27 +29,44 @@ public static void AddLocalEnvironment(this WebApplicationBuilder builder) } } - public static void AddBlazorServices(this IServiceCollection services) + public static void AddFrontendServices(this IServiceCollection services) { + services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); } - public static IServiceCollection AddBackEndApi(this IServiceCollection services, + public static IServiceCollection AddUserClaimsSyncService(this IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + + return services; + } + + public static IServiceCollection AddBackendApi(this IServiceCollection services, IConfiguration configuration) { - services.AddOptions() - .Bind(configuration.GetSection(WebApiOptions.SectionName)) + services.AddOptions() + .Bind(configuration.GetSection(BackendApiOptions.SectionName)) .ValidateDataAnnotations() .ValidateOnStart(); - services.AddScoped(builder => + services.AddAccessTokenHttpClient(options => + { + options.BaseAddress = new Uri(configuration["BackendApi:BaseUrl"] ?? throw new InvalidOperationException("Base URL for BackEndApi is not configured.")); + options.ClientName = "BackendApiClient"; + options.MaxRetry = 3; + }); + + services.AddScoped(provider => { - var options = builder.GetRequiredService>().Value; - return new WebApiClient(options.BaseUrl.ToString(), new HttpClient()); + var options = provider.GetRequiredService>().Value; + var httpClientFactory = provider.GetRequiredService(); + var httpClient = httpClientFactory.CreateClient("BackendApiClient"); + return new BackendApiClient(options.BaseUrl.ToString(), httpClient); }); return services; } -} +} \ No newline at end of file diff --git a/src/Presentation.Blazor/ConfigureServicesAuth.cs b/src/Presentation.Blazor/ConfigureServicesAuth.cs new file mode 100644 index 0000000..89a4b8e --- /dev/null +++ b/src/Presentation.Blazor/ConfigureServicesAuth.cs @@ -0,0 +1,149 @@ +using Cannery.Aspects.Components.Auth.Middleware; +using Cannery.Aspects.Components.Auth.Services; +using Goodtocode.SemanticKernel.Presentation.Blazor.Options; +using Goodtocode.SecuredHttpClient.Middleware; +using Goodtocode.SecuredHttpClient.Options; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.Identity.Web; + +namespace Goodtocode.SemanticKernel.Presentation.Blazor; + +public static class ConfigureServicesAuth +{ + private struct TokenRoleClaimTypes + { + public const string Roles = "roles"; + public const string Groups = "groups"; + } + + public static void AddAuthenticationForDownstream(this IServiceCollection services, IConfiguration configuration) + { + services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) + .AddMicrosoftIdentityWebApp(options => + { + configuration.GetSection("EntraExternalId").Bind(options); + options.SignInScheme = OpenIdConnectDefaults.AuthenticationScheme; + }) + .EnableTokenAcquisitionToCallDownstreamApi() + .AddInMemoryTokenCaches() + .AddDownstreamApi("BackendApi", configOptions => + { + configOptions.BaseUrl = configuration["BackendApi:BaseUrl"]; + configOptions.Scopes = [$"api://{configuration["BackendApi:ClientId"] ?? Guid.Empty.ToString()}/.default", + "User.Read"]; + }); + + services.Configure(OpenIdConnectDefaults.AuthenticationScheme, options => + { + options.SaveTokens = true; + options.Events = new OpenIdConnectEvents + { + OnTokenValidated = context => + { + var syncService = context.HttpContext.RequestServices.GetService(); + syncService?.UserChanged(context.Principal); + return Task.CompletedTask; + } + }; + }); + + ////For development: .AddInMemoryTokenCaches() + ////For Production: .AddDistributedTokenCaches() + ////services.AddDistributedMemoryCache(); + //services.Configure( + // options => + // { + // // Disable L1 Cache default: false + // //options.DisableL1Cache = false; + + // // L1 Cache Size Limit default: 500 MB + // //options.L1CacheOptions.SizeLimit = 500 * 1024 * 1024; + + // // Encrypt tokens at rest default: false + // options.Encrypt = true; + + // // Sliding Expiration default: 1 hour + // //options.SlidingExpiration = TimeSpan.FromHours(1); + // }); + + ///* When you move to web farm testing with tokens encrypted at rest while + // * hosting multiple app instances and stop using AddDistributedMemoryCache + // * in favor of a production distributed token cache provider, enable the + // * following code, which configures Data Protection to protect keys with + // * Azure Key Vault and maintain keys in Azure Blob Storage. + // * Our recommended approach for using Azure Blob Storage and Azure Key + // * Vault is to use an Azure Managed Identity in production. + // * Give the Managed Identity 'Key Vault Crypto User' and + // * 'Storage Blob Data Contributor' roles. Assign the Managed Identity + // * to the App Service in Settings > Identity > User assigned > Add. + // * Other options, both within Azure and outside of Azure, are available for + // * managing Data Protection keys across multiple app instances. See the + // * ASP.NET Core Data Protection documentation for details. + + //// Requires the Microsoft.Extensions.Azure NuGet package + //services.TryAddSingleton(); + + //TokenCredential? credential; + + //if (builder.Environment.IsProduction()) + //{ + // credential = new ManagedIdentityCredential("{MANAGED IDENTITY CLIENT ID}"); + //} + //else + //{ + // // Local development and testing only + // DefaultAzureCredentialOptions options = new() + // { + // // Specify the tenant ID to use the dev credentials when running the app locally + // // in Visual Studio. + // VisualStudioTenantId = "{TENANT ID}", + // SharedTokenCacheTenantId = "{TENANT ID}" + // }; + + // credential = new DefaultAzureCredential(options); + //} + + //services.AddDataProtection() + // .SetApplicationName("BlazorWebAppEntra") + // .PersistKeysToAzureBlobStorage(new Uri("{BLOB URI}"), credential) + // .ProtectKeysWithAzureKeyVault( new Uri("{KEY IDENTIFIER}"), credential); + //*/ + //// Add DI + ////services.AddScoped(); + //// Use + ////await tokenAcquisition.GetAccessTokenForUserAsync(new[] { "your-scope" }, OpenIdConnectDefaults.AuthenticationScheme); + } + + public static IServiceCollection AddAccessTokenHttpClient( + this IServiceCollection services, + Action configureOptions) + { + var options = new ResilientHttpClientOptions(); + configureOptions(options); + + if (options.BaseAddress == null) + throw new ArgumentNullException(nameof(configureOptions), "BaseAddress must be provided."); + if (string.IsNullOrWhiteSpace(options.ClientName)) + throw new ArgumentNullException(nameof(configureOptions), "ClientName must be provided."); + + services.AddOptions() + .ValidateDataAnnotations() + .ValidateOnStart(); + services.AddScoped(); + services.AddScoped(); + + services.AddHttpClient(options.ClientName, clientOptions => + { + clientOptions.DefaultRequestHeaders.Clear(); + clientOptions.BaseAddress = options.BaseAddress; + }) + .AddHttpMessageHandler() + .AddStandardResilienceHandler(resilienceOptions => + { + resilienceOptions.Retry.UseJitter = true; + resilienceOptions.Retry.MaxRetryAttempts = options.MaxRetry; + }); + + return services; + } +} diff --git a/src/Presentation.Blazor/Options/WebApiOptions.cs b/src/Presentation.Blazor/Options/BackendApiOptions.cs similarity index 53% rename from src/Presentation.Blazor/Options/WebApiOptions.cs rename to src/Presentation.Blazor/Options/BackendApiOptions.cs index 48dfb1d..023e5be 100644 --- a/src/Presentation.Blazor/Options/WebApiOptions.cs +++ b/src/Presentation.Blazor/Options/BackendApiOptions.cs @@ -1,13 +1,13 @@ using System.ComponentModel.DataAnnotations; -namespace Goodtocode.SemanticKernel.Infrastructure.SemanticKernel.Options; +namespace Goodtocode.SemanticKernel.Presentation.Blazor.Options; /// /// Presentation.WebApi settings /// -public sealed class WebApiOptions +public sealed class BackendApiOptions { - public const string SectionName = "WebApi"; + public const string SectionName = "BackendApi"; [Required] public Uri BaseUrl { get; set; } = default!; diff --git a/src/Presentation.Blazor/Options/ResilientHttpClientOptions.cs b/src/Presentation.Blazor/Options/ResilientHttpClientOptions.cs new file mode 100644 index 0000000..ac4346d --- /dev/null +++ b/src/Presentation.Blazor/Options/ResilientHttpClientOptions.cs @@ -0,0 +1,7 @@ +namespace Goodtocode.SemanticKernel.Presentation.Blazor.Options; +public class ResilientHttpClientOptions +{ + public Uri? BaseAddress { get; set; } + public string? ClientName { get; set; } + public int MaxRetry { get; set; } = 5; +} \ No newline at end of file diff --git a/src/Presentation.Blazor/Pages/Chat/Chat.razor b/src/Presentation.Blazor/Pages/Chat/Chat.razor deleted file mode 100644 index 5a4bd07..0000000 --- a/src/Presentation.Blazor/Pages/Chat/Chat.razor +++ /dev/null @@ -1,71 +0,0 @@ -@page "/" -@page "/chat" - -@using Microsoft.AspNetCore.Components.Web -@using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Components -@using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Models -@using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Services -@using Goodtocode.SemanticKernel.Presentation.Blazor.Services - -@namespace Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat - -@inject IChatService chatService - -Semantic Kernel Chat Session - -
-
-
-

What can I help you with?

-
-
-
-
- - -
-
- - -
-
-
-
-
- -@code { - private ChatSessionsModel chatSessions = new ChatSessionsModel(); - - protected override async Task OnInitializedAsync() - { - chatSessions = new ChatSessionsModel(); - chatSessions.AddRange(await chatService.GetChatSessionsAsync()); - StateHasChanged(); - } - - private void HandleNewSessionPressed() - { - chatSessions.ClearActive(); - StateHasChanged(); - } - - private void HandleSessionCreated(ChatSessionModel chatSession) - { - chatSessions.Add(chatSession); - chatSessions.SetActive(chatSession); - StateHasChanged(); - } - - private void HandleSessionSelected(ChatSessionModel chatSession) - { - chatSessions.ClearActive(); - chatSessions.SetActive(chatSession); - StateHasChanged(); - } - - private async Task HandleMessageSubmitted() - { - chatSessions.RefreshItem(await chatService.GetChatSessionAsync(chatSessions?.ActiveSession?.Id ?? Guid.Empty)); - StateHasChanged(); - } -} \ No newline at end of file diff --git a/src/Presentation.Blazor/Pages/Chat/Chat.razor.css b/src/Presentation.Blazor/Pages/Chat/Chat.razor.css deleted file mode 100644 index 49babf7..0000000 --- a/src/Presentation.Blazor/Pages/Chat/Chat.razor.css +++ /dev/null @@ -1,6 +0,0 @@ -.chat-messages-panel { - display: flex; - flex-direction: column; - height: 84.5vh; - padding: 0; -} \ No newline at end of file diff --git a/src/Presentation.Blazor/Pages/Chat/Components/ChatMessageList.razor b/src/Presentation.Blazor/Pages/Chat/Components/ChatMessageList.razor new file mode 100644 index 0000000..8c63280 --- /dev/null +++ b/src/Presentation.Blazor/Pages/Chat/Components/ChatMessageList.razor @@ -0,0 +1,27 @@ +@using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Models +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.JSInterop + +@inject IJSRuntime JSRuntime + + + @foreach (var message in Messages ?? Enumerable.Empty()) + { + var isUser = IsUserMessage(message); + + + @message.Content + + + + } + + + +@code { + [Parameter] + public IEnumerable Messages { get; set; } = new List(); + + private bool IsUserMessage(ChatMessageModel message) => + message?.Role?.ToLowerInvariant() == "user"; +} \ No newline at end of file diff --git a/src/Presentation.Blazor/Pages/Chat/Components/ChatMessages.razor b/src/Presentation.Blazor/Pages/Chat/Components/ChatMessages.razor deleted file mode 100644 index 15b9a6d..0000000 --- a/src/Presentation.Blazor/Pages/Chat/Components/ChatMessages.razor +++ /dev/null @@ -1,61 +0,0 @@ -@using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Models -@using Goodtocode.SemanticKernel.Presentation.Blazor.Services -@using Microsoft.AspNetCore.Components.Web -@using Microsoft.JSInterop - -@namespace Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Components - -@inject IJSRuntime JSRuntime - -@code { - [Parameter] public IEnumerable Messages { get; set; } = new List(); - - private bool IsUserMessage(ChatMessageModel message) => - message?.Role?.ToLowerInvariant() == "user"; - - private ElementReference lastMessageRef; - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (Messages?.Any() == true) - { - await JSRuntime.InvokeVoidAsync("scrollToLastElement", lastMessageRef); - } - } -} - -
-
- @if (Messages != null) - { - var lastMessage = Messages.LastOrDefault(); - foreach (var message in Messages) - { - var isUser = IsUserMessage(message); -
- @if (message == lastMessage) - { -
- @message.Content -
- } - else - { -
- @message.Content -
- } -
- } - } -
-
- - - \ No newline at end of file diff --git a/src/Presentation.Blazor/Pages/Chat/Components/ChatMessages.razor.css b/src/Presentation.Blazor/Pages/Chat/Components/ChatMessages.razor.css deleted file mode 100644 index 8a10997..0000000 --- a/src/Presentation.Blazor/Pages/Chat/Components/ChatMessages.razor.css +++ /dev/null @@ -1,46 +0,0 @@ -.messages-panel { - flex: 1 1 auto; - padding-bottom: 1rem; -} - -.message-list { - max-height: 100%; - overflow-y: auto; - border-radius: 0.25rem; - background-color: var(--bs-light); - box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); -} - .message-list::-webkit-scrollbar { - width: 8px; - } - - .message-list::-webkit-scrollbar-thumb { - background-color: var(--bs-secondary); - border-radius: 4px; - } - - .message-list::-webkit-scrollbar-track { - background-color: var(--bs-light); - } - -.message-item { - display: flex; - align-items: center; - padding: 0.5rem 1rem; - cursor: pointer; - transition: background-color 0.3s ease; -} - - .message-item.rounded-custom { - border-radius: 0.25rem; - overflow: hidden; - } - - .message-item p { - margin-top: 0; - margin-bottom: 0; - } - -.user-role { - max-width: 75%; -} diff --git a/src/Presentation.Blazor/Pages/Chat/Components/ChatSessionEditForm.razor b/src/Presentation.Blazor/Pages/Chat/Components/ChatSessionEditForm.razor new file mode 100644 index 0000000..4da7455 --- /dev/null +++ b/src/Presentation.Blazor/Pages/Chat/Components/ChatSessionEditForm.razor @@ -0,0 +1,42 @@ +@using System.ComponentModel.DataAnnotations +@using Microsoft.AspNetCore.Components.Forms + + + + + + + + + + +@code { + [Parameter] public string? Title { get; set; } + [Parameter] public EventCallback OnSave { get; set; } + [Parameter] public EventCallback OnCancel { get; set; } + + private EditSessionModel editModel = new(); + private EditContext? editContext; + + protected override void OnParametersSet() + { + editModel = new EditSessionModel { Title = Title }; + editContext = new EditContext(editModel); + } + + private async Task HandleValidSubmit() + { + if (editContext is null || !editContext.Validate()) + return; + await OnSave.InvokeAsync(editModel.Title!); + } + + public class EditSessionModel + { + [Required(ErrorMessage = "Title is required.")] + public string? Title { get; set; } + } +} \ No newline at end of file diff --git a/src/Presentation.Blazor/Pages/Chat/Components/ChatSessionList.razor b/src/Presentation.Blazor/Pages/Chat/Components/ChatSessionList.razor new file mode 100644 index 0000000..6aa534b --- /dev/null +++ b/src/Presentation.Blazor/Pages/Chat/Components/ChatSessionList.razor @@ -0,0 +1,103 @@ +@using Cannery.Aspects.Components.Skeleton +@using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Models +@using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Services + +@inject IChatService chatService + +@if (Sessions?.Any() == true) +{ + + + @{ + var sessionItem = (ChatSessionTreeViewItem)item; + var session = sessionItem.Session; + var isSelected = (selectedTreeItem is ChatSessionTreeViewItem selected) + && (item is ChatSessionTreeViewItem current) + && selected.Session.Id == current.Session.Id; + } + @if (editingSessionId == session.Id) + { + + } + else if (isSelected) + { + + + @sessionItem.Text + + + + } + else + { + @sessionItem.Text + } + + +} +else +{ + +} + +@code { + [Parameter] public IEnumerable? Sessions { get; set; } + [Parameter] public EventCallback OnSessionSelected { get; set; } + [Parameter] public EventCallback OnRenameSession { get; set; } + + private IEnumerable? _sessionTreeItemsCache; + + private IEnumerable SessionTreeItems + => _sessionTreeItemsCache ??= Sessions?.Select(ChatSessionTreeViewItem.CreateFrom).Cast().ToList() + ?? Enumerable.Empty(); + + private Guid? editingSessionId; + private EditSessionModel editModel = new(); + private EditContext? editContext; + private ITreeViewItem? selectedTreeItem; + + private async Task OnTreeSessionSelected(object? selectedItem) + { + selectedTreeItem = selectedItem as ITreeViewItem; + if (selectedTreeItem is ChatSessionTreeViewItem item) + { + await OnSessionSelected.InvokeAsync(item.Session); + } + } + + private void StartEditing(ChatSessionModel session) + { + editingSessionId = session.Id; + editModel = new EditSessionModel { Title = session.Title }; + editContext = new EditContext(editModel); + } + + private async Task EditSessionTitleAsync(ChatSessionModel session, string title) + { + session.Title = title; + editingSessionId = null; + await chatService.RenameSessionAsync(session.Id, title); + await OnRenameSession.InvokeAsync(session); + } + + private void CancelEditing() => editingSessionId = null; + + public void ClearSelection() + { + selectedTreeItem = null; + StateHasChanged(); + } + + protected override void OnParametersSet() => _sessionTreeItemsCache = null; + + public class EditSessionModel + { + [Required(ErrorMessage = "Title is required.")] + public string? Title { get; set; } + } +} \ No newline at end of file diff --git a/src/Presentation.Blazor/Pages/Chat/Components/ChatSessionScroll.razor b/src/Presentation.Blazor/Pages/Chat/Components/ChatSessionScroll.razor new file mode 100644 index 0000000..2a56fd7 --- /dev/null +++ b/src/Presentation.Blazor/Pages/Chat/Components/ChatSessionScroll.razor @@ -0,0 +1,85 @@ +@using Cannery.Aspects.Components.Skeleton +@using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Models +@using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Services + +@inject IChatService chatService + +@if (Sessions?.Any() == true) +{ + + @foreach (var session in Sessions) + { + + @if (editingSessionId == session.Id) + { + + } + else + { + + + @session.Title + + @if (selectedSession?.Id == session.Id) + { + + } + + } + + } + +} +else +{ + +} + +@code { + [Parameter] public IEnumerable? Sessions { get; set; } + [Parameter] public EventCallback OnSessionSelected { get; set; } + [Parameter] public EventCallback OnRenameSession { get; set; } + + private Guid? editingSessionId; + private EditSessionModel editModel = new(); + private EditContext? editContext; + private ChatSessionModel? selectedSession; + + private async Task SelectSession(ChatSessionModel session) + { + selectedSession = session; + await OnSessionSelected.InvokeAsync(session); + } + + private void StartEditing(ChatSessionModel session) + { + editingSessionId = session.Id; + editModel = new EditSessionModel { Title = session.Title }; + editContext = new EditContext(editModel); + } + + private async Task EditSessionTitleAsync(ChatSessionModel session, string title) + { + session.Title = title; + editingSessionId = null; + await chatService.RenameSessionAsync(session.Id, title); + await OnRenameSession.InvokeAsync(session); + } + + private void CancelEditing() => editingSessionId = null; + + public void ClearSelection() + { + selectedSession = null; + StateHasChanged(); + } + + public class EditSessionModel + { + [Required(ErrorMessage = "Title is required.")] + public string? Title { get; set; } + } +} \ No newline at end of file diff --git a/src/Presentation.Blazor/Pages/Chat/Components/ChatSessions.razor b/src/Presentation.Blazor/Pages/Chat/Components/ChatSessions.razor deleted file mode 100644 index 7975409..0000000 --- a/src/Presentation.Blazor/Pages/Chat/Components/ChatSessions.razor +++ /dev/null @@ -1,104 +0,0 @@ -@using Goodtocode.SemanticKernel.Presentation.Blazor.Components.Skeleton -@using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Models -@using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Services -@using Microsoft.AspNetCore.Components.Web -@using System.ComponentModel.DataAnnotations -@using Microsoft.AspNetCore.Components.Forms - -@namespace Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Components - -@inject IChatService chatService - -@code -{ - [Parameter] public IEnumerable? Sessions { get; set; } - [Parameter] public EventCallback OnSessionSelected { get; set; } - [Parameter] public EventCallback OnRenameSession { get; set; } -} - -
- @if (Sessions?.Count() > 0) - { - foreach (var session in Sessions) - { -
-
- @if (editingSessionId == session.Id) - { - -
- - - -
- -
- } - else - { -

- @(string.IsNullOrWhiteSpace(session.Title) ? "Untitled Session" : session.Title) -

- } -
- -
- } - } - else - { - - } -
- -@code -{ - private Guid? editingSessionId; - private EditSessionModel editModel = new(); - private EditContext? editContext; - - private void StartEditing(ChatSessionModel session) - { - editingSessionId = session.Id; - editModel = new EditSessionModel { Title = session.Title }; - editContext = new EditContext(editModel); - - } - - private async Task EditSessionTitleAsync(ChatSessionModel session) - { - if (editContext is null || !editContext.Validate()) - return; - - session.Title = editModel!.Title!; - editingSessionId = null; - await chatService.RenameSessionAsync(session.Id, session.Title); - await OnRenameSession.InvokeAsync(session); - } - - private void CancelEditing() - { - editingSessionId = null; - } - - private async Task SelectSessionAsync(ChatSessionModel selectedSession) - { - selectedSession.IsSelected = true; - await OnSessionSelected.InvokeAsync(selectedSession); - StateHasChanged(); - } - - public class EditSessionModel - { - [Required(ErrorMessage = "Title is required.")] - public string? Title { get; set; } - } -} \ No newline at end of file diff --git a/src/Presentation.Blazor/Pages/Chat/Components/ChatSessions.razor.css b/src/Presentation.Blazor/Pages/Chat/Components/ChatSessions.razor.css deleted file mode 100644 index a72c646..0000000 --- a/src/Presentation.Blazor/Pages/Chat/Components/ChatSessions.razor.css +++ /dev/null @@ -1,37 +0,0 @@ -.session-list { - overflow-y: auto; - border-radius: 0.25rem; - background-color: var(--bs-light); - box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); -} - - .session-list::-webkit-scrollbar { - width: 8px; - } - - .session-list::-webkit-scrollbar-thumb { - background-color: var(--bs-secondary); - border-radius: 4px; - } - - .session-list::-webkit-scrollbar-track { - background-color: var(--bs-light); - } - -.session-item { - display: flex; - align-items: center; - padding: 0.5rem 1rem; - cursor: pointer; - transition: background-color 0.3s ease; -} - - .session-item.rounded-custom { - border-radius: 0.25rem; - overflow: hidden; - } - - .session-item p { - margin-top: 0; - margin-bottom: 0; - } diff --git a/src/Presentation.Blazor/Pages/Chat/Components/NewChatMessage.razor.css b/src/Presentation.Blazor/Pages/Chat/Components/NewChatMessage.razor.css deleted file mode 100644 index 4386374..0000000 --- a/src/Presentation.Blazor/Pages/Chat/Components/NewChatMessage.razor.css +++ /dev/null @@ -1,8 +0,0 @@ -.message-input-panel { - position: sticky; - bottom: 0; - background: #fff; - z-index: 10; - box-shadow: 0 -2px 8px rgba(0,0,0,0.08); - padding: 1rem; -} diff --git a/src/Presentation.Blazor/Pages/Chat/Components/NewChatMessage.razor b/src/Presentation.Blazor/Pages/Chat/Components/NewChatMessageCard.razor similarity index 68% rename from src/Presentation.Blazor/Pages/Chat/Components/NewChatMessage.razor rename to src/Presentation.Blazor/Pages/Chat/Components/NewChatMessageCard.razor index 6ea7260..3786462 100644 --- a/src/Presentation.Blazor/Pages/Chat/Components/NewChatMessage.razor +++ b/src/Presentation.Blazor/Pages/Chat/Components/NewChatMessageCard.razor @@ -1,44 +1,39 @@ -@using System.ComponentModel.DataAnnotations @using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Models @using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Services -@using Goodtocode.SemanticKernel.Presentation.Blazor.Services -@using Goodtocode.SemanticKernel.Presentation.WebApi.Client -@using Microsoft.AspNetCore.Components.Forms -@using Microsoft.AspNetCore.Components.Web - -@namespace Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Components @inject IChatService chatService -@code { - [Parameter] public ChatSessionsModel Sessions { get; set; } = new ChatSessionsModel(); - [Parameter] public EventCallback OnSessionCreated { get; set; } - [Parameter] public EventCallback OnMessageSubmitted { get; set; } -} - -
- -
- - -
+ +
-
+ @code { + [Parameter] public string Height { get; set; } = "80px"; + [Parameter] public string Width { get; set; } = "70%"; + [Parameter] public ChatSessionsModel Sessions { get; set; } = new ChatSessionsModel(); + [Parameter] public EventCallback OnSessionCreated { get; set; } + [Parameter] public EventCallback OnMessageSubmitted { get; set; } + public class ChatMessageInputModel { [Required(ErrorMessage = "Message is required.")] diff --git a/src/Presentation.Blazor/Pages/Chat/Components/NewChatMessageInput.razor b/src/Presentation.Blazor/Pages/Chat/Components/NewChatMessageInput.razor new file mode 100644 index 0000000..048f7d9 --- /dev/null +++ b/src/Presentation.Blazor/Pages/Chat/Components/NewChatMessageInput.razor @@ -0,0 +1,91 @@ +@using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Models +@using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Services + +@inject IChatService chatService + + + + + + @if (!isSubmitting) + { + @("Send") + } + else + { + + } + + + + + + +@code +{ + [Parameter] public string Height { get; set; } = "80px"; + [Parameter] public string Width { get; set; } = "70%"; + [Parameter] public ChatSessionsModel Sessions { get; set; } = new ChatSessionsModel(); + [Parameter] public EventCallback OnSessionCreated { get; set; } + [Parameter] public EventCallback OnMessageSubmitted { get; set; } + + public class ChatMessageInputModel + { + [Required(ErrorMessage = "Message is required.")] + public string NewMessage { get; set; } = string.Empty; + } + + private ChatMessageInputModel messageModel { get; set; } = new(); + private EditContext? editContext; + private bool isSubmitting = false; + + protected override void OnInitialized() + { + editContext = new EditContext(messageModel); + } + + private async Task SubmitMessage() + { + if (editContext is not null && editContext.Validate()) + { + isSubmitting = true; + StateHasChanged(); + try + { + var newMessageModel = new ChatMessageModel + { + Id = Guid.NewGuid(), + Role = "User", + Content = messageModel.NewMessage, + Timestamp = DateTimeOffset.Now + }; + if (Sessions.ActiveSession == null) + { + var chatSession = await chatService.CreateSessionAsync(messageModel.NewMessage); + await OnSessionCreated.InvokeAsync(chatSession); + } + else + { + newMessageModel.ChatSessionId = Sessions.ActiveSession.Id; + await chatService.SendMessageAsync(Sessions.ActiveSession!.Id, messageModel.NewMessage); + await OnMessageSubmitted.InvokeAsync(); + } + ClearForm(); + } + finally + { + isSubmitting = false; + StateHasChanged(); + } + } + } + + private void ClearForm() + { + messageModel.NewMessage = string.Empty; + editContext = new EditContext(messageModel); + } +} diff --git a/src/Presentation.Blazor/Pages/Chat/Components/NewChatSession.razor b/src/Presentation.Blazor/Pages/Chat/Components/NewChatSession.razor deleted file mode 100644 index 89fed97..0000000 --- a/src/Presentation.Blazor/Pages/Chat/Components/NewChatSession.razor +++ /dev/null @@ -1,16 +0,0 @@ -@using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Models -@using Microsoft.AspNetCore.Components.Web - -@namespace Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Components - -@code -{ - [Parameter] public EventCallback OnNewSessionPressed { get; set; } - - private async Task NewSessionAsync() - { - await OnNewSessionPressed.InvokeAsync(); - } -} - - diff --git a/src/Presentation.Blazor/Pages/Chat/Components/NewChatSessionButton.razor b/src/Presentation.Blazor/Pages/Chat/Components/NewChatSessionButton.razor new file mode 100644 index 0000000..a48be81 --- /dev/null +++ b/src/Presentation.Blazor/Pages/Chat/Components/NewChatSessionButton.razor @@ -0,0 +1,17 @@ + + + @Title + + +@code +{ + [Parameter] public EventCallback OnNewSessionPressed { get; set; } + [Parameter] public string? Title { get; set; } = "New Chat"; + [Parameter] public string? Style { get; set; } + [Parameter] public string? Class { get; set; } + + private async Task NewSessionAsync() + { + await OnNewSessionPressed.InvokeAsync(); + } +} diff --git a/src/Presentation.Blazor/Pages/Chat/Models/ChatSessionModel.cs b/src/Presentation.Blazor/Pages/Chat/Models/ChatSessionModel.cs index 9a87b42..851ffe7 100644 --- a/src/Presentation.Blazor/Pages/Chat/Models/ChatSessionModel.cs +++ b/src/Presentation.Blazor/Pages/Chat/Models/ChatSessionModel.cs @@ -11,7 +11,7 @@ public static List Create(ICollection chatSess { Id = dto.Id, Title = dto.Title, - AuthorId = dto.AuthorId, + ActorId = dto.ActorId, Timestamp = dto.Timestamp, IsSelected = false, Messages = [.. dto.Messages.Select(m => new ChatMessageModel @@ -30,7 +30,7 @@ public static ChatSessionModel Create(ChatSessionDto chatSession) { Id = chatSession.Id, Title = chatSession.Title, - AuthorId = chatSession.AuthorId, + ActorId = chatSession.ActorId, Timestamp = chatSession.Timestamp, IsSelected = false, Messages = [.. chatSession.Messages.Select(m => new ChatMessageModel @@ -45,7 +45,7 @@ public static ChatSessionModel Create(ChatSessionDto chatSession) public Guid Id { get; set; } = Guid.Empty; public string Title { get; set; } = string.Empty; - public Guid AuthorId { get; set; } = Guid.Empty; + public Guid ActorId { get; set; } = Guid.Empty; public DateTimeOffset Timestamp { get; set; } public virtual ICollection? Messages { get; set; } diff --git a/src/Presentation.Blazor/Pages/Chat/Models/ChatSessionTreeViewItem.cs b/src/Presentation.Blazor/Pages/Chat/Models/ChatSessionTreeViewItem.cs new file mode 100644 index 0000000..9d4d136 --- /dev/null +++ b/src/Presentation.Blazor/Pages/Chat/Models/ChatSessionTreeViewItem.cs @@ -0,0 +1,47 @@ +using Microsoft.FluentUI.AspNetCore.Components; + +namespace Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Models +{ + public class ChatSessionTreeViewItem : ITreeViewItem + { + public ChatSessionModel Session { get; } + + public ChatSessionTreeViewItem(ChatSessionModel session) + { + Session = session; + } + + public static ChatSessionTreeViewItem CreateFrom(ChatSessionModel session) + => new(session); + + // ITreeViewItem implementation + public string Id + { + get { return Session.Id.ToString(); } + set { } + } + + public string Text + { + get { return string.IsNullOrWhiteSpace(Session.Title) ? "Untitled Session" : Session.Title; } + set { } + } + + // Flat list, no children + public IEnumerable? Children => null; + + public IEnumerable? Items + { + get => Children; + set { } + } + + public Icon? IconCollapsed { get; set; } + public Icon? IconExpanded { get; set; } + public bool Disabled { get; set; } + public bool Expanded { get; set; } + public Func? OnExpandedAsync { get; set; } + + // Add other ITreeViewItem members as required by your Fluent UI version + } +} \ No newline at end of file diff --git a/src/Presentation.Blazor/Pages/Chat/Models/ChatSessionsModel.cs b/src/Presentation.Blazor/Pages/Chat/Models/ChatSessionsModel.cs index 3a160b8..a4e061e 100644 --- a/src/Presentation.Blazor/Pages/Chat/Models/ChatSessionsModel.cs +++ b/src/Presentation.Blazor/Pages/Chat/Models/ChatSessionsModel.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.ObjectModel; +using System.Collections.ObjectModel; using System.ComponentModel; namespace Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Models; diff --git a/src/Presentation.Blazor/Pages/Chat/Services/ChatService.cs b/src/Presentation.Blazor/Pages/Chat/Services/ChatService.cs index 01bbefb..64266d6 100644 --- a/src/Presentation.Blazor/Pages/Chat/Services/ChatService.cs +++ b/src/Presentation.Blazor/Pages/Chat/Services/ChatService.cs @@ -1,6 +1,7 @@ -using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Models; +using Goodtocode.SemanticKernel.Presentation.WebApi.Client; using Goodtocode.SemanticKernel.Presentation.Blazor.Services; -using Goodtocode.SemanticKernel.Presentation.WebApi.Client; +using Cannery.Aspects.Components.Auth; +using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Models; namespace Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Services; @@ -13,23 +14,27 @@ public interface IChatService Task SendMessageAsync(Guid chatSessionId, string newMessage); } -public class ChatService(WebApiClient client, IUserService userUtilityService) : IChatService +public class ChatService(BackendApiClient client, IUserClaimsInfo userInfo) : ApiService, IChatService { - private readonly WebApiClient _client = client; - private readonly IUserService _userService = userUtilityService; + private readonly BackendApiClient _apiClient = client; + private readonly IUserClaimsInfo _userInfo = userInfo; public async Task> GetChatSessionsAsync() { - var userId = await _userService.GetUserIdAsync(); - var response = await _client.GetAuthorChatSessionsPaginatedQueryAsync(userId, DateTime.UtcNow.AddDays(-30), DateTime.UtcNow, 1, 20).ConfigureAwait(false); + var response = await HandleApiException(() => _apiClient.GetMyChatSessionsPaginatedAsync( + DateTime.UtcNow.AddDays(-30), + DateTime.UtcNow, + 1, + 20 + )); return ChatSessionModel.Create(response.Items); } public async Task GetChatSessionAsync(Guid chatSessionId) { - var userId = await _userService.GetUserIdAsync(); - var response = await _client.GetAuthorChatSessionQueryAsync(userId, chatSessionId).ConfigureAwait(false); + var response = await HandleApiException(() => _apiClient.GetMyChatSessionAsync( + chatSessionId)); return ChatSessionModel.Create(response); } @@ -38,24 +43,28 @@ public async Task CreateSessionAsync(string firstMessage) { var command = new CreateChatSessionCommand { - AuthorId = await _userService.GetUserIdAsync(), + ActorId = _userInfo.ObjectId, Message = firstMessage }; - var response = await _client.CreateChatSessionCommandAsync(command).ConfigureAwait(false); + var response = await HandleApiException(() => _apiClient.CreateChatSessionCommandAsync(command)); return ChatSessionModel.Create(response); } public async Task RenameSessionAsync(Guid chatSessionId, string newTitle) { - await _client.PatchChatSessionCommandAsync(chatSessionId, new PatchChatSessionCommand { Id = chatSessionId, Title = newTitle }).ConfigureAwait(false); + await HandleApiException(() => _apiClient.PatchChatSessionCommandAsync(chatSessionId, new PatchChatSessionCommand { Id = chatSessionId, Title = newTitle })); } public async Task SendMessageAsync(Guid chatSessionId, string newMessage) { - var response = await _client.CreateChatMessageCommandAsync(new CreateChatMessageCommand { ChatSessionId = chatSessionId, Message = newMessage }).ConfigureAwait(false); + var response = await HandleApiException(() => _apiClient.CreateChatMessageCommandAsync( + new CreateChatMessageCommand + { + ChatSessionId = chatSessionId, + Message = newMessage + })); return ChatMessageModel.Create(response); } -} - +} \ No newline at end of file diff --git a/src/Presentation.Blazor/Pages/ChatPage.razor b/src/Presentation.Blazor/Pages/ChatPage.razor new file mode 100644 index 0000000..8cd0bb0 --- /dev/null +++ b/src/Presentation.Blazor/Pages/ChatPage.razor @@ -0,0 +1,120 @@ +@page "/chat" + +@using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Components +@using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Models +@using Goodtocode.SemanticKernel.Presentation.Blazor.Pages.Chat.Services +@using Microsoft.AspNetCore.Authorization + +@attribute [Authorize] + +@inject IChatService chatService + +Semantic Kernel Chat Session + + + + + + + @if (sessionListOrientation == Orientation.Vertical) + { + + } + else + { + + } + + + + + What can I help you with? + + + @if (sessionListOrientation == Orientation.Vertical) + { + + } + else + { + + + } + + + + +@code { + private ChatSessionsModel chatSessions = new ChatSessionsModel(); + private ChatSessionList? chatSessionListRef; + private ChatSessionScroll? chatSessionScrollRef; + private Orientation sessionListOrientation = Orientation.Vertical; + private int sessionListVerticalGap; + private int sessionListHorizontalGap; + private bool wrapSessions = false; + private string chatContentStyle = "max-width:50vw;"; + + protected override async Task OnInitializedAsync() + { + chatSessions = new ChatSessionsModel(); + chatSessions.AddRange(await chatService.GetChatSessionsAsync()); + StateHasChanged(); + } + + private void HandleNewSessionPressed() + { + chatSessionListRef?.ClearSelection(); + chatSessionScrollRef?.ClearSelection(); + chatSessions.ClearActive(); + StateHasChanged(); + } + + private void HandleSessionCreated(ChatSessionModel chatSession) + { + chatSessions.Add(chatSession); + chatSessions.SetActive(chatSession); + StateHasChanged(); + } + + private void HandleSessionSelected(ChatSessionModel chatSession) + { + chatSessions.ClearActive(); + chatSessions.SetActive(chatSession); + StateHasChanged(); + } + + private async Task HandleMessageSubmitted() + { + chatSessions.RefreshItem(await chatService.GetChatSessionAsync(chatSessions?.ActiveSession?.Id ?? Guid.Empty)); + StateHasChanged(); + } + + private void OnBreakpointEnterHandler(GridItemSize size) + { + wrapSessions = size == GridItemSize.Xs ? true : false; + if (size == GridItemSize.Xs || size == GridItemSize.Sm) + { + sessionListOrientation = Orientation.Horizontal; + sessionListVerticalGap = 0; + sessionListHorizontalGap = 8; + chatContentStyle = ""; + } + else + { + sessionListOrientation = Orientation.Vertical; + sessionListVerticalGap = 8; + sessionListHorizontalGap = 0; + chatContentStyle = "max-width:50vw;"; + } + StateHasChanged(); + } +} \ No newline at end of file diff --git a/src/Presentation.Blazor/Pages/Counter.razor b/src/Presentation.Blazor/Pages/Counter.razor deleted file mode 100644 index be81954..0000000 --- a/src/Presentation.Blazor/Pages/Counter.razor +++ /dev/null @@ -1,21 +0,0 @@ -@page "/counter" -@using Microsoft.AspNetCore.Components.Web - -@namespace Goodtocode.SemanticKernel.Presentation.Blazor.Pages - -Counter - -

Counter

- -

Current count: @currentCount

- - - -@code { - private int currentCount = 0; - - private void IncrementCount() - { - currentCount++; - } -} diff --git a/src/Presentation.Blazor/Pages/Error.razor b/src/Presentation.Blazor/Pages/Error.razor deleted file mode 100644 index aaeaa11..0000000 --- a/src/Presentation.Blazor/Pages/Error.razor +++ /dev/null @@ -1,39 +0,0 @@ -@page "/Error" -@using Microsoft.AspNetCore.Components.Web -@using System.Diagnostics - -@namespace Goodtocode.SemanticKernel.Presentation.Blazor.Pages - -Error - -

Error.

-

An error occurred while processing your request.

- -@if (ShowRequestId) -{ -

- Request ID: @RequestId -

-} - -

Development Mode

-

- Swapping to Development environment will display more detailed information about the error that occurred. -

-

- The Development environment shouldn't be enabled for deployed applications. - It can result in displaying sensitive information from exceptions to end users. - For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development - and restarting the app. -

- -@code { - [CascadingParameter] - private HttpContext? HttpContext { get; set; } - - private string? RequestId { get; set; } - private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - - protected override void OnInitialized() => - RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; -} diff --git a/src/Presentation.Blazor/Pages/HomePage.razor b/src/Presentation.Blazor/Pages/HomePage.razor new file mode 100644 index 0000000..ac79fc0 --- /dev/null +++ b/src/Presentation.Blazor/Pages/HomePage.razor @@ -0,0 +1,60 @@ +@page "/" +@using Cannery.Aspects.Components.Typography +@using Microsoft.AspNetCore.Authorization + +@attribute [AllowAnonymous] + +Home + + + Welcome to Semantic Kernel + + + Simplifying the cloud for IT professionals. Gain actionable intelligence, manage your digital assets, and empower your team with a modern, approachable dashboard. + + + + + + + + Asset Management + + + + Easily add, organize, and monitor your digital assets in one place. + + + + + + + Agent Enrollments + + + + Deploy and manage digital agents to automate and streamline your workflows. + + + + + + + Insights & Analytics + + + + Visualize trends, track performance, and make data-driven decisions with confidence. + + + + + +@code { + [Inject] private NavigationManager Navigation { get; set; } = default!; + + private void NavigateToAssets() + { + Navigation.NavigateTo("/assets"); + } +} diff --git a/src/Presentation.Blazor/Pages/Weather.razor b/src/Presentation.Blazor/Pages/Weather.razor deleted file mode 100644 index c787484..0000000 --- a/src/Presentation.Blazor/Pages/Weather.razor +++ /dev/null @@ -1,68 +0,0 @@ -@page "/weather" -@attribute [StreamRendering] - -@namespace Goodtocode.SemanticKernel.Presentation.Blazor.Pages - -@using Microsoft.AspNetCore.Components.Web - -Weather - -

Weather

- -

This component demonstrates showing data.

- -@if (forecasts == null) -{ -

Loading...

-} -else -{ - - - - - - - - - - - @foreach (var forecast in forecasts) - { - - - - - - - } - -
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
-} - -@code { - private WeatherForecast[]? forecasts; - - protected override async Task OnInitializedAsync() - { - // Simulate asynchronous loading to demonstrate streaming rendering - await Task.Delay(500); - - var startDate = DateOnly.FromDateTime(DateTime.Now); - var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; - forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = startDate.AddDays(index), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = summaries[Random.Shared.Next(summaries.Length)] - }).ToArray(); - } - - private class WeatherForecast - { - public DateOnly Date { get; set; } - public int TemperatureC { get; set; } - public string? Summary { get; set; } - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - } -} diff --git a/src/Presentation.Blazor/Pages/_Imports.razor b/src/Presentation.Blazor/Pages/_Imports.razor new file mode 100644 index 0000000..ee0243b --- /dev/null +++ b/src/Presentation.Blazor/Pages/_Imports.razor @@ -0,0 +1,17 @@ +@using System.ComponentModel.DataAnnotations +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Web +@using static Microsoft.AspNetCore.Components.Web.RenderMode +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.FluentUI.AspNetCore.Components +@using Icons = Microsoft.FluentUI.AspNetCore.Components.Icons +@using Microsoft.JSInterop +@using Cannery.Aspects.Components +@using Cannery.Aspects.Components.Auth +@using Cannery.Aspects.Components.Skeleton +@using Cannery.Aspects.Components.Typography +@using Goodtocode.SemanticKernel.Presentation.WebApi.Client \ No newline at end of file diff --git a/src/Presentation.Blazor/Presentation.Blazor.csproj b/src/Presentation.Blazor/Presentation.Blazor.csproj index 36f3177..86aec7c 100644 --- a/src/Presentation.Blazor/Presentation.Blazor.csproj +++ b/src/Presentation.Blazor/Presentation.Blazor.csproj @@ -4,19 +4,29 @@ Goodtocode.SemanticKernel.Presentation.Blazor Goodtocode.SemanticKernel.Presentation.Blazor 1.0.0 - net9.0 + net10.0 enable enable c2325466-9b0d-494a-984a-11ad18457d3f - - - - - - + + + + + + + + + + + + + + + + diff --git a/src/Presentation.Blazor/Program.cs b/src/Presentation.Blazor/Program.cs index e59b5d6..fe4333a 100644 --- a/src/Presentation.Blazor/Program.cs +++ b/src/Presentation.Blazor/Program.cs @@ -1,6 +1,9 @@ using Azure.Monitor.OpenTelemetry.AspNetCore; +using Cannery.Aspects.Components.Auth.Routing; using Goodtocode.SemanticKernel.Presentation.Blazor; using Goodtocode.SemanticKernel.Presentation.Blazor.Components; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.FluentUI.AspNetCore.Components; var builder = WebApplication.CreateBuilder(args); @@ -9,17 +12,25 @@ builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); -builder.Services.AddBackEndApi(builder.Configuration); +builder.Services.AddFluentUIComponents(); -builder.Services.AddBlazorServices(); +builder.Services.AddUserClaimsSyncService(); -builder.Services.AddHttpContextAccessor(); +builder.Services.AddAuthenticationForDownstream(builder.Configuration); + +builder.Services.AddAuthorization(); builder.Services.AddOpenTelemetry().UseAzureMonitor(options => { options.ConnectionString = builder.Configuration["ApplicationInsights:ConnectionString"]; }); +builder.Services.AddHttpContextAccessor(); + +builder.Services.AddBackendApi(builder.Configuration); + +builder.Services.AddFrontendServices(); + var app = builder.Build(); if (app.Environment.IsDevelopment() || app.Environment.IsLocal()) @@ -33,12 +44,23 @@ // ToDo: Add CSP Header } +app.UseForwardedHeaders(new ForwardedHeadersOptions +{ + ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto +}); + +app.UseAuthentication(); +app.UseAuthorization(); + app.UseHttpsRedirection(); app.UseAntiforgery(); app.MapStaticAssets(); + app.MapRazorComponents() .AddInteractiveServerRenderMode(); +app.MapGroup("/authentication").MapSignInSignOut(); + app.Run(); diff --git a/src/Presentation.Blazor/Properties/launchSettings.json b/src/Presentation.Blazor/Properties/launchSettings.json index f39412c..39aaa11 100644 --- a/src/Presentation.Blazor/Properties/launchSettings.json +++ b/src/Presentation.Blazor/Properties/launchSettings.json @@ -6,7 +6,7 @@ "dotnetRunMessages": true, "launchBrowser": false, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "http://localhost:5165", + "applicationUrl": "http://localhost:7165", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Local" } @@ -16,7 +16,7 @@ "dotnetRunMessages": true, "launchBrowser": false, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "https://localhost:7046;http://localhost:5165", + "applicationUrl": "https://localhost:7175;http://localhost:7165", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Local" } diff --git a/src/Presentation.Blazor/Services/ApiService.cs b/src/Presentation.Blazor/Services/ApiService.cs new file mode 100644 index 0000000..0e85d2c --- /dev/null +++ b/src/Presentation.Blazor/Services/ApiService.cs @@ -0,0 +1,51 @@ +using Goodtocode.SemanticKernel.Presentation.WebApi.Client; +using System.ComponentModel.DataAnnotations; +using System.Text.Json; + +namespace Goodtocode.SemanticKernel.Presentation.Blazor.Services; + +public abstract class ApiService +{ + protected static async Task HandleApiException(Func apiCall) + { + try + { + await apiCall().ConfigureAwait(false); + } + catch (ApiException ex) when (ex.StatusCode == 400) + { + var errors = ParseValidationErrors(ex.Response); + throw new ValidationException("Validation failed", null, errors); + } + } + + protected static async Task HandleApiException(Func> apiCall) + { + try + { + return await apiCall().ConfigureAwait(false); + } + catch (ApiException ex) when (ex.StatusCode == 400) + { + var errors = ParseValidationErrors(ex.Response); + throw new ValidationException("Validation failed", null, errors); + } + } + + protected static Dictionary> ParseValidationErrors(string content) + { + var result = new Dictionary>(); + if (!string.IsNullOrEmpty(content)) + { + var doc = JsonDocument.Parse(content); + if (doc.RootElement.TryGetProperty("errors", out var errorsElement)) + { + foreach (var property in errorsElement.EnumerateObject()) + { + result[property.Name] = [.. property.Value.EnumerateArray().Select(e => e.GetString())]; + } + } + } + return result; + } +} diff --git a/src/Presentation.Blazor/Services/UserService.cs b/src/Presentation.Blazor/Services/UserService.cs deleted file mode 100644 index 22c9e28..0000000 --- a/src/Presentation.Blazor/Services/UserService.cs +++ /dev/null @@ -1,52 +0,0 @@ -namespace Goodtocode.SemanticKernel.Presentation.Blazor.Services; - -public interface IUserService -{ - Task GetUserIdAsync(); -} - -public class UserService(ILocalStorageService storageService, IHttpContextAccessor contextAccessor) : IUserService -{ - private readonly ILocalStorageService _storageService = storageService; - private readonly HttpContext? context = contextAccessor?.HttpContext; - - public const string UserIdKey = "userId"; - - public async Task GetUserIdAsync() - { - return await GetUserIdFromCookieAsync(); - } - - private async Task GetUserIdFromLocalStorageAsync() - { - var storedUserId = await _storageService.GetItemAsync(UserIdKey); - if (!Guid.TryParse(storedUserId, out Guid userId)) - { - userId = Guid.NewGuid(); - await _storageService.SetItemAsync(UserIdKey, userId.ToString()); - } - - return userId; - } - - private async Task GetUserIdFromCookieAsync() - { - var userIdCookie = context?.Request.Cookies[UserIdKey]; - var userId = Guid.NewGuid(); - await Task.Run(() => - { - if (!string.IsNullOrEmpty(userIdCookie)) - { - userId = Guid.Parse(userIdCookie); - } - else - { - context?.Response.Cookies.Append(UserIdKey, userId.ToString(), new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }); - userIdCookie = userId.ToString(); - } - }); - - - return userId; - } -} \ No newline at end of file diff --git a/src/Presentation.Blazor/Services/UserSyncService.cs b/src/Presentation.Blazor/Services/UserSyncService.cs new file mode 100644 index 0000000..f66c311 --- /dev/null +++ b/src/Presentation.Blazor/Services/UserSyncService.cs @@ -0,0 +1,73 @@ +using Goodtocode.SemanticKernel.Presentation.WebApi.Client; +using System.Security.Authentication; +using System.Security.Claims; +using Cannery.Aspects.Components.Auth.Services; +using Cannery.Aspects.Components.Auth; + +namespace Goodtocode.SemanticKernel.Presentation.Blazor.Services; + +public class UserSyncService(BackendApiClient apiClient, IUserClaimsInfo userInfo) : ApiService, IUserSyncService +{ + private readonly BackendApiClient _apiClient = apiClient; + private readonly IUserClaimsInfo _userInfo = userInfo; + + public const string UserSyncClaimName = "user_sync_status"; + + public enum SyncStatus + { + Pending, + Synced, + Failed + } + + private bool _isSyncing; + + public void UserChanged(ClaimsPrincipal? user) + { + SetSyncStatus(user, SyncStatus.Pending); + } + + public async Task SyncUserAsync(ClaimsPrincipal? user) + { + if (_isSyncing) return; + _isSyncing = true; + try + { + ClaimsIdentity? identity = user?.Identity as ClaimsIdentity ?? throw new AuthenticationException("User identity is missing or invalid."); + + var syncClaim = identity.FindFirst(UserSyncClaimName)?.Value ?? UserSyncService.SyncStatus.Pending.ToString(); + if (identity.IsAuthenticated && syncClaim == UserSyncService.SyncStatus.Pending.ToString()) + { + await HandleApiException(() => _apiClient.SaveMyActorAsync(new SaveMyActorCommand + { + TenantId = _userInfo.TenantId, + FirstName = _userInfo.Givenname, + LastName = _userInfo.Surname, + Email = _userInfo.Email + })); + } + SetSyncStatus(user, SyncStatus.Synced); + } + catch (Exception) + { + SetSyncStatus(user, SyncStatus.Failed); + throw; + } + finally + { + _isSyncing = false; + } + } + + protected void SetSyncStatus(ClaimsPrincipal? user, SyncStatus newStatus) + { + ClaimsIdentity? identity = user?.Identity as ClaimsIdentity ?? throw new AuthenticationException("User identity is missing or invalid."); + var existingClaim = identity.FindFirst(UserSyncClaimName); + if (existingClaim != null) + { + identity.RemoveClaim(existingClaim); + } + + identity.AddClaim(new Claim(UserSyncClaimName, newStatus.ToString())); + } +} diff --git a/src/Presentation.Blazor/appsettings.Development.json b/src/Presentation.Blazor/appsettings.Development.json index 349df89..5254bc0 100644 --- a/src/Presentation.Blazor/appsettings.Development.json +++ b/src/Presentation.Blazor/appsettings.Development.json @@ -6,13 +6,20 @@ } }, "ApplicationInsights": { - "ConnectionString": "InstrumentationKey=00000000-0000-0000-0000-000000000000;EndpointSuffix=applicationinsights.azure.com" + "ConnectionString": "InstrumentationKey=9a7279d9-9800-43d2-b05f-155605768276;IngestionEndpoint=https://westus2-2.in.applicationinsights.azure.com/;LiveEndpoint=https://westus2.livediagnostics.monitor.azure.com/;ApplicationId=6a69e75a-cbaa-441f-ad5c-1df55479adbf" }, "Clarity": { - "ProjectId": "" + "ProjectId": "v8s54axxyw" }, "AllowedHosts": "*", - "WebApi": { - "BaseUrl": "https://localhost:7777" + "BackendApi": { + "BaseUrl": "", + "ClientId": "" + }, + "EntraExternalId": { + "Instance": "https://goodtocodesecuredev.ciamlogin.com/", + "TenantId": "", + "ClientId": "", + "ValidateAuthority": true } } diff --git a/src/Presentation.Blazor/appsettings.Local.json b/src/Presentation.Blazor/appsettings.Local.json index 349df89..573d8ce 100644 --- a/src/Presentation.Blazor/appsettings.Local.json +++ b/src/Presentation.Blazor/appsettings.Local.json @@ -6,13 +6,20 @@ } }, "ApplicationInsights": { - "ConnectionString": "InstrumentationKey=00000000-0000-0000-0000-000000000000;EndpointSuffix=applicationinsights.azure.com" + "ConnectionString": "InstrumentationKey=9a7279d9-9800-43d2-b05f-155605768276;IngestionEndpoint=https://westus2-2.in.applicationinsights.azure.com/;LiveEndpoint=https://westus2.livediagnostics.monitor.azure.com/;ApplicationId=6a69e75a-cbaa-441f-ad5c-1df55479adbf" }, "Clarity": { - "ProjectId": "" + "ProjectId": "v8s54axxyw" }, "AllowedHosts": "*", - "WebApi": { - "BaseUrl": "https://localhost:7777" + "BackendApi": { + "BaseUrl": "https://localhost:6075", + "ClientId": "c0150160-f59e-4798-933a-a06fcf5193d6" + }, + "EntraExternalId": { + "Instance": "https://goodtocodesecuredev.ciamlogin.com/", + "TenantId": "19800ad2-82e2-4946-abd5-c87ba78a7224", + "ClientId": "ccc36192-7b76-4f3d-9965-27a3aacc76a5", + "ValidateAuthority": true } } diff --git a/src/Presentation.Blazor/appsettings.Production.json b/src/Presentation.Blazor/appsettings.Production.json index 349df89..0c947ed 100644 --- a/src/Presentation.Blazor/appsettings.Production.json +++ b/src/Presentation.Blazor/appsettings.Production.json @@ -6,13 +6,20 @@ } }, "ApplicationInsights": { - "ConnectionString": "InstrumentationKey=00000000-0000-0000-0000-000000000000;EndpointSuffix=applicationinsights.azure.com" + "ConnectionString": "" }, "Clarity": { - "ProjectId": "" + "ProjectId": "v8s54axxyw" }, "AllowedHosts": "*", - "WebApi": { - "BaseUrl": "https://localhost:7777" + "BackendApi": { + "BaseUrl": "", + "ClientId": "" + }, + "EntraExternalId": { + "Instance": "https://goodtocodesecuredev.ciamlogin.com/", + "TenantId": "", + "ClientId": "", + "ValidateAuthority": true } } diff --git a/src/Presentation.Blazor/wwwroot/app.css b/src/Presentation.Blazor/wwwroot/app.css index f7dc195..e06d025 100644 --- a/src/Presentation.Blazor/wwwroot/app.css +++ b/src/Presentation.Blazor/wwwroot/app.css @@ -1,48 +1,14 @@ + :root { - --gtc-primary-bg: #0078D4; - --gtc-primary-border: #0078D4; - --gtc-primary-bg-hover: #005A9E; - --gtc-primary-border-hover: #005A9E; + /* Semantic Kernel (DI-UX) Branding Variables */ + --di-font: 'Segoe UI', 'Inter', Arial, sans-serif; + /* Fluent UI variable overrides for light mode */ + --fluent-header-background: #181f2a; /* dark header */ + --fluent-header-foreground: #f3f4f6; /* light text */ + --fluent-body-background: #F9FAFB; /* light body/nav */ + --fluent-body-foreground: #111827; /* dark text */ } html, body { - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - height: 100%; - min-height: 100vh; -} - -h1:focus { - outline: none !important; - box-shadow: none !important; -} - -a { - text-decoration: none; -} - -a:focus { - outline: none !important; - box-shadow: none !important; -} - -.bg-primary-translucent { - background-color: rgba(var(--bs-primary-rgb), 0.8) !important; -} - -.bg-secondary-translucent { - background-color: rgba(var(--bs-secondary-rgb), 0.8) !important; -} - -.input-group .form-control { - border-radius: 0.25rem; -} - -.gtc-primary { - background-color: var(--gtc-primary-bg); - border-color: var(--gtc-primary-border); + font-family: var(--di-font); } - -.gtc-primary:hover { - background-color: var(--gtc-primary-bg-hover); - border-color: var(--gtc-primary-border-hover); -} \ No newline at end of file diff --git a/src/Presentation.Blazor/wwwroot/css/site.css b/src/Presentation.Blazor/wwwroot/css/site.css new file mode 100644 index 0000000..e69de29 diff --git a/src/Presentation.Blazor/wwwroot/favicon.png b/src/Presentation.Blazor/wwwroot/favicon.png index 9b3a8ab318963462d44972e02f614279e95ffa9b..27bc9b7d85c66a9d3b41cebecc81aa983c912d7a 100644 GIT binary patch literal 5916 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 QzZS1LNJphs$u{DD0C$WF(f|Me delta 1323 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 Y4layaSXIQP00000Nks-uM6N<$f@>I&b^rhX delta 2182 zcmbW3`9Bkm1IFjf5lO-vF)T%fIdY$qV+@fiEVH$hV@NrsZ6Ab@8098M?xSe&k+b@^ zNyvt|Dhf>_Liq6M`!9Tdc%J9={^j}gc^+u9ML`rNgtd@>lmGw#5VEy_UHy|@{}UhY zAJ?lg=cIz1d7uaFT+P&o5DqAH#g?=>xuZ8Y6BrEu@SU@TnY%{%t``tOTqkA8T`%R+ zM1H0x@~8FFOd{jNWhnmGWM~3_B6dkq#4a@bQ|bc&8&0axlBc{;pr95?(+qB-zymE8 zqm^T3)n|rNiEn2He_n* z&=dp+CK~v@E_l#u5;V(L)BM41hD$QJ%%>-OdVhb|OeOEH z8o?=#n0JOT=kgO&KsCKVB!Ro-@22V`FrP+zb53RTLmq)W3$QRtE9#IunicP&FEKJXe36~zMSI1?M&2+D8YAgbxb z3!?E{=kERHI?u1W(T5P(>gOXO#)NR|*8w36ddTl_tBMgMs+>75HBvKzA9@=bGphrc zeWgRgGa&M$4E}f=`PVH<>2+7JkFSgAJ*`^cOELbxjll#aJ8W6*6w8wCYFVF>z9@e$ zc!Vzr&FO3q11#KdiTbjyQ&r2b^iR~p(?61Xoq9g(IXBfbKw1JPliPQjI1SijG%b5B z7oW0N?AAYX`?k-urGG)$LF^y2n46}iYdouU7c5D1w+VZjB02rR$m9iXSMBckf=gpf z7{O<4yxFzM!|QBG$3`*!FUSn!eEXwthJ57Rm zG;}j-crEwk&4E$KztYcOHj)|}{Nf||(jfy?qQ$AR4Zoj{W3KbcD``LUF^stI z#J;#CLTGb5p7Qf^=n|*zK40!N-yM7r`25)2q^n!7r*N-~*go6di%=Poi%K3Ai1Szp4s<(Oh(A3sK zOpL?$?ze?YhJLepQQzrmRRk>^rJH){jxv?t{IQEtH^x;baAT!lK*QSY=HNM$5f53% zGc}vF{5CZqU}rd2YZ;pjG4~;@O6{Sw1;5r#_v*yEjg@^t^$7Bx!O`o0fF~{ZGZGTJ zI{LK{5<}bVZJDg)%dqy>3GoGs7Mr0vU-OiKFRi%jk-4f7Mc=y%0q~u1uqzbn1a|2Y z*1TIk6F^hHm}_MR<|28koDIBiQdc*ik5*goy3D3Q-?iGcN&QrU;L*`gZ&%z&?Fw$h zf~S(ZY~2!~zR_CE=j|eE2IoSrGsdkwQfS?GeBv6A#R98%hxEn|)1@@P6E2Y93|2Wj zXOZRA(e#Q3K41i9$b{uVrs0($d$^8HXeow4Jq)InD{*;~AaUpJ9PMHiRh)=c*}OC9wpLAX z5SG|{3n4gKS+dLhrYLRTNs0aT!#xv}v%N-;mn5OC&vKoIb-MzD@+@>yzHr5Z> zNj5~v;`-_aCOQV$h{IjC@B$J(%9Bl~!V^mVB)2Ps={;YH{hT`@_w(=%=72eICbObp z^5Y@^zph5bvs5dGx3;Kce>VqRl--|}wImZ%Xds_oeX8*hh)gV$$x45C7RF_t@?Fh0 zO$ek-)@egxPmnZQz4uVL6u@CLl43u99Z@ZZfdM)cd3aXo{b=<+L!<7J@awcK>76AX z9tAvYb_@|7vhBTH%Ha!~zpf;FDky{1*JhxXg(?jDlmb@fd2#nRF>G}n+POVje4NTx zjKsd#bp4KLk{H6!vIiPFdNNLz%*vrzzYRF|%-s{q!Ty*rfO_AKsO^FR!R&FI;!vF@ zB&P9@y94GhlzmyX%Rs#Qiw_Tit^V@fc|v3Om4mQQ7aESyyX66JG+Ox^tF(|Ao?oQ? y)aU140Jc^LScAol*#7|XStQE< diff --git a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css b/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css deleted file mode 100644 index 3882a81..0000000 --- a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css +++ /dev/null @@ -1,4085 +0,0 @@ -/*! - * Bootstrap Grid v5.3.3 (https://getbootstrap.com/) - * Copyright 2011-2024 The Bootstrap Authors - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ -.container, -.container-fluid, -.container-xxl, -.container-xl, -.container-lg, -.container-md, -.container-sm { - --bs-gutter-x: 1.5rem; - --bs-gutter-y: 0; - width: 100%; - padding-right: calc(var(--bs-gutter-x) * 0.5); - padding-left: calc(var(--bs-gutter-x) * 0.5); - margin-right: auto; - margin-left: auto; -} - -@media (min-width: 576px) { - .container-sm, .container { - max-width: 540px; - } -} -@media (min-width: 768px) { - .container-md, .container-sm, .container { - max-width: 720px; - } -} -@media (min-width: 992px) { - .container-lg, .container-md, .container-sm, .container { - max-width: 960px; - } -} -@media (min-width: 1200px) { - .container-xl, .container-lg, .container-md, .container-sm, .container { - max-width: 1140px; - } -} -@media (min-width: 1400px) { - .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container { - max-width: 1320px; - } -} -:root { - --bs-breakpoint-xs: 0; - --bs-breakpoint-sm: 576px; - --bs-breakpoint-md: 768px; - --bs-breakpoint-lg: 992px; - --bs-breakpoint-xl: 1200px; - --bs-breakpoint-xxl: 1400px; -} - -.row { - --bs-gutter-x: 1.5rem; - --bs-gutter-y: 0; - display: flex; - flex-wrap: wrap; - margin-top: calc(-1 * var(--bs-gutter-y)); - margin-right: calc(-0.5 * var(--bs-gutter-x)); - margin-left: calc(-0.5 * var(--bs-gutter-x)); -} -.row > * { - box-sizing: border-box; - flex-shrink: 0; - width: 100%; - max-width: 100%; - padding-right: calc(var(--bs-gutter-x) * 0.5); - padding-left: calc(var(--bs-gutter-x) * 0.5); - margin-top: var(--bs-gutter-y); -} - -.col { - flex: 1 0 0%; -} - -.row-cols-auto > * { - flex: 0 0 auto; - width: auto; -} - -.row-cols-1 > * { - flex: 0 0 auto; - width: 100%; -} - -.row-cols-2 > * { - flex: 0 0 auto; - width: 50%; -} - -.row-cols-3 > * { - flex: 0 0 auto; - width: 33.33333333%; -} - -.row-cols-4 > * { - flex: 0 0 auto; - width: 25%; -} - -.row-cols-5 > * { - flex: 0 0 auto; - width: 20%; -} - -.row-cols-6 > * { - flex: 0 0 auto; - width: 16.66666667%; -} - -.col-auto { - flex: 0 0 auto; - width: auto; -} - -.col-1 { - flex: 0 0 auto; - width: 8.33333333%; -} - -.col-2 { - flex: 0 0 auto; - width: 16.66666667%; -} - -.col-3 { - flex: 0 0 auto; - width: 25%; -} - -.col-4 { - flex: 0 0 auto; - width: 33.33333333%; -} - -.col-5 { - flex: 0 0 auto; - width: 41.66666667%; -} - -.col-6 { - flex: 0 0 auto; - width: 50%; -} - -.col-7 { - flex: 0 0 auto; - width: 58.33333333%; -} - -.col-8 { - flex: 0 0 auto; - width: 66.66666667%; -} - -.col-9 { - flex: 0 0 auto; - width: 75%; -} - -.col-10 { - flex: 0 0 auto; - width: 83.33333333%; -} - -.col-11 { - flex: 0 0 auto; - width: 91.66666667%; -} - -.col-12 { - flex: 0 0 auto; - width: 100%; -} - -.offset-1 { - margin-left: 8.33333333%; -} - -.offset-2 { - margin-left: 16.66666667%; -} - -.offset-3 { - margin-left: 25%; -} - -.offset-4 { - margin-left: 33.33333333%; -} - -.offset-5 { - margin-left: 41.66666667%; -} - -.offset-6 { - margin-left: 50%; -} - -.offset-7 { - margin-left: 58.33333333%; -} - -.offset-8 { - margin-left: 66.66666667%; -} - -.offset-9 { - margin-left: 75%; -} - -.offset-10 { - margin-left: 83.33333333%; -} - -.offset-11 { - margin-left: 91.66666667%; -} - -.g-0, -.gx-0 { - --bs-gutter-x: 0; -} - -.g-0, -.gy-0 { - --bs-gutter-y: 0; -} - -.g-1, -.gx-1 { - --bs-gutter-x: 0.25rem; -} - -.g-1, -.gy-1 { - --bs-gutter-y: 0.25rem; -} - -.g-2, -.gx-2 { - --bs-gutter-x: 0.5rem; -} - -.g-2, -.gy-2 { - --bs-gutter-y: 0.5rem; -} - -.g-3, -.gx-3 { - --bs-gutter-x: 1rem; -} - -.g-3, -.gy-3 { - --bs-gutter-y: 1rem; -} - -.g-4, -.gx-4 { - --bs-gutter-x: 1.5rem; -} - -.g-4, -.gy-4 { - --bs-gutter-y: 1.5rem; -} - -.g-5, -.gx-5 { - --bs-gutter-x: 3rem; -} - -.g-5, -.gy-5 { - --bs-gutter-y: 3rem; -} - -@media (min-width: 576px) { - .col-sm { - flex: 1 0 0%; - } - .row-cols-sm-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-sm-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-sm-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-sm-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-sm-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-sm-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-sm-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-sm-auto { - flex: 0 0 auto; - width: auto; - } - .col-sm-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-sm-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-sm-3 { - flex: 0 0 auto; - width: 25%; - } - .col-sm-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-sm-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-sm-6 { - flex: 0 0 auto; - width: 50%; - } - .col-sm-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-sm-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-sm-9 { - flex: 0 0 auto; - width: 75%; - } - .col-sm-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-sm-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-sm-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-sm-0 { - margin-left: 0; - } - .offset-sm-1 { - margin-left: 8.33333333%; - } - .offset-sm-2 { - margin-left: 16.66666667%; - } - .offset-sm-3 { - margin-left: 25%; - } - .offset-sm-4 { - margin-left: 33.33333333%; - } - .offset-sm-5 { - margin-left: 41.66666667%; - } - .offset-sm-6 { - margin-left: 50%; - } - .offset-sm-7 { - margin-left: 58.33333333%; - } - .offset-sm-8 { - margin-left: 66.66666667%; - } - .offset-sm-9 { - margin-left: 75%; - } - .offset-sm-10 { - margin-left: 83.33333333%; - } - .offset-sm-11 { - margin-left: 91.66666667%; - } - .g-sm-0, - .gx-sm-0 { - --bs-gutter-x: 0; - } - .g-sm-0, - .gy-sm-0 { - --bs-gutter-y: 0; - } - .g-sm-1, - .gx-sm-1 { - --bs-gutter-x: 0.25rem; - } - .g-sm-1, - .gy-sm-1 { - --bs-gutter-y: 0.25rem; - } - .g-sm-2, - .gx-sm-2 { - --bs-gutter-x: 0.5rem; - } - .g-sm-2, - .gy-sm-2 { - --bs-gutter-y: 0.5rem; - } - .g-sm-3, - .gx-sm-3 { - --bs-gutter-x: 1rem; - } - .g-sm-3, - .gy-sm-3 { - --bs-gutter-y: 1rem; - } - .g-sm-4, - .gx-sm-4 { - --bs-gutter-x: 1.5rem; - } - .g-sm-4, - .gy-sm-4 { - --bs-gutter-y: 1.5rem; - } - .g-sm-5, - .gx-sm-5 { - --bs-gutter-x: 3rem; - } - .g-sm-5, - .gy-sm-5 { - --bs-gutter-y: 3rem; - } -} -@media (min-width: 768px) { - .col-md { - flex: 1 0 0%; - } - .row-cols-md-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-md-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-md-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-md-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-md-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-md-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-md-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-md-auto { - flex: 0 0 auto; - width: auto; - } - .col-md-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-md-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-md-3 { - flex: 0 0 auto; - width: 25%; - } - .col-md-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-md-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-md-6 { - flex: 0 0 auto; - width: 50%; - } - .col-md-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-md-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-md-9 { - flex: 0 0 auto; - width: 75%; - } - .col-md-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-md-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-md-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-md-0 { - margin-left: 0; - } - .offset-md-1 { - margin-left: 8.33333333%; - } - .offset-md-2 { - margin-left: 16.66666667%; - } - .offset-md-3 { - margin-left: 25%; - } - .offset-md-4 { - margin-left: 33.33333333%; - } - .offset-md-5 { - margin-left: 41.66666667%; - } - .offset-md-6 { - margin-left: 50%; - } - .offset-md-7 { - margin-left: 58.33333333%; - } - .offset-md-8 { - margin-left: 66.66666667%; - } - .offset-md-9 { - margin-left: 75%; - } - .offset-md-10 { - margin-left: 83.33333333%; - } - .offset-md-11 { - margin-left: 91.66666667%; - } - .g-md-0, - .gx-md-0 { - --bs-gutter-x: 0; - } - .g-md-0, - .gy-md-0 { - --bs-gutter-y: 0; - } - .g-md-1, - .gx-md-1 { - --bs-gutter-x: 0.25rem; - } - .g-md-1, - .gy-md-1 { - --bs-gutter-y: 0.25rem; - } - .g-md-2, - .gx-md-2 { - --bs-gutter-x: 0.5rem; - } - .g-md-2, - .gy-md-2 { - --bs-gutter-y: 0.5rem; - } - .g-md-3, - .gx-md-3 { - --bs-gutter-x: 1rem; - } - .g-md-3, - .gy-md-3 { - --bs-gutter-y: 1rem; - } - .g-md-4, - .gx-md-4 { - --bs-gutter-x: 1.5rem; - } - .g-md-4, - .gy-md-4 { - --bs-gutter-y: 1.5rem; - } - .g-md-5, - .gx-md-5 { - --bs-gutter-x: 3rem; - } - .g-md-5, - .gy-md-5 { - --bs-gutter-y: 3rem; - } -} -@media (min-width: 992px) { - .col-lg { - flex: 1 0 0%; - } - .row-cols-lg-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-lg-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-lg-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-lg-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-lg-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-lg-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-lg-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-lg-auto { - flex: 0 0 auto; - width: auto; - } - .col-lg-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-lg-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-lg-3 { - flex: 0 0 auto; - width: 25%; - } - .col-lg-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-lg-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-lg-6 { - flex: 0 0 auto; - width: 50%; - } - .col-lg-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-lg-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-lg-9 { - flex: 0 0 auto; - width: 75%; - } - .col-lg-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-lg-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-lg-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-lg-0 { - margin-left: 0; - } - .offset-lg-1 { - margin-left: 8.33333333%; - } - .offset-lg-2 { - margin-left: 16.66666667%; - } - .offset-lg-3 { - margin-left: 25%; - } - .offset-lg-4 { - margin-left: 33.33333333%; - } - .offset-lg-5 { - margin-left: 41.66666667%; - } - .offset-lg-6 { - margin-left: 50%; - } - .offset-lg-7 { - margin-left: 58.33333333%; - } - .offset-lg-8 { - margin-left: 66.66666667%; - } - .offset-lg-9 { - margin-left: 75%; - } - .offset-lg-10 { - margin-left: 83.33333333%; - } - .offset-lg-11 { - margin-left: 91.66666667%; - } - .g-lg-0, - .gx-lg-0 { - --bs-gutter-x: 0; - } - .g-lg-0, - .gy-lg-0 { - --bs-gutter-y: 0; - } - .g-lg-1, - .gx-lg-1 { - --bs-gutter-x: 0.25rem; - } - .g-lg-1, - .gy-lg-1 { - --bs-gutter-y: 0.25rem; - } - .g-lg-2, - .gx-lg-2 { - --bs-gutter-x: 0.5rem; - } - .g-lg-2, - .gy-lg-2 { - --bs-gutter-y: 0.5rem; - } - .g-lg-3, - .gx-lg-3 { - --bs-gutter-x: 1rem; - } - .g-lg-3, - .gy-lg-3 { - --bs-gutter-y: 1rem; - } - .g-lg-4, - .gx-lg-4 { - --bs-gutter-x: 1.5rem; - } - .g-lg-4, - .gy-lg-4 { - --bs-gutter-y: 1.5rem; - } - .g-lg-5, - .gx-lg-5 { - --bs-gutter-x: 3rem; - } - .g-lg-5, - .gy-lg-5 { - --bs-gutter-y: 3rem; - } -} -@media (min-width: 1200px) { - .col-xl { - flex: 1 0 0%; - } - .row-cols-xl-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-xl-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-xl-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-xl-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-xl-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-xl-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-xl-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xl-auto { - flex: 0 0 auto; - width: auto; - } - .col-xl-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-xl-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xl-3 { - flex: 0 0 auto; - width: 25%; - } - .col-xl-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-xl-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-xl-6 { - flex: 0 0 auto; - width: 50%; - } - .col-xl-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-xl-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-xl-9 { - flex: 0 0 auto; - width: 75%; - } - .col-xl-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-xl-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-xl-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-xl-0 { - margin-left: 0; - } - .offset-xl-1 { - margin-left: 8.33333333%; - } - .offset-xl-2 { - margin-left: 16.66666667%; - } - .offset-xl-3 { - margin-left: 25%; - } - .offset-xl-4 { - margin-left: 33.33333333%; - } - .offset-xl-5 { - margin-left: 41.66666667%; - } - .offset-xl-6 { - margin-left: 50%; - } - .offset-xl-7 { - margin-left: 58.33333333%; - } - .offset-xl-8 { - margin-left: 66.66666667%; - } - .offset-xl-9 { - margin-left: 75%; - } - .offset-xl-10 { - margin-left: 83.33333333%; - } - .offset-xl-11 { - margin-left: 91.66666667%; - } - .g-xl-0, - .gx-xl-0 { - --bs-gutter-x: 0; - } - .g-xl-0, - .gy-xl-0 { - --bs-gutter-y: 0; - } - .g-xl-1, - .gx-xl-1 { - --bs-gutter-x: 0.25rem; - } - .g-xl-1, - .gy-xl-1 { - --bs-gutter-y: 0.25rem; - } - .g-xl-2, - .gx-xl-2 { - --bs-gutter-x: 0.5rem; - } - .g-xl-2, - .gy-xl-2 { - --bs-gutter-y: 0.5rem; - } - .g-xl-3, - .gx-xl-3 { - --bs-gutter-x: 1rem; - } - .g-xl-3, - .gy-xl-3 { - --bs-gutter-y: 1rem; - } - .g-xl-4, - .gx-xl-4 { - --bs-gutter-x: 1.5rem; - } - .g-xl-4, - .gy-xl-4 { - --bs-gutter-y: 1.5rem; - } - .g-xl-5, - .gx-xl-5 { - --bs-gutter-x: 3rem; - } - .g-xl-5, - .gy-xl-5 { - --bs-gutter-y: 3rem; - } -} -@media (min-width: 1400px) { - .col-xxl { - flex: 1 0 0%; - } - .row-cols-xxl-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-xxl-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-xxl-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-xxl-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-xxl-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-xxl-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-xxl-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xxl-auto { - flex: 0 0 auto; - width: auto; - } - .col-xxl-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-xxl-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xxl-3 { - flex: 0 0 auto; - width: 25%; - } - .col-xxl-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-xxl-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-xxl-6 { - flex: 0 0 auto; - width: 50%; - } - .col-xxl-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-xxl-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-xxl-9 { - flex: 0 0 auto; - width: 75%; - } - .col-xxl-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-xxl-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-xxl-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-xxl-0 { - margin-left: 0; - } - .offset-xxl-1 { - margin-left: 8.33333333%; - } - .offset-xxl-2 { - margin-left: 16.66666667%; - } - .offset-xxl-3 { - margin-left: 25%; - } - .offset-xxl-4 { - margin-left: 33.33333333%; - } - .offset-xxl-5 { - margin-left: 41.66666667%; - } - .offset-xxl-6 { - margin-left: 50%; - } - .offset-xxl-7 { - margin-left: 58.33333333%; - } - .offset-xxl-8 { - margin-left: 66.66666667%; - } - .offset-xxl-9 { - margin-left: 75%; - } - .offset-xxl-10 { - margin-left: 83.33333333%; - } - .offset-xxl-11 { - margin-left: 91.66666667%; - } - .g-xxl-0, - .gx-xxl-0 { - --bs-gutter-x: 0; - } - .g-xxl-0, - .gy-xxl-0 { - --bs-gutter-y: 0; - } - .g-xxl-1, - .gx-xxl-1 { - --bs-gutter-x: 0.25rem; - } - .g-xxl-1, - .gy-xxl-1 { - --bs-gutter-y: 0.25rem; - } - .g-xxl-2, - .gx-xxl-2 { - --bs-gutter-x: 0.5rem; - } - .g-xxl-2, - .gy-xxl-2 { - --bs-gutter-y: 0.5rem; - } - .g-xxl-3, - .gx-xxl-3 { - --bs-gutter-x: 1rem; - } - .g-xxl-3, - .gy-xxl-3 { - --bs-gutter-y: 1rem; - } - .g-xxl-4, - .gx-xxl-4 { - --bs-gutter-x: 1.5rem; - } - .g-xxl-4, - .gy-xxl-4 { - --bs-gutter-y: 1.5rem; - } - .g-xxl-5, - .gx-xxl-5 { - --bs-gutter-x: 3rem; - } - .g-xxl-5, - .gy-xxl-5 { - --bs-gutter-y: 3rem; - } -} -.d-inline { - display: inline !important; -} - -.d-inline-block { - display: inline-block !important; -} - -.d-block { - display: block !important; -} - -.d-grid { - display: grid !important; -} - -.d-inline-grid { - display: inline-grid !important; -} - -.d-table { - display: table !important; -} - -.d-table-row { - display: table-row !important; -} - -.d-table-cell { - display: table-cell !important; -} - -.d-flex { - display: flex !important; -} - -.d-inline-flex { - display: inline-flex !important; -} - -.d-none { - display: none !important; -} - -.flex-fill { - flex: 1 1 auto !important; -} - -.flex-row { - flex-direction: row !important; -} - -.flex-column { - flex-direction: column !important; -} - -.flex-row-reverse { - flex-direction: row-reverse !important; -} - -.flex-column-reverse { - flex-direction: column-reverse !important; -} - -.flex-grow-0 { - flex-grow: 0 !important; -} - -.flex-grow-1 { - flex-grow: 1 !important; -} - -.flex-shrink-0 { - flex-shrink: 0 !important; -} - -.flex-shrink-1 { - flex-shrink: 1 !important; -} - -.flex-wrap { - flex-wrap: wrap !important; -} - -.flex-nowrap { - flex-wrap: nowrap !important; -} - -.flex-wrap-reverse { - flex-wrap: wrap-reverse !important; -} - -.justify-content-start { - justify-content: flex-start !important; -} - -.justify-content-end { - justify-content: flex-end !important; -} - -.justify-content-center { - justify-content: center !important; -} - -.justify-content-between { - justify-content: space-between !important; -} - -.justify-content-around { - justify-content: space-around !important; -} - -.justify-content-evenly { - justify-content: space-evenly !important; -} - -.align-items-start { - align-items: flex-start !important; -} - -.align-items-end { - align-items: flex-end !important; -} - -.align-items-center { - align-items: center !important; -} - -.align-items-baseline { - align-items: baseline !important; -} - -.align-items-stretch { - align-items: stretch !important; -} - -.align-content-start { - align-content: flex-start !important; -} - -.align-content-end { - align-content: flex-end !important; -} - -.align-content-center { - align-content: center !important; -} - -.align-content-between { - align-content: space-between !important; -} - -.align-content-around { - align-content: space-around !important; -} - -.align-content-stretch { - align-content: stretch !important; -} - -.align-self-auto { - align-self: auto !important; -} - -.align-self-start { - align-self: flex-start !important; -} - -.align-self-end { - align-self: flex-end !important; -} - -.align-self-center { - align-self: center !important; -} - -.align-self-baseline { - align-self: baseline !important; -} - -.align-self-stretch { - align-self: stretch !important; -} - -.order-first { - order: -1 !important; -} - -.order-0 { - order: 0 !important; -} - -.order-1 { - order: 1 !important; -} - -.order-2 { - order: 2 !important; -} - -.order-3 { - order: 3 !important; -} - -.order-4 { - order: 4 !important; -} - -.order-5 { - order: 5 !important; -} - -.order-last { - order: 6 !important; -} - -.m-0 { - margin: 0 !important; -} - -.m-1 { - margin: 0.25rem !important; -} - -.m-2 { - margin: 0.5rem !important; -} - -.m-3 { - margin: 1rem !important; -} - -.m-4 { - margin: 1.5rem !important; -} - -.m-5 { - margin: 3rem !important; -} - -.m-auto { - margin: auto !important; -} - -.mx-0 { - margin-right: 0 !important; - margin-left: 0 !important; -} - -.mx-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; -} - -.mx-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; -} - -.mx-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; -} - -.mx-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; -} - -.mx-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; -} - -.mx-auto { - margin-right: auto !important; - margin-left: auto !important; -} - -.my-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; -} - -.my-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; -} - -.my-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; -} - -.my-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; -} - -.my-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; -} - -.my-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; -} - -.my-auto { - margin-top: auto !important; - margin-bottom: auto !important; -} - -.mt-0 { - margin-top: 0 !important; -} - -.mt-1 { - margin-top: 0.25rem !important; -} - -.mt-2 { - margin-top: 0.5rem !important; -} - -.mt-3 { - margin-top: 1rem !important; -} - -.mt-4 { - margin-top: 1.5rem !important; -} - -.mt-5 { - margin-top: 3rem !important; -} - -.mt-auto { - margin-top: auto !important; -} - -.me-0 { - margin-right: 0 !important; -} - -.me-1 { - margin-right: 0.25rem !important; -} - -.me-2 { - margin-right: 0.5rem !important; -} - -.me-3 { - margin-right: 1rem !important; -} - -.me-4 { - margin-right: 1.5rem !important; -} - -.me-5 { - margin-right: 3rem !important; -} - -.me-auto { - margin-right: auto !important; -} - -.mb-0 { - margin-bottom: 0 !important; -} - -.mb-1 { - margin-bottom: 0.25rem !important; -} - -.mb-2 { - margin-bottom: 0.5rem !important; -} - -.mb-3 { - margin-bottom: 1rem !important; -} - -.mb-4 { - margin-bottom: 1.5rem !important; -} - -.mb-5 { - margin-bottom: 3rem !important; -} - -.mb-auto { - margin-bottom: auto !important; -} - -.ms-0 { - margin-left: 0 !important; -} - -.ms-1 { - margin-left: 0.25rem !important; -} - -.ms-2 { - margin-left: 0.5rem !important; -} - -.ms-3 { - margin-left: 1rem !important; -} - -.ms-4 { - margin-left: 1.5rem !important; -} - -.ms-5 { - margin-left: 3rem !important; -} - -.ms-auto { - margin-left: auto !important; -} - -.p-0 { - padding: 0 !important; -} - -.p-1 { - padding: 0.25rem !important; -} - -.p-2 { - padding: 0.5rem !important; -} - -.p-3 { - padding: 1rem !important; -} - -.p-4 { - padding: 1.5rem !important; -} - -.p-5 { - padding: 3rem !important; -} - -.px-0 { - padding-right: 0 !important; - padding-left: 0 !important; -} - -.px-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; -} - -.px-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; -} - -.px-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; -} - -.px-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; -} - -.px-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; -} - -.py-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; -} - -.py-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; -} - -.py-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; -} - -.py-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; -} - -.py-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; -} - -.py-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; -} - -.pt-0 { - padding-top: 0 !important; -} - -.pt-1 { - padding-top: 0.25rem !important; -} - -.pt-2 { - padding-top: 0.5rem !important; -} - -.pt-3 { - padding-top: 1rem !important; -} - -.pt-4 { - padding-top: 1.5rem !important; -} - -.pt-5 { - padding-top: 3rem !important; -} - -.pe-0 { - padding-right: 0 !important; -} - -.pe-1 { - padding-right: 0.25rem !important; -} - -.pe-2 { - padding-right: 0.5rem !important; -} - -.pe-3 { - padding-right: 1rem !important; -} - -.pe-4 { - padding-right: 1.5rem !important; -} - -.pe-5 { - padding-right: 3rem !important; -} - -.pb-0 { - padding-bottom: 0 !important; -} - -.pb-1 { - padding-bottom: 0.25rem !important; -} - -.pb-2 { - padding-bottom: 0.5rem !important; -} - -.pb-3 { - padding-bottom: 1rem !important; -} - -.pb-4 { - padding-bottom: 1.5rem !important; -} - -.pb-5 { - padding-bottom: 3rem !important; -} - -.ps-0 { - padding-left: 0 !important; -} - -.ps-1 { - padding-left: 0.25rem !important; -} - -.ps-2 { - padding-left: 0.5rem !important; -} - -.ps-3 { - padding-left: 1rem !important; -} - -.ps-4 { - padding-left: 1.5rem !important; -} - -.ps-5 { - padding-left: 3rem !important; -} - -@media (min-width: 576px) { - .d-sm-inline { - display: inline !important; - } - .d-sm-inline-block { - display: inline-block !important; - } - .d-sm-block { - display: block !important; - } - .d-sm-grid { - display: grid !important; - } - .d-sm-inline-grid { - display: inline-grid !important; - } - .d-sm-table { - display: table !important; - } - .d-sm-table-row { - display: table-row !important; - } - .d-sm-table-cell { - display: table-cell !important; - } - .d-sm-flex { - display: flex !important; - } - .d-sm-inline-flex { - display: inline-flex !important; - } - .d-sm-none { - display: none !important; - } - .flex-sm-fill { - flex: 1 1 auto !important; - } - .flex-sm-row { - flex-direction: row !important; - } - .flex-sm-column { - flex-direction: column !important; - } - .flex-sm-row-reverse { - flex-direction: row-reverse !important; - } - .flex-sm-column-reverse { - flex-direction: column-reverse !important; - } - .flex-sm-grow-0 { - flex-grow: 0 !important; - } - .flex-sm-grow-1 { - flex-grow: 1 !important; - } - .flex-sm-shrink-0 { - flex-shrink: 0 !important; - } - .flex-sm-shrink-1 { - flex-shrink: 1 !important; - } - .flex-sm-wrap { - flex-wrap: wrap !important; - } - .flex-sm-nowrap { - flex-wrap: nowrap !important; - } - .flex-sm-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-sm-start { - justify-content: flex-start !important; - } - .justify-content-sm-end { - justify-content: flex-end !important; - } - .justify-content-sm-center { - justify-content: center !important; - } - .justify-content-sm-between { - justify-content: space-between !important; - } - .justify-content-sm-around { - justify-content: space-around !important; - } - .justify-content-sm-evenly { - justify-content: space-evenly !important; - } - .align-items-sm-start { - align-items: flex-start !important; - } - .align-items-sm-end { - align-items: flex-end !important; - } - .align-items-sm-center { - align-items: center !important; - } - .align-items-sm-baseline { - align-items: baseline !important; - } - .align-items-sm-stretch { - align-items: stretch !important; - } - .align-content-sm-start { - align-content: flex-start !important; - } - .align-content-sm-end { - align-content: flex-end !important; - } - .align-content-sm-center { - align-content: center !important; - } - .align-content-sm-between { - align-content: space-between !important; - } - .align-content-sm-around { - align-content: space-around !important; - } - .align-content-sm-stretch { - align-content: stretch !important; - } - .align-self-sm-auto { - align-self: auto !important; - } - .align-self-sm-start { - align-self: flex-start !important; - } - .align-self-sm-end { - align-self: flex-end !important; - } - .align-self-sm-center { - align-self: center !important; - } - .align-self-sm-baseline { - align-self: baseline !important; - } - .align-self-sm-stretch { - align-self: stretch !important; - } - .order-sm-first { - order: -1 !important; - } - .order-sm-0 { - order: 0 !important; - } - .order-sm-1 { - order: 1 !important; - } - .order-sm-2 { - order: 2 !important; - } - .order-sm-3 { - order: 3 !important; - } - .order-sm-4 { - order: 4 !important; - } - .order-sm-5 { - order: 5 !important; - } - .order-sm-last { - order: 6 !important; - } - .m-sm-0 { - margin: 0 !important; - } - .m-sm-1 { - margin: 0.25rem !important; - } - .m-sm-2 { - margin: 0.5rem !important; - } - .m-sm-3 { - margin: 1rem !important; - } - .m-sm-4 { - margin: 1.5rem !important; - } - .m-sm-5 { - margin: 3rem !important; - } - .m-sm-auto { - margin: auto !important; - } - .mx-sm-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-sm-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-sm-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-sm-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-sm-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-sm-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-sm-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-sm-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-sm-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-sm-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-sm-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-sm-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-sm-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-sm-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-sm-0 { - margin-top: 0 !important; - } - .mt-sm-1 { - margin-top: 0.25rem !important; - } - .mt-sm-2 { - margin-top: 0.5rem !important; - } - .mt-sm-3 { - margin-top: 1rem !important; - } - .mt-sm-4 { - margin-top: 1.5rem !important; - } - .mt-sm-5 { - margin-top: 3rem !important; - } - .mt-sm-auto { - margin-top: auto !important; - } - .me-sm-0 { - margin-right: 0 !important; - } - .me-sm-1 { - margin-right: 0.25rem !important; - } - .me-sm-2 { - margin-right: 0.5rem !important; - } - .me-sm-3 { - margin-right: 1rem !important; - } - .me-sm-4 { - margin-right: 1.5rem !important; - } - .me-sm-5 { - margin-right: 3rem !important; - } - .me-sm-auto { - margin-right: auto !important; - } - .mb-sm-0 { - margin-bottom: 0 !important; - } - .mb-sm-1 { - margin-bottom: 0.25rem !important; - } - .mb-sm-2 { - margin-bottom: 0.5rem !important; - } - .mb-sm-3 { - margin-bottom: 1rem !important; - } - .mb-sm-4 { - margin-bottom: 1.5rem !important; - } - .mb-sm-5 { - margin-bottom: 3rem !important; - } - .mb-sm-auto { - margin-bottom: auto !important; - } - .ms-sm-0 { - margin-left: 0 !important; - } - .ms-sm-1 { - margin-left: 0.25rem !important; - } - .ms-sm-2 { - margin-left: 0.5rem !important; - } - .ms-sm-3 { - margin-left: 1rem !important; - } - .ms-sm-4 { - margin-left: 1.5rem !important; - } - .ms-sm-5 { - margin-left: 3rem !important; - } - .ms-sm-auto { - margin-left: auto !important; - } - .p-sm-0 { - padding: 0 !important; - } - .p-sm-1 { - padding: 0.25rem !important; - } - .p-sm-2 { - padding: 0.5rem !important; - } - .p-sm-3 { - padding: 1rem !important; - } - .p-sm-4 { - padding: 1.5rem !important; - } - .p-sm-5 { - padding: 3rem !important; - } - .px-sm-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-sm-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-sm-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-sm-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-sm-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-sm-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-sm-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-sm-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-sm-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-sm-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-sm-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-sm-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-sm-0 { - padding-top: 0 !important; - } - .pt-sm-1 { - padding-top: 0.25rem !important; - } - .pt-sm-2 { - padding-top: 0.5rem !important; - } - .pt-sm-3 { - padding-top: 1rem !important; - } - .pt-sm-4 { - padding-top: 1.5rem !important; - } - .pt-sm-5 { - padding-top: 3rem !important; - } - .pe-sm-0 { - padding-right: 0 !important; - } - .pe-sm-1 { - padding-right: 0.25rem !important; - } - .pe-sm-2 { - padding-right: 0.5rem !important; - } - .pe-sm-3 { - padding-right: 1rem !important; - } - .pe-sm-4 { - padding-right: 1.5rem !important; - } - .pe-sm-5 { - padding-right: 3rem !important; - } - .pb-sm-0 { - padding-bottom: 0 !important; - } - .pb-sm-1 { - padding-bottom: 0.25rem !important; - } - .pb-sm-2 { - padding-bottom: 0.5rem !important; - } - .pb-sm-3 { - padding-bottom: 1rem !important; - } - .pb-sm-4 { - padding-bottom: 1.5rem !important; - } - .pb-sm-5 { - padding-bottom: 3rem !important; - } - .ps-sm-0 { - padding-left: 0 !important; - } - .ps-sm-1 { - padding-left: 0.25rem !important; - } - .ps-sm-2 { - padding-left: 0.5rem !important; - } - .ps-sm-3 { - padding-left: 1rem !important; - } - .ps-sm-4 { - padding-left: 1.5rem !important; - } - .ps-sm-5 { - padding-left: 3rem !important; - } -} -@media (min-width: 768px) { - .d-md-inline { - display: inline !important; - } - .d-md-inline-block { - display: inline-block !important; - } - .d-md-block { - display: block !important; - } - .d-md-grid { - display: grid !important; - } - .d-md-inline-grid { - display: inline-grid !important; - } - .d-md-table { - display: table !important; - } - .d-md-table-row { - display: table-row !important; - } - .d-md-table-cell { - display: table-cell !important; - } - .d-md-flex { - display: flex !important; - } - .d-md-inline-flex { - display: inline-flex !important; - } - .d-md-none { - display: none !important; - } - .flex-md-fill { - flex: 1 1 auto !important; - } - .flex-md-row { - flex-direction: row !important; - } - .flex-md-column { - flex-direction: column !important; - } - .flex-md-row-reverse { - flex-direction: row-reverse !important; - } - .flex-md-column-reverse { - flex-direction: column-reverse !important; - } - .flex-md-grow-0 { - flex-grow: 0 !important; - } - .flex-md-grow-1 { - flex-grow: 1 !important; - } - .flex-md-shrink-0 { - flex-shrink: 0 !important; - } - .flex-md-shrink-1 { - flex-shrink: 1 !important; - } - .flex-md-wrap { - flex-wrap: wrap !important; - } - .flex-md-nowrap { - flex-wrap: nowrap !important; - } - .flex-md-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-md-start { - justify-content: flex-start !important; - } - .justify-content-md-end { - justify-content: flex-end !important; - } - .justify-content-md-center { - justify-content: center !important; - } - .justify-content-md-between { - justify-content: space-between !important; - } - .justify-content-md-around { - justify-content: space-around !important; - } - .justify-content-md-evenly { - justify-content: space-evenly !important; - } - .align-items-md-start { - align-items: flex-start !important; - } - .align-items-md-end { - align-items: flex-end !important; - } - .align-items-md-center { - align-items: center !important; - } - .align-items-md-baseline { - align-items: baseline !important; - } - .align-items-md-stretch { - align-items: stretch !important; - } - .align-content-md-start { - align-content: flex-start !important; - } - .align-content-md-end { - align-content: flex-end !important; - } - .align-content-md-center { - align-content: center !important; - } - .align-content-md-between { - align-content: space-between !important; - } - .align-content-md-around { - align-content: space-around !important; - } - .align-content-md-stretch { - align-content: stretch !important; - } - .align-self-md-auto { - align-self: auto !important; - } - .align-self-md-start { - align-self: flex-start !important; - } - .align-self-md-end { - align-self: flex-end !important; - } - .align-self-md-center { - align-self: center !important; - } - .align-self-md-baseline { - align-self: baseline !important; - } - .align-self-md-stretch { - align-self: stretch !important; - } - .order-md-first { - order: -1 !important; - } - .order-md-0 { - order: 0 !important; - } - .order-md-1 { - order: 1 !important; - } - .order-md-2 { - order: 2 !important; - } - .order-md-3 { - order: 3 !important; - } - .order-md-4 { - order: 4 !important; - } - .order-md-5 { - order: 5 !important; - } - .order-md-last { - order: 6 !important; - } - .m-md-0 { - margin: 0 !important; - } - .m-md-1 { - margin: 0.25rem !important; - } - .m-md-2 { - margin: 0.5rem !important; - } - .m-md-3 { - margin: 1rem !important; - } - .m-md-4 { - margin: 1.5rem !important; - } - .m-md-5 { - margin: 3rem !important; - } - .m-md-auto { - margin: auto !important; - } - .mx-md-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-md-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-md-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-md-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-md-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-md-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-md-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-md-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-md-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-md-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-md-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-md-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-md-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-md-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-md-0 { - margin-top: 0 !important; - } - .mt-md-1 { - margin-top: 0.25rem !important; - } - .mt-md-2 { - margin-top: 0.5rem !important; - } - .mt-md-3 { - margin-top: 1rem !important; - } - .mt-md-4 { - margin-top: 1.5rem !important; - } - .mt-md-5 { - margin-top: 3rem !important; - } - .mt-md-auto { - margin-top: auto !important; - } - .me-md-0 { - margin-right: 0 !important; - } - .me-md-1 { - margin-right: 0.25rem !important; - } - .me-md-2 { - margin-right: 0.5rem !important; - } - .me-md-3 { - margin-right: 1rem !important; - } - .me-md-4 { - margin-right: 1.5rem !important; - } - .me-md-5 { - margin-right: 3rem !important; - } - .me-md-auto { - margin-right: auto !important; - } - .mb-md-0 { - margin-bottom: 0 !important; - } - .mb-md-1 { - margin-bottom: 0.25rem !important; - } - .mb-md-2 { - margin-bottom: 0.5rem !important; - } - .mb-md-3 { - margin-bottom: 1rem !important; - } - .mb-md-4 { - margin-bottom: 1.5rem !important; - } - .mb-md-5 { - margin-bottom: 3rem !important; - } - .mb-md-auto { - margin-bottom: auto !important; - } - .ms-md-0 { - margin-left: 0 !important; - } - .ms-md-1 { - margin-left: 0.25rem !important; - } - .ms-md-2 { - margin-left: 0.5rem !important; - } - .ms-md-3 { - margin-left: 1rem !important; - } - .ms-md-4 { - margin-left: 1.5rem !important; - } - .ms-md-5 { - margin-left: 3rem !important; - } - .ms-md-auto { - margin-left: auto !important; - } - .p-md-0 { - padding: 0 !important; - } - .p-md-1 { - padding: 0.25rem !important; - } - .p-md-2 { - padding: 0.5rem !important; - } - .p-md-3 { - padding: 1rem !important; - } - .p-md-4 { - padding: 1.5rem !important; - } - .p-md-5 { - padding: 3rem !important; - } - .px-md-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-md-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-md-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-md-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-md-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-md-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-md-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-md-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-md-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-md-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-md-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-md-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-md-0 { - padding-top: 0 !important; - } - .pt-md-1 { - padding-top: 0.25rem !important; - } - .pt-md-2 { - padding-top: 0.5rem !important; - } - .pt-md-3 { - padding-top: 1rem !important; - } - .pt-md-4 { - padding-top: 1.5rem !important; - } - .pt-md-5 { - padding-top: 3rem !important; - } - .pe-md-0 { - padding-right: 0 !important; - } - .pe-md-1 { - padding-right: 0.25rem !important; - } - .pe-md-2 { - padding-right: 0.5rem !important; - } - .pe-md-3 { - padding-right: 1rem !important; - } - .pe-md-4 { - padding-right: 1.5rem !important; - } - .pe-md-5 { - padding-right: 3rem !important; - } - .pb-md-0 { - padding-bottom: 0 !important; - } - .pb-md-1 { - padding-bottom: 0.25rem !important; - } - .pb-md-2 { - padding-bottom: 0.5rem !important; - } - .pb-md-3 { - padding-bottom: 1rem !important; - } - .pb-md-4 { - padding-bottom: 1.5rem !important; - } - .pb-md-5 { - padding-bottom: 3rem !important; - } - .ps-md-0 { - padding-left: 0 !important; - } - .ps-md-1 { - padding-left: 0.25rem !important; - } - .ps-md-2 { - padding-left: 0.5rem !important; - } - .ps-md-3 { - padding-left: 1rem !important; - } - .ps-md-4 { - padding-left: 1.5rem !important; - } - .ps-md-5 { - padding-left: 3rem !important; - } -} -@media (min-width: 992px) { - .d-lg-inline { - display: inline !important; - } - .d-lg-inline-block { - display: inline-block !important; - } - .d-lg-block { - display: block !important; - } - .d-lg-grid { - display: grid !important; - } - .d-lg-inline-grid { - display: inline-grid !important; - } - .d-lg-table { - display: table !important; - } - .d-lg-table-row { - display: table-row !important; - } - .d-lg-table-cell { - display: table-cell !important; - } - .d-lg-flex { - display: flex !important; - } - .d-lg-inline-flex { - display: inline-flex !important; - } - .d-lg-none { - display: none !important; - } - .flex-lg-fill { - flex: 1 1 auto !important; - } - .flex-lg-row { - flex-direction: row !important; - } - .flex-lg-column { - flex-direction: column !important; - } - .flex-lg-row-reverse { - flex-direction: row-reverse !important; - } - .flex-lg-column-reverse { - flex-direction: column-reverse !important; - } - .flex-lg-grow-0 { - flex-grow: 0 !important; - } - .flex-lg-grow-1 { - flex-grow: 1 !important; - } - .flex-lg-shrink-0 { - flex-shrink: 0 !important; - } - .flex-lg-shrink-1 { - flex-shrink: 1 !important; - } - .flex-lg-wrap { - flex-wrap: wrap !important; - } - .flex-lg-nowrap { - flex-wrap: nowrap !important; - } - .flex-lg-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-lg-start { - justify-content: flex-start !important; - } - .justify-content-lg-end { - justify-content: flex-end !important; - } - .justify-content-lg-center { - justify-content: center !important; - } - .justify-content-lg-between { - justify-content: space-between !important; - } - .justify-content-lg-around { - justify-content: space-around !important; - } - .justify-content-lg-evenly { - justify-content: space-evenly !important; - } - .align-items-lg-start { - align-items: flex-start !important; - } - .align-items-lg-end { - align-items: flex-end !important; - } - .align-items-lg-center { - align-items: center !important; - } - .align-items-lg-baseline { - align-items: baseline !important; - } - .align-items-lg-stretch { - align-items: stretch !important; - } - .align-content-lg-start { - align-content: flex-start !important; - } - .align-content-lg-end { - align-content: flex-end !important; - } - .align-content-lg-center { - align-content: center !important; - } - .align-content-lg-between { - align-content: space-between !important; - } - .align-content-lg-around { - align-content: space-around !important; - } - .align-content-lg-stretch { - align-content: stretch !important; - } - .align-self-lg-auto { - align-self: auto !important; - } - .align-self-lg-start { - align-self: flex-start !important; - } - .align-self-lg-end { - align-self: flex-end !important; - } - .align-self-lg-center { - align-self: center !important; - } - .align-self-lg-baseline { - align-self: baseline !important; - } - .align-self-lg-stretch { - align-self: stretch !important; - } - .order-lg-first { - order: -1 !important; - } - .order-lg-0 { - order: 0 !important; - } - .order-lg-1 { - order: 1 !important; - } - .order-lg-2 { - order: 2 !important; - } - .order-lg-3 { - order: 3 !important; - } - .order-lg-4 { - order: 4 !important; - } - .order-lg-5 { - order: 5 !important; - } - .order-lg-last { - order: 6 !important; - } - .m-lg-0 { - margin: 0 !important; - } - .m-lg-1 { - margin: 0.25rem !important; - } - .m-lg-2 { - margin: 0.5rem !important; - } - .m-lg-3 { - margin: 1rem !important; - } - .m-lg-4 { - margin: 1.5rem !important; - } - .m-lg-5 { - margin: 3rem !important; - } - .m-lg-auto { - margin: auto !important; - } - .mx-lg-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-lg-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-lg-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-lg-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-lg-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-lg-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-lg-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-lg-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-lg-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-lg-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-lg-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-lg-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-lg-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-lg-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-lg-0 { - margin-top: 0 !important; - } - .mt-lg-1 { - margin-top: 0.25rem !important; - } - .mt-lg-2 { - margin-top: 0.5rem !important; - } - .mt-lg-3 { - margin-top: 1rem !important; - } - .mt-lg-4 { - margin-top: 1.5rem !important; - } - .mt-lg-5 { - margin-top: 3rem !important; - } - .mt-lg-auto { - margin-top: auto !important; - } - .me-lg-0 { - margin-right: 0 !important; - } - .me-lg-1 { - margin-right: 0.25rem !important; - } - .me-lg-2 { - margin-right: 0.5rem !important; - } - .me-lg-3 { - margin-right: 1rem !important; - } - .me-lg-4 { - margin-right: 1.5rem !important; - } - .me-lg-5 { - margin-right: 3rem !important; - } - .me-lg-auto { - margin-right: auto !important; - } - .mb-lg-0 { - margin-bottom: 0 !important; - } - .mb-lg-1 { - margin-bottom: 0.25rem !important; - } - .mb-lg-2 { - margin-bottom: 0.5rem !important; - } - .mb-lg-3 { - margin-bottom: 1rem !important; - } - .mb-lg-4 { - margin-bottom: 1.5rem !important; - } - .mb-lg-5 { - margin-bottom: 3rem !important; - } - .mb-lg-auto { - margin-bottom: auto !important; - } - .ms-lg-0 { - margin-left: 0 !important; - } - .ms-lg-1 { - margin-left: 0.25rem !important; - } - .ms-lg-2 { - margin-left: 0.5rem !important; - } - .ms-lg-3 { - margin-left: 1rem !important; - } - .ms-lg-4 { - margin-left: 1.5rem !important; - } - .ms-lg-5 { - margin-left: 3rem !important; - } - .ms-lg-auto { - margin-left: auto !important; - } - .p-lg-0 { - padding: 0 !important; - } - .p-lg-1 { - padding: 0.25rem !important; - } - .p-lg-2 { - padding: 0.5rem !important; - } - .p-lg-3 { - padding: 1rem !important; - } - .p-lg-4 { - padding: 1.5rem !important; - } - .p-lg-5 { - padding: 3rem !important; - } - .px-lg-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-lg-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-lg-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-lg-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-lg-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-lg-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-lg-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-lg-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-lg-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-lg-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-lg-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-lg-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-lg-0 { - padding-top: 0 !important; - } - .pt-lg-1 { - padding-top: 0.25rem !important; - } - .pt-lg-2 { - padding-top: 0.5rem !important; - } - .pt-lg-3 { - padding-top: 1rem !important; - } - .pt-lg-4 { - padding-top: 1.5rem !important; - } - .pt-lg-5 { - padding-top: 3rem !important; - } - .pe-lg-0 { - padding-right: 0 !important; - } - .pe-lg-1 { - padding-right: 0.25rem !important; - } - .pe-lg-2 { - padding-right: 0.5rem !important; - } - .pe-lg-3 { - padding-right: 1rem !important; - } - .pe-lg-4 { - padding-right: 1.5rem !important; - } - .pe-lg-5 { - padding-right: 3rem !important; - } - .pb-lg-0 { - padding-bottom: 0 !important; - } - .pb-lg-1 { - padding-bottom: 0.25rem !important; - } - .pb-lg-2 { - padding-bottom: 0.5rem !important; - } - .pb-lg-3 { - padding-bottom: 1rem !important; - } - .pb-lg-4 { - padding-bottom: 1.5rem !important; - } - .pb-lg-5 { - padding-bottom: 3rem !important; - } - .ps-lg-0 { - padding-left: 0 !important; - } - .ps-lg-1 { - padding-left: 0.25rem !important; - } - .ps-lg-2 { - padding-left: 0.5rem !important; - } - .ps-lg-3 { - padding-left: 1rem !important; - } - .ps-lg-4 { - padding-left: 1.5rem !important; - } - .ps-lg-5 { - padding-left: 3rem !important; - } -} -@media (min-width: 1200px) { - .d-xl-inline { - display: inline !important; - } - .d-xl-inline-block { - display: inline-block !important; - } - .d-xl-block { - display: block !important; - } - .d-xl-grid { - display: grid !important; - } - .d-xl-inline-grid { - display: inline-grid !important; - } - .d-xl-table { - display: table !important; - } - .d-xl-table-row { - display: table-row !important; - } - .d-xl-table-cell { - display: table-cell !important; - } - .d-xl-flex { - display: flex !important; - } - .d-xl-inline-flex { - display: inline-flex !important; - } - .d-xl-none { - display: none !important; - } - .flex-xl-fill { - flex: 1 1 auto !important; - } - .flex-xl-row { - flex-direction: row !important; - } - .flex-xl-column { - flex-direction: column !important; - } - .flex-xl-row-reverse { - flex-direction: row-reverse !important; - } - .flex-xl-column-reverse { - flex-direction: column-reverse !important; - } - .flex-xl-grow-0 { - flex-grow: 0 !important; - } - .flex-xl-grow-1 { - flex-grow: 1 !important; - } - .flex-xl-shrink-0 { - flex-shrink: 0 !important; - } - .flex-xl-shrink-1 { - flex-shrink: 1 !important; - } - .flex-xl-wrap { - flex-wrap: wrap !important; - } - .flex-xl-nowrap { - flex-wrap: nowrap !important; - } - .flex-xl-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-xl-start { - justify-content: flex-start !important; - } - .justify-content-xl-end { - justify-content: flex-end !important; - } - .justify-content-xl-center { - justify-content: center !important; - } - .justify-content-xl-between { - justify-content: space-between !important; - } - .justify-content-xl-around { - justify-content: space-around !important; - } - .justify-content-xl-evenly { - justify-content: space-evenly !important; - } - .align-items-xl-start { - align-items: flex-start !important; - } - .align-items-xl-end { - align-items: flex-end !important; - } - .align-items-xl-center { - align-items: center !important; - } - .align-items-xl-baseline { - align-items: baseline !important; - } - .align-items-xl-stretch { - align-items: stretch !important; - } - .align-content-xl-start { - align-content: flex-start !important; - } - .align-content-xl-end { - align-content: flex-end !important; - } - .align-content-xl-center { - align-content: center !important; - } - .align-content-xl-between { - align-content: space-between !important; - } - .align-content-xl-around { - align-content: space-around !important; - } - .align-content-xl-stretch { - align-content: stretch !important; - } - .align-self-xl-auto { - align-self: auto !important; - } - .align-self-xl-start { - align-self: flex-start !important; - } - .align-self-xl-end { - align-self: flex-end !important; - } - .align-self-xl-center { - align-self: center !important; - } - .align-self-xl-baseline { - align-self: baseline !important; - } - .align-self-xl-stretch { - align-self: stretch !important; - } - .order-xl-first { - order: -1 !important; - } - .order-xl-0 { - order: 0 !important; - } - .order-xl-1 { - order: 1 !important; - } - .order-xl-2 { - order: 2 !important; - } - .order-xl-3 { - order: 3 !important; - } - .order-xl-4 { - order: 4 !important; - } - .order-xl-5 { - order: 5 !important; - } - .order-xl-last { - order: 6 !important; - } - .m-xl-0 { - margin: 0 !important; - } - .m-xl-1 { - margin: 0.25rem !important; - } - .m-xl-2 { - margin: 0.5rem !important; - } - .m-xl-3 { - margin: 1rem !important; - } - .m-xl-4 { - margin: 1.5rem !important; - } - .m-xl-5 { - margin: 3rem !important; - } - .m-xl-auto { - margin: auto !important; - } - .mx-xl-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-xl-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-xl-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-xl-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-xl-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-xl-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-xl-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-xl-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-xl-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-xl-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-xl-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-xl-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-xl-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-xl-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-xl-0 { - margin-top: 0 !important; - } - .mt-xl-1 { - margin-top: 0.25rem !important; - } - .mt-xl-2 { - margin-top: 0.5rem !important; - } - .mt-xl-3 { - margin-top: 1rem !important; - } - .mt-xl-4 { - margin-top: 1.5rem !important; - } - .mt-xl-5 { - margin-top: 3rem !important; - } - .mt-xl-auto { - margin-top: auto !important; - } - .me-xl-0 { - margin-right: 0 !important; - } - .me-xl-1 { - margin-right: 0.25rem !important; - } - .me-xl-2 { - margin-right: 0.5rem !important; - } - .me-xl-3 { - margin-right: 1rem !important; - } - .me-xl-4 { - margin-right: 1.5rem !important; - } - .me-xl-5 { - margin-right: 3rem !important; - } - .me-xl-auto { - margin-right: auto !important; - } - .mb-xl-0 { - margin-bottom: 0 !important; - } - .mb-xl-1 { - margin-bottom: 0.25rem !important; - } - .mb-xl-2 { - margin-bottom: 0.5rem !important; - } - .mb-xl-3 { - margin-bottom: 1rem !important; - } - .mb-xl-4 { - margin-bottom: 1.5rem !important; - } - .mb-xl-5 { - margin-bottom: 3rem !important; - } - .mb-xl-auto { - margin-bottom: auto !important; - } - .ms-xl-0 { - margin-left: 0 !important; - } - .ms-xl-1 { - margin-left: 0.25rem !important; - } - .ms-xl-2 { - margin-left: 0.5rem !important; - } - .ms-xl-3 { - margin-left: 1rem !important; - } - .ms-xl-4 { - margin-left: 1.5rem !important; - } - .ms-xl-5 { - margin-left: 3rem !important; - } - .ms-xl-auto { - margin-left: auto !important; - } - .p-xl-0 { - padding: 0 !important; - } - .p-xl-1 { - padding: 0.25rem !important; - } - .p-xl-2 { - padding: 0.5rem !important; - } - .p-xl-3 { - padding: 1rem !important; - } - .p-xl-4 { - padding: 1.5rem !important; - } - .p-xl-5 { - padding: 3rem !important; - } - .px-xl-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-xl-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-xl-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-xl-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-xl-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-xl-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-xl-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-xl-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-xl-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-xl-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-xl-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-xl-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-xl-0 { - padding-top: 0 !important; - } - .pt-xl-1 { - padding-top: 0.25rem !important; - } - .pt-xl-2 { - padding-top: 0.5rem !important; - } - .pt-xl-3 { - padding-top: 1rem !important; - } - .pt-xl-4 { - padding-top: 1.5rem !important; - } - .pt-xl-5 { - padding-top: 3rem !important; - } - .pe-xl-0 { - padding-right: 0 !important; - } - .pe-xl-1 { - padding-right: 0.25rem !important; - } - .pe-xl-2 { - padding-right: 0.5rem !important; - } - .pe-xl-3 { - padding-right: 1rem !important; - } - .pe-xl-4 { - padding-right: 1.5rem !important; - } - .pe-xl-5 { - padding-right: 3rem !important; - } - .pb-xl-0 { - padding-bottom: 0 !important; - } - .pb-xl-1 { - padding-bottom: 0.25rem !important; - } - .pb-xl-2 { - padding-bottom: 0.5rem !important; - } - .pb-xl-3 { - padding-bottom: 1rem !important; - } - .pb-xl-4 { - padding-bottom: 1.5rem !important; - } - .pb-xl-5 { - padding-bottom: 3rem !important; - } - .ps-xl-0 { - padding-left: 0 !important; - } - .ps-xl-1 { - padding-left: 0.25rem !important; - } - .ps-xl-2 { - padding-left: 0.5rem !important; - } - .ps-xl-3 { - padding-left: 1rem !important; - } - .ps-xl-4 { - padding-left: 1.5rem !important; - } - .ps-xl-5 { - padding-left: 3rem !important; - } -} -@media (min-width: 1400px) { - .d-xxl-inline { - display: inline !important; - } - .d-xxl-inline-block { - display: inline-block !important; - } - .d-xxl-block { - display: block !important; - } - .d-xxl-grid { - display: grid !important; - } - .d-xxl-inline-grid { - display: inline-grid !important; - } - .d-xxl-table { - display: table !important; - } - .d-xxl-table-row { - display: table-row !important; - } - .d-xxl-table-cell { - display: table-cell !important; - } - .d-xxl-flex { - display: flex !important; - } - .d-xxl-inline-flex { - display: inline-flex !important; - } - .d-xxl-none { - display: none !important; - } - .flex-xxl-fill { - flex: 1 1 auto !important; - } - .flex-xxl-row { - flex-direction: row !important; - } - .flex-xxl-column { - flex-direction: column !important; - } - .flex-xxl-row-reverse { - flex-direction: row-reverse !important; - } - .flex-xxl-column-reverse { - flex-direction: column-reverse !important; - } - .flex-xxl-grow-0 { - flex-grow: 0 !important; - } - .flex-xxl-grow-1 { - flex-grow: 1 !important; - } - .flex-xxl-shrink-0 { - flex-shrink: 0 !important; - } - .flex-xxl-shrink-1 { - flex-shrink: 1 !important; - } - .flex-xxl-wrap { - flex-wrap: wrap !important; - } - .flex-xxl-nowrap { - flex-wrap: nowrap !important; - } - .flex-xxl-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-xxl-start { - justify-content: flex-start !important; - } - .justify-content-xxl-end { - justify-content: flex-end !important; - } - .justify-content-xxl-center { - justify-content: center !important; - } - .justify-content-xxl-between { - justify-content: space-between !important; - } - .justify-content-xxl-around { - justify-content: space-around !important; - } - .justify-content-xxl-evenly { - justify-content: space-evenly !important; - } - .align-items-xxl-start { - align-items: flex-start !important; - } - .align-items-xxl-end { - align-items: flex-end !important; - } - .align-items-xxl-center { - align-items: center !important; - } - .align-items-xxl-baseline { - align-items: baseline !important; - } - .align-items-xxl-stretch { - align-items: stretch !important; - } - .align-content-xxl-start { - align-content: flex-start !important; - } - .align-content-xxl-end { - align-content: flex-end !important; - } - .align-content-xxl-center { - align-content: center !important; - } - .align-content-xxl-between { - align-content: space-between !important; - } - .align-content-xxl-around { - align-content: space-around !important; - } - .align-content-xxl-stretch { - align-content: stretch !important; - } - .align-self-xxl-auto { - align-self: auto !important; - } - .align-self-xxl-start { - align-self: flex-start !important; - } - .align-self-xxl-end { - align-self: flex-end !important; - } - .align-self-xxl-center { - align-self: center !important; - } - .align-self-xxl-baseline { - align-self: baseline !important; - } - .align-self-xxl-stretch { - align-self: stretch !important; - } - .order-xxl-first { - order: -1 !important; - } - .order-xxl-0 { - order: 0 !important; - } - .order-xxl-1 { - order: 1 !important; - } - .order-xxl-2 { - order: 2 !important; - } - .order-xxl-3 { - order: 3 !important; - } - .order-xxl-4 { - order: 4 !important; - } - .order-xxl-5 { - order: 5 !important; - } - .order-xxl-last { - order: 6 !important; - } - .m-xxl-0 { - margin: 0 !important; - } - .m-xxl-1 { - margin: 0.25rem !important; - } - .m-xxl-2 { - margin: 0.5rem !important; - } - .m-xxl-3 { - margin: 1rem !important; - } - .m-xxl-4 { - margin: 1.5rem !important; - } - .m-xxl-5 { - margin: 3rem !important; - } - .m-xxl-auto { - margin: auto !important; - } - .mx-xxl-0 { - margin-right: 0 !important; - margin-left: 0 !important; - } - .mx-xxl-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; - } - .mx-xxl-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; - } - .mx-xxl-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; - } - .mx-xxl-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; - } - .mx-xxl-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; - } - .mx-xxl-auto { - margin-right: auto !important; - margin-left: auto !important; - } - .my-xxl-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-xxl-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-xxl-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-xxl-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-xxl-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-xxl-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-xxl-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-xxl-0 { - margin-top: 0 !important; - } - .mt-xxl-1 { - margin-top: 0.25rem !important; - } - .mt-xxl-2 { - margin-top: 0.5rem !important; - } - .mt-xxl-3 { - margin-top: 1rem !important; - } - .mt-xxl-4 { - margin-top: 1.5rem !important; - } - .mt-xxl-5 { - margin-top: 3rem !important; - } - .mt-xxl-auto { - margin-top: auto !important; - } - .me-xxl-0 { - margin-right: 0 !important; - } - .me-xxl-1 { - margin-right: 0.25rem !important; - } - .me-xxl-2 { - margin-right: 0.5rem !important; - } - .me-xxl-3 { - margin-right: 1rem !important; - } - .me-xxl-4 { - margin-right: 1.5rem !important; - } - .me-xxl-5 { - margin-right: 3rem !important; - } - .me-xxl-auto { - margin-right: auto !important; - } - .mb-xxl-0 { - margin-bottom: 0 !important; - } - .mb-xxl-1 { - margin-bottom: 0.25rem !important; - } - .mb-xxl-2 { - margin-bottom: 0.5rem !important; - } - .mb-xxl-3 { - margin-bottom: 1rem !important; - } - .mb-xxl-4 { - margin-bottom: 1.5rem !important; - } - .mb-xxl-5 { - margin-bottom: 3rem !important; - } - .mb-xxl-auto { - margin-bottom: auto !important; - } - .ms-xxl-0 { - margin-left: 0 !important; - } - .ms-xxl-1 { - margin-left: 0.25rem !important; - } - .ms-xxl-2 { - margin-left: 0.5rem !important; - } - .ms-xxl-3 { - margin-left: 1rem !important; - } - .ms-xxl-4 { - margin-left: 1.5rem !important; - } - .ms-xxl-5 { - margin-left: 3rem !important; - } - .ms-xxl-auto { - margin-left: auto !important; - } - .p-xxl-0 { - padding: 0 !important; - } - .p-xxl-1 { - padding: 0.25rem !important; - } - .p-xxl-2 { - padding: 0.5rem !important; - } - .p-xxl-3 { - padding: 1rem !important; - } - .p-xxl-4 { - padding: 1.5rem !important; - } - .p-xxl-5 { - padding: 3rem !important; - } - .px-xxl-0 { - padding-right: 0 !important; - padding-left: 0 !important; - } - .px-xxl-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; - } - .px-xxl-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; - } - .px-xxl-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; - } - .px-xxl-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; - } - .px-xxl-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; - } - .py-xxl-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-xxl-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-xxl-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-xxl-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-xxl-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-xxl-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-xxl-0 { - padding-top: 0 !important; - } - .pt-xxl-1 { - padding-top: 0.25rem !important; - } - .pt-xxl-2 { - padding-top: 0.5rem !important; - } - .pt-xxl-3 { - padding-top: 1rem !important; - } - .pt-xxl-4 { - padding-top: 1.5rem !important; - } - .pt-xxl-5 { - padding-top: 3rem !important; - } - .pe-xxl-0 { - padding-right: 0 !important; - } - .pe-xxl-1 { - padding-right: 0.25rem !important; - } - .pe-xxl-2 { - padding-right: 0.5rem !important; - } - .pe-xxl-3 { - padding-right: 1rem !important; - } - .pe-xxl-4 { - padding-right: 1.5rem !important; - } - .pe-xxl-5 { - padding-right: 3rem !important; - } - .pb-xxl-0 { - padding-bottom: 0 !important; - } - .pb-xxl-1 { - padding-bottom: 0.25rem !important; - } - .pb-xxl-2 { - padding-bottom: 0.5rem !important; - } - .pb-xxl-3 { - padding-bottom: 1rem !important; - } - .pb-xxl-4 { - padding-bottom: 1.5rem !important; - } - .pb-xxl-5 { - padding-bottom: 3rem !important; - } - .ps-xxl-0 { - padding-left: 0 !important; - } - .ps-xxl-1 { - padding-left: 0.25rem !important; - } - .ps-xxl-2 { - padding-left: 0.5rem !important; - } - .ps-xxl-3 { - padding-left: 1rem !important; - } - .ps-xxl-4 { - padding-left: 1.5rem !important; - } - .ps-xxl-5 { - padding-left: 3rem !important; - } -} -@media print { - .d-print-inline { - display: inline !important; - } - .d-print-inline-block { - display: inline-block !important; - } - .d-print-block { - display: block !important; - } - .d-print-grid { - display: grid !important; - } - .d-print-inline-grid { - display: inline-grid !important; - } - .d-print-table { - display: table !important; - } - .d-print-table-row { - display: table-row !important; - } - .d-print-table-cell { - display: table-cell !important; - } - .d-print-flex { - display: flex !important; - } - .d-print-inline-flex { - display: inline-flex !important; - } - .d-print-none { - display: none !important; - } -} - -/*# sourceMappingURL=bootstrap-grid.css.map */ \ No newline at end of file diff --git a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map b/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map deleted file mode 100644 index ce99ec1..0000000 --- a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../scss/mixins/_banner.scss","../../scss/_containers.scss","../../scss/mixins/_container.scss","bootstrap-grid.css","../../scss/mixins/_breakpoints.scss","../../scss/_variables.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_utilities.scss","../../scss/utilities/_api.scss"],"names":[],"mappings":"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EE,mBAAA;EJ6+BR;EI1+BM;;IAEE,mBAAA;EJ4+BR;AACF;AKpiCQ;EAOI,0BAAA;ALgiCZ;;AKviCQ;EAOI,gCAAA;ALoiCZ;;AK3iCQ;EAOI,yBAAA;ALwiCZ;;AK/iCQ;EAOI,wBAAA;AL4iCZ;;AKnjCQ;EAOI,+BAAA;ALgjCZ;;AKvjCQ;EAOI,yBAAA;ALojCZ;;AK3jCQ;EAOI,6BAAA;ALwjCZ;;AK/jCQ;EAOI,8BAAA;AL4jCZ;;AKnkCQ;EAOI,wBAAA;ALgkCZ;;AKvkCQ;EAOI,+BAAA;ALokCZ;;AK3kCQ;EAOI,wBAAA;ALwkCZ;;AK/kCQ;EAOI,yBAAA;AL4kCZ;;AKnlCQ;EAOI,8BAAA;ALglCZ;;AKvlCQ;EAOI,iCAAA;ALolCZ;;AK3lCQ;EAOI,sCAAA;ALwlCZ;;AK/lCQ;EAOI,yCAAA;AL4lCZ;;AKnmCQ;EAOI,uBAAA;ALgmCZ;;AKvmCQ;EAOI,uBAAA;ALomCZ;;AK3mCQ;EAOI,yBAAA;ALwmCZ;;AK/mCQ;EAOI,yBAAA;AL4mCZ;;AKnnCQ;EAOI,0BAAA;ALgnCZ;;AKvnCQ;EAOI,4BAAA;ALonCZ;;AK3nCQ;EAOI,kCAAA;ALwnCZ;;AK/nCQ;EAOI,sCAAA;AL4nCZ;;AKnoCQ;EAOI,oCAAA;ALgoCZ;;AKvoCQ;EAOI,kCAAA;ALooCZ;;AK3oCQ;EAOI,yCAAA;ALwoCZ;;AK/oCQ;EAOI,wCAAA;AL4oCZ;;AKnpCQ;EAOI,wCAAA;ALgpCZ;;AKvpCQ;EAOI,kCAAA;ALopCZ;;AK3pCQ;EAOI,gCAAA;ALwpCZ;;AK/pCQ;EAOI,8BAAA;AL4pCZ;;AKnqCQ;EAOI,gCAAA;ALgqCZ;;AKvqCQ;EAOI,+BAAA;ALoqCZ;;AK3qCQ;EAOI,oCAAA;ALwqCZ;;AK/qCQ;EAOI,kCAAA;AL4qCZ;;AKnrCQ;EAOI,gCAAA;ALgrCZ;;AKvrCQ;EAOI,uCAAA;ALorCZ;;AK3rCQ;EAOI,sCAAA;ALwrCZ;;AK/rCQ;EAOI,iCAAA;AL4rCZ;;AKnsCQ;EAOI,2BAAA;ALgsCZ;;AKvsCQ;EAOI,iCAAA;ALosCZ;;AK3sCQ;EAOI,+BAAA;ALwsCZ;;AK/sCQ;EAOI,6BAAA;AL4sCZ;;AKntCQ;EA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;AL8iDZ;;AKrjDQ;EAOI,4BAAA;ALkjDZ;;AKzjDQ;EAOI,2BAAA;ALsjDZ;;AK7jDQ;EAOI,iCAAA;AL0jDZ;;AKjkDQ;EAOI,gCAAA;AL8jDZ;;AKrkDQ;EAOI,8BAAA;ALkkDZ;;AKzkDQ;EAOI,gCAAA;ALskDZ;;AK7kDQ;EAOI,8BAAA;AL0kDZ;;AKjlDQ;EAOI,4BAAA;AL8kDZ;;AKrlDQ;EAOI,kCAAA;ALklDZ;;AKzlDQ;EAOI,iCAAA;ALslDZ;;AK7lDQ;EAOI,+BAAA;AL0lDZ;;AKjmDQ;EAOI,iCAAA;AL8lDZ;;AKrmDQ;EAOI,+BAAA;ALkmDZ;;AKzmDQ;EAOI,0BAAA;ALsmDZ;;AK7mDQ;EAOI,gCAAA;AL0mDZ;;AKjnDQ;EAOI,+BAAA;AL8mDZ;;AKrnDQ;EAOI,6BAAA;ALknDZ;;AKznDQ;EAOI,+BAAA;ALsnDZ;;AK7nDQ;EAOI,6BAAA;AL0nDZ;;ACpoDI;EIGI;IAOI,0BAAA;EL+nDV;EKtoDM;IAOI,gCAAA;ELkoDV;EKzoDM;IAOI,yBAAA;ELqoDV;EK5oDM;IAOI,wBAAA;ELwoDV;EK/oDM;IAOI,+BAAA;EL2oDV;EKlpDM;IAOI,yBAAA;EL8oDV;EKrpDM;IAOI,6BAAA;ELipDV;EKxpDM;IAOI,8BAAA;ELopDV;EK3pDM;IAOI,wBAAA;ELupDV;EK9pDM;IAOI,+BAAA;EL0pDV;EKjqDM;IAOI,wBAAA;EL6pDV;EKpqDM;IAOI,yBAAA;ELgqDV;EKvqDM;IAOI,8BAAA;ELmqDV;EK1qDM;IAOI,iCAAA;ELsqDV;EK7qDM;IAOI,sCAAA;ELyqDV;EKhrDM;IAOI,yCAAA;EL4qDV;EKnrDM;IAOI,uBAAA;EL+qDV;EKtrDM;IAOI,uBAAA;ELkrDV;EKzrDM;IAOI,yBAAA;ELqrDV;EK5rDM;IAOI,yBAAA;ELwrDV;EK/rDM;IAOI,0BAAA;EL2rDV;EKlsDM;IAOI,4BAAA;EL8rDV;EKrsDM;IAOI,kCAAA;ELisDV;EKxsDM;IAOI,sCAAA;ELosDV;EK3sDM;IAOI,oCAAA;ELusDV;EK9sDM;IAOI,kCAAA;EL0sDV;EKjtDM;IAOI,yCAAA;EL6sDV;EKptDM;IAOI,wCAAA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nEV;EK/nEM;IAOI,yCAAA;EL2nEV;EKloEM;IAOI,uBAAA;EL8nEV;EKroEM;IAOI,uBAAA;ELioEV;EKxoEM;IAOI,yBAAA;ELooEV;EK3oEM;IAOI,yBAAA;ELuoEV;EK9oEM;IAOI,0BAAA;EL0oEV;EKjpEM;IAOI,4BAAA;EL6oEV;EKppEM;IAOI,kCAAA;ELgpEV;EKvpEM;IAOI,sCAAA;ELmpEV;EK1pEM;IAOI,oCAAA;ELspEV;EK7pEM;IAOI,kCAAA;ELypEV;EKhqEM;IAOI,yCAAA;EL4pEV;EKnqEM;IAOI,wCAAA;EL+pEV;EKtqEM;IAOI,wCAAA;ELkqEV;EKzqEM;IAOI,kCAAA;ELqqEV;EK5qEM;IAOI,gCAAA;ELwqEV;EK/qEM;IAOI,8BAAA;EL2qEV;EKlrEM;IAOI,gCAAA;EL8qEV;EKrrEM;IAOI,+BAAA;ELirEV;EKxrEM;IAOI,oCAAA;ELorEV;EK3rEM;IAOI,kCAAA;ELurEV;EK9rEM;IAOI,gCAAA;EL0rEV;EKjsEM;IAOI,uCAAA;EL6rEV;EKpsEM;IAOI,sCAAA;ELgsEV;EKvsEM;IAOI,iCAAA;ELmsEV;EK1sEM;IAOI,2BAAA;ELssEV;EK7sEM;IAOI,iCAAA;ELysEV;EKhtEM;IAOI,+BAAA;EL4sEV;EKntEM;IAOI,6BAAA;EL+sEV;EKttEM;IAOI,+BAAA;ELktEV;EKztEM;IAOI,8BAAA;ELqtEV;EK5tEM;IAOI,oBAAA;ELwtEV;EK/tEM;IAOI,mBAAA;EL2tEV;EKluEM;IAOI,mBAAA;EL8tEV;EKruEM;IAOI,mBAAA;ELiuEV;EKxuEM;IAOI,mBAAA;ELouEV;EK3uEM;IAOI,mBAAA;ELuuEV;EK9uEM;IAOI,mBAAA;EL0uEV;EKjvEM;IAOI,mBAAA;EL6uEV;EKpvEM;IAOI,oBAAA;ELgvEV;EKvvEM;IAOI,0BAAA;ELmvEV;EK1vEM;IAOI,yBAAA;ELsvEV;EK7vEM;IAOI,uBAAA;ELyvEV;EKhwEM;IAOI,yBAAA;EL4vEV;EKnwEM;IAOI,uBAAA;EL+vEV;EKtwEM;IAOI,uBAAA;ELkwEV;EKzwEM;IAOI,0BAAA;IAAA,yBAAA;ELswEV;EK7wEM;IAO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EKviFM;IAOI,yBAAA;ELmiFV;EK1iFM;IAOI,wBAAA;ELsiFV;EK7iFM;IAOI,+BAAA;ELyiFV;EKhjFM;IAOI,yBAAA;EL4iFV;EKnjFM;IAOI,6BAAA;EL+iFV;EKtjFM;IAOI,8BAAA;ELkjFV;EKzjFM;IAOI,wBAAA;ELqjFV;EK5jFM;IAOI,+BAAA;ELwjFV;EK/jFM;IAOI,wBAAA;EL2jFV;EKlkFM;IAOI,yBAAA;EL8jFV;EKrkFM;IAOI,8BAAA;ELikFV;EKxkFM;IAOI,iCAAA;ELokFV;EK3kFM;IAOI,sCAAA;ELukFV;EK9kFM;IAOI,yCAAA;EL0kFV;EKjlFM;IAOI,uBAAA;EL6kFV;EKplFM;IAOI,uBAAA;ELglFV;EKvlFM;IAOI,yBAAA;ELmlFV;EK1lFM;IAOI,yBAAA;ELslFV;EK7lFM;IAOI,0BAAA;ELylFV;EKhmFM;IAOI,4BAAA;EL4lFV;EKnmFM;IAOI,kCAAA;EL+lFV;EKtmFM;IAOI,sCAAA;ELkmFV;EKzmFM;IAOI,oCAAA;ELqmFV;EK5mFM;IAOI,kCAAA;ELwmFV;EK/mFM;IAOI,yCAAA;EL2mFV;EKlnFM;IAOI,wCAAA;EL8mFV;EKrnFM;IAOI,wCAAA;ELinFV;EKxnFM;IAOI,kCAAA;ELonFV;EK3nFM;IAOI,gCAAA;ELunFV;EK9nFM;IAOI,8BAAA;EL0nFV;EKjoFM;IAOI,gCAAA;EL6nFV;EKpoFM;IAOI,+BAAA;ELgoFV;EKvoFM;IAOI,oCAAA;ELmoFV;EK1oFM;IAOI,kCAAA;ELsoFV;EK7oFM;IAOI,gCAAA;ELyoFV;EKhpFM;IAOI,uCAAA;EL4oFV;EKnpFM;IAOI,sCAAA;EL+oFV;EKtpFM;IAOI,iCAAA;ELkpFV;EKzpFM;IAOI,2BAAA;ELqpFV;EK5pFM;IAOI,iCAAA;ELwpFV;EK/pFM;IAOI,+BAAA;EL2pFV;EKlqFM;IAOI,6BAAA;EL8pFV;EKrqFM;IAOI,+BAAA;ELiqFV;EKxqFM;IAOI,8BAAA;ELoqFV;EK3qFM;IAOI,oBAAA;ELuqFV;EK9qFM;IAOI,mBAAA;EL0qFV;EKjrFM;IAOI,mBAAA;EL6qFV;EKprFM;IAOI,mBAAA;ELgrFV;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AAA;ELy8FV;EKh9FM;IAOI,iCAAA;EL48FV;EKn9FM;IAOI,+BAAA;EL+8FV;EKt9FM;IAOI,iCAAA;ELk9FV;EKz9FM;IAOI,+BAAA;ELq9FV;EK59FM;IAOI,0BAAA;ELw9FV;EK/9FM;IAOI,gCAAA;EL29FV;EKl+FM;IAOI,+BAAA;EL89FV;EKr+FM;IAOI,6BAAA;ELi+FV;EKx+FM;IAOI,+BAAA;ELo+FV;EK3+FM;IAOI,6BAAA;ELu+FV;AACF;ACl/FI;EIGI;IAOI,0BAAA;EL4+FV;EKn/FM;IAOI,gCAAA;EL++FV;EKt/FM;IAOI,yBAAA;ELk/FV;EKz/FM;IAOI,wBAAA;ELq/FV;EK5/FM;IAOI,+BAAA;ELw/FV;EK//FM;IAOI,yBAAA;EL2/FV;EKlgGM;IAOI,6BAAA;EL8/FV;EKrgGM;IAOI,8BAAA;ELigGV;EKxgGM;IAOI,wBAAA;ELogGV;EK3gGM;IAOI,+BAAA;ELugGV;EK9gGM;IAOI,wBAAA;EL0gGV;EKjhGM;IAOI,yBAAA;EL6gGV;EKphGM;IAOI,8BAAA;ELghGV;EKvhGM;IAOI,iCAAA;ELmhGV;EK1hGM;IAOI,sCAAA;ELshGV;EK7hGM;IAOI,yCAAA;ELyhGV;EKhiGM;IAOI,uBAAA;EL4hGV;EKniGM;IAOI,uBAAA;EL+hGV;EKtiGM;IAOI,yBAAA;ELkiGV;EKziGM;IAOI,yBAAA;ELqiGV;EK5iGM;IAOI,0BAAA;ELwiGV;EK/iGM;IAOI,4BAAA;EL2iGV;EKljGM;IAOI,kCAAA;EL8iGV;EKrjGM;IAOI,sCAAA;ELijGV;EKxjGM;IAOI,oCAAA;ELojGV;EK3jGM;IAOI,kCAAA;ELujGV;EK9jGM;IAOI,yCAAA;EL0jGV;EKjkGM;IAOI,wCAAA;EL6jGV;EKpkGM;IAOI,wCAAA;ELgkGV;EKvkGM;IAOI,kCAAA;ELmkGV;EK1kGM;IAOI,gCAAA;ELskGV;EK7kGM;IAOI,8BAAA;ELykGV;EKhlGM;IAOI,gCAAA;EL4kGV;EKnlGM;IAOI,+BAAA;EL+kGV;EKtlGM;IAOI,oCAAA;ELklGV;EKzlGM;IAOI,kCAAA;ELqlGV;EK5lGM;IAOI,gCAAA;ELwlGV;EK/l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ELi3GV;EKx3GM;IAOI,+BAAA;ELo3GV;EK33GM;IAOI,8BAAA;ELu3GV;EK93GM;IAOI,4BAAA;EL03GV;EKj4GM;IAOI,8BAAA;EL63GV;EKp4GM;IAOI,4BAAA;ELg4GV;EKv4GM;IAOI,2BAAA;ELm4GV;EK14GM;IAOI,iCAAA;ELs4GV;EK74GM;IAOI,gCAAA;ELy4GV;EKh5GM;IAOI,8BAAA;EL44GV;EKn5GM;IAOI,gCAAA;EL+4GV;EKt5GM;IAOI,8BAAA;ELk5GV;EKz5GM;IAOI,4BAAA;ELq5GV;EK55GM;IAOI,kCAAA;ELw5GV;EK/5GM;IAOI,iCAAA;EL25GV;EKl6GM;IAOI,+BAAA;EL85GV;EKr6GM;IAOI,iCAAA;ELi6GV;EKx6GM;IAOI,+BAAA;ELo6GV;EK36GM;IAOI,0BAAA;ELu6GV;EK96GM;IAOI,gCAAA;EL06GV;EKj7GM;IAOI,+BAAA;EL66GV;EKp7GM;IAOI,6BAAA;ELg7GV;EKv7GM;IAOI,+BAAA;ELm7GV;EK17GM;IAOI,6BAAA;ELs7GV;AACF;ACj8GI;EIGI;IAOI,0BAAA;EL27GV;EKl8GM;IAOI,gCAAA;EL87GV;EKr8GM;IAOI,yBAAA;ELi8GV;EKx8GM;IAOI,wBAAA;ELo8GV;EK38GM;IAOI,+BAAA;ELu8GV;EK98GM;IAOI,yBAAA;EL08GV;EKj9GM;IAOI,6BAAA;EL68GV;EKp9GM;IAOI,8BAAA;ELg9GV;EKv9GM;IAOI,wBAAA;ELm9GV;EK19GM;IAOI,+BAAA;ELs9GV;EK79GM;IAOI,wBAAA;ELy9GV;EKh+GM;IAOI,yBAAA;EL49GV;EKn+GM;IAOI,8BAAA;EL+9GV;EKt+GM;IAOI,iCAAA;ELk+GV;EKz+GM;IAOI,sCAAA;ELq+GV;EK5+GM;IAOI,yCAAA;ELw+GV;EK/+GM;IAOI,uBAAA;EL2+GV;EKl/GM;IAOI,uBAAA;EL8+GV;EKr/GM;IAOI,yBAAA;ELi/GV;EKx/GM;IAOI,yBAAA;ELo/GV;EK3/GM;IAOI,0BAAA;ELu/GV;EK9/GM;IAOI,4BAAA;EL0/GV;EKjgHM;IAOI,kCAAA;EL6/GV;EKpgHM;IAOI,sCAAA;ELggHV;EKvgHM;I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","file":"bootstrap-grid.css","sourcesContent":["@mixin bsBanner($file) {\n /*!\n * Bootstrap #{$file} v5.3.3 (https://getbootstrap.com/)\n * Copyright 2011-2024 The Bootstrap Authors\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n}\n","// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n@if $enable-container-classes {\n // Single container class with breakpoint max-widths\n .container,\n // 100% wide container at all breakpoints\n .container-fluid {\n @include make-container();\n }\n\n // Responsive containers that are 100% wide until a breakpoint\n @each $breakpoint, $container-max-width in $container-max-widths {\n .container-#{$breakpoint} {\n @extend .container-fluid;\n }\n\n @include media-breakpoint-up($breakpoint, $grid-breakpoints) {\n %responsive-container-#{$breakpoint} {\n max-width: $container-max-width;\n }\n\n // Extend each breakpoint which is smaller or equal to the current breakpoint\n $extend-breakpoint: true;\n\n @each $name, $width in $grid-breakpoints {\n @if ($extend-breakpoint) {\n .container#{breakpoint-infix($name, $grid-breakpoints)} {\n @extend %responsive-container-#{$breakpoint};\n }\n\n // Once the current breakpoint is reached, stop extending\n @if ($breakpoint == $name) {\n $extend-breakpoint: false;\n }\n }\n }\n }\n }\n}\n","// Container mixins\n\n@mixin make-container($gutter: $container-padding-x) {\n --#{$prefix}gutter-x: #{$gutter};\n --#{$prefix}gutter-y: 0;\n width: 100%;\n padding-right: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list\n padding-left: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list\n margin-right: auto;\n margin-left: auto;\n}\n","/*!\n * Bootstrap Grid v5.3.3 (https://getbootstrap.com/)\n * Copyright 2011-2024 The Bootstrap Authors\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n.container,\n.container-fluid,\n.container-xxl,\n.container-xl,\n.container-lg,\n.container-md,\n.container-sm {\n --bs-gutter-x: 1.5rem;\n --bs-gutter-y: 0;\n width: 100%;\n padding-right: calc(var(--bs-gutter-x) * 0.5);\n padding-left: calc(var(--bs-gutter-x) * 0.5);\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container-sm, .container {\n max-width: 540px;\n }\n}\n@media (min-width: 768px) {\n .container-md, .container-sm, .container {\n max-width: 720px;\n }\n}\n@media (min-width: 992px) {\n .container-lg, .container-md, .container-sm, .container {\n max-width: 960px;\n }\n}\n@media (min-width: 1200px) {\n .container-xl, .container-lg, .container-md, .container-sm, .container {\n max-width: 1140px;\n }\n}\n@media (min-width: 1400px) {\n .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container {\n max-width: 1320px;\n }\n}\n:root {\n --bs-breakpoint-xs: 0;\n --bs-breakpoint-sm: 576px;\n --bs-breakpoint-md: 768px;\n --bs-breakpoint-lg: 992px;\n --bs-breakpoint-xl: 1200px;\n --bs-breakpoint-xxl: 1400px;\n}\n\n.row {\n --bs-gutter-x: 1.5rem;\n --bs-gutter-y: 0;\n display: flex;\n flex-wrap: wrap;\n margin-top: calc(-1 * var(--bs-gutter-y));\n margin-right: calc(-0.5 * var(--bs-gutter-x));\n margin-left: calc(-0.5 * var(--bs-gutter-x));\n}\n.row > * {\n box-sizing: border-box;\n flex-shrink: 0;\n width: 100%;\n max-width: 100%;\n padding-right: calc(var(--bs-gutter-x) * 0.5);\n padding-left: calc(var(--bs-gutter-x) * 0.5);\n margin-top: var(--bs-gutter-y);\n}\n\n.col {\n flex: 1 0 0%;\n}\n\n.row-cols-auto > * {\n flex: 0 0 auto;\n width: auto;\n}\n\n.row-cols-1 > * {\n flex: 0 0 auto;\n width: 100%;\n}\n\n.row-cols-2 > * {\n flex: 0 0 auto;\n width: 50%;\n}\n\n.row-cols-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n}\n\n.row-cols-4 > * {\n flex: 0 0 auto;\n width: 25%;\n}\n\n.row-cols-5 > * {\n flex: 0 0 auto;\n width: 20%;\n}\n\n.row-cols-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n}\n\n.col-auto {\n flex: 0 0 auto;\n width: auto;\n}\n\n.col-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n}\n\n.col-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n}\n\n.col-3 {\n flex: 0 0 auto;\n width: 25%;\n}\n\n.col-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n}\n\n.col-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n}\n\n.col-6 {\n flex: 0 0 auto;\n width: 50%;\n}\n\n.col-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n}\n\n.col-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n}\n\n.col-9 {\n flex: 0 0 auto;\n width: 75%;\n}\n\n.col-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n}\n\n.col-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n}\n\n.col-12 {\n flex: 0 0 auto;\n width: 100%;\n}\n\n.offset-1 {\n margin-left: 8.33333333%;\n}\n\n.offset-2 {\n margin-left: 16.66666667%;\n}\n\n.offset-3 {\n margin-left: 25%;\n}\n\n.offset-4 {\n margin-left: 33.33333333%;\n}\n\n.offset-5 {\n margin-left: 41.66666667%;\n}\n\n.offset-6 {\n margin-left: 50%;\n}\n\n.offset-7 {\n margin-left: 58.33333333%;\n}\n\n.offset-8 {\n margin-left: 66.66666667%;\n}\n\n.offset-9 {\n margin-left: 75%;\n}\n\n.offset-10 {\n margin-left: 83.33333333%;\n}\n\n.offset-11 {\n margin-left: 91.66666667%;\n}\n\n.g-0,\n.gx-0 {\n --bs-gutter-x: 0;\n}\n\n.g-0,\n.gy-0 {\n --bs-gutter-y: 0;\n}\n\n.g-1,\n.gx-1 {\n --bs-gutter-x: 0.25rem;\n}\n\n.g-1,\n.gy-1 {\n --bs-gutter-y: 0.25rem;\n}\n\n.g-2,\n.gx-2 {\n --bs-gutter-x: 0.5rem;\n}\n\n.g-2,\n.gy-2 {\n --bs-gutter-y: 0.5rem;\n}\n\n.g-3,\n.gx-3 {\n --bs-gutter-x: 1rem;\n}\n\n.g-3,\n.gy-3 {\n --bs-gutter-y: 1rem;\n}\n\n.g-4,\n.gx-4 {\n --bs-gutter-x: 1.5rem;\n}\n\n.g-4,\n.gy-4 {\n --bs-gutter-y: 1.5rem;\n}\n\n.g-5,\n.gx-5 {\n --bs-gutter-x: 3rem;\n}\n\n.g-5,\n.gy-5 {\n --bs-gutter-y: 3rem;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n flex: 1 0 0%;\n }\n .row-cols-sm-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-sm-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-sm-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-sm-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-sm-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-sm-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-sm-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-sm-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-sm-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-sm-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-sm-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-sm-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-sm-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-sm-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-sm-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-sm-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-sm-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-sm-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-sm-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-sm-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-sm-0 {\n margin-left: 0;\n }\n .offset-sm-1 {\n margin-left: 8.33333333%;\n }\n .offset-sm-2 {\n margin-left: 16.66666667%;\n }\n .offset-sm-3 {\n margin-left: 25%;\n }\n .offset-sm-4 {\n margin-left: 33.33333333%;\n }\n .offset-sm-5 {\n margin-left: 41.66666667%;\n }\n .offset-sm-6 {\n margin-left: 50%;\n }\n .offset-sm-7 {\n margin-left: 58.33333333%;\n }\n .offset-sm-8 {\n margin-left: 66.66666667%;\n }\n .offset-sm-9 {\n margin-left: 75%;\n }\n .offset-sm-10 {\n margin-left: 83.33333333%;\n }\n .offset-sm-11 {\n margin-left: 91.66666667%;\n }\n .g-sm-0,\n .gx-sm-0 {\n --bs-gutter-x: 0;\n }\n .g-sm-0,\n .gy-sm-0 {\n --bs-gutter-y: 0;\n }\n .g-sm-1,\n .gx-sm-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-sm-1,\n .gy-sm-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-sm-2,\n .gx-sm-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-sm-2,\n .gy-sm-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-sm-3,\n .gx-sm-3 {\n --bs-gutter-x: 1rem;\n }\n .g-sm-3,\n .gy-sm-3 {\n --bs-gutter-y: 1rem;\n }\n .g-sm-4,\n .gx-sm-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-sm-4,\n .gy-sm-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-sm-5,\n .gx-sm-5 {\n --bs-gutter-x: 3rem;\n }\n .g-sm-5,\n .gy-sm-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 768px) {\n .col-md {\n flex: 1 0 0%;\n }\n .row-cols-md-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-md-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-md-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-md-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-md-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-md-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-md-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-md-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-md-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-md-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-md-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-md-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-md-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-md-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-md-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-md-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-md-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-md-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-md-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-md-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-md-0 {\n margin-left: 0;\n }\n .offset-md-1 {\n margin-left: 8.33333333%;\n }\n .offset-md-2 {\n margin-left: 16.66666667%;\n }\n .offset-md-3 {\n margin-left: 25%;\n }\n .offset-md-4 {\n margin-left: 33.33333333%;\n }\n .offset-md-5 {\n margin-left: 41.66666667%;\n }\n .offset-md-6 {\n margin-left: 50%;\n }\n .offset-md-7 {\n margin-left: 58.33333333%;\n }\n .offset-md-8 {\n margin-left: 66.66666667%;\n }\n .offset-md-9 {\n margin-left: 75%;\n }\n .offset-md-10 {\n margin-left: 83.33333333%;\n }\n .offset-md-11 {\n margin-left: 91.66666667%;\n }\n .g-md-0,\n .gx-md-0 {\n --bs-gutter-x: 0;\n }\n .g-md-0,\n .gy-md-0 {\n --bs-gutter-y: 0;\n }\n .g-md-1,\n .gx-md-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-md-1,\n .gy-md-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-md-2,\n .gx-md-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-md-2,\n .gy-md-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-md-3,\n .gx-md-3 {\n --bs-gutter-x: 1rem;\n }\n .g-md-3,\n .gy-md-3 {\n --bs-gutter-y: 1rem;\n }\n .g-md-4,\n .gx-md-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-md-4,\n .gy-md-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-md-5,\n .gx-md-5 {\n --bs-gutter-x: 3rem;\n }\n .g-md-5,\n .gy-md-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 992px) {\n .col-lg {\n flex: 1 0 0%;\n }\n .row-cols-lg-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-lg-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-lg-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-lg-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-lg-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-lg-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-lg-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-lg-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-lg-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-lg-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-lg-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-lg-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-lg-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-lg-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-lg-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-lg-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-lg-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-lg-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-lg-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-lg-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-lg-0 {\n margin-left: 0;\n }\n .offset-lg-1 {\n margin-left: 8.33333333%;\n }\n .offset-lg-2 {\n margin-left: 16.66666667%;\n }\n .offset-lg-3 {\n margin-left: 25%;\n }\n .offset-lg-4 {\n margin-left: 33.33333333%;\n }\n .offset-lg-5 {\n margin-left: 41.66666667%;\n }\n .offset-lg-6 {\n margin-left: 50%;\n }\n .offset-lg-7 {\n margin-left: 58.33333333%;\n }\n .offset-lg-8 {\n margin-left: 66.66666667%;\n }\n .offset-lg-9 {\n margin-left: 75%;\n }\n .offset-lg-10 {\n margin-left: 83.33333333%;\n }\n .offset-lg-11 {\n margin-left: 91.66666667%;\n }\n .g-lg-0,\n .gx-lg-0 {\n --bs-gutter-x: 0;\n }\n .g-lg-0,\n .gy-lg-0 {\n --bs-gutter-y: 0;\n }\n .g-lg-1,\n .gx-lg-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-lg-1,\n .gy-lg-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-lg-2,\n .gx-lg-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-lg-2,\n .gy-lg-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-lg-3,\n .gx-lg-3 {\n --bs-gutter-x: 1rem;\n }\n .g-lg-3,\n .gy-lg-3 {\n --bs-gutter-y: 1rem;\n }\n .g-lg-4,\n .gx-lg-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-lg-4,\n .gy-lg-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-lg-5,\n .gx-lg-5 {\n --bs-gutter-x: 3rem;\n }\n .g-lg-5,\n .gy-lg-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 1200px) {\n .col-xl {\n flex: 1 0 0%;\n }\n .row-cols-xl-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-xl-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-xl-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-xl-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-xl-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-xl-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-xl-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xl-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-xl-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-xl-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xl-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-xl-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-xl-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-xl-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-xl-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-xl-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-xl-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-xl-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-xl-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-xl-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-xl-0 {\n margin-left: 0;\n }\n .offset-xl-1 {\n margin-left: 8.33333333%;\n }\n .offset-xl-2 {\n margin-left: 16.66666667%;\n }\n .offset-xl-3 {\n margin-left: 25%;\n }\n .offset-xl-4 {\n margin-left: 33.33333333%;\n }\n .offset-xl-5 {\n margin-left: 41.66666667%;\n }\n .offset-xl-6 {\n margin-left: 50%;\n }\n .offset-xl-7 {\n margin-left: 58.33333333%;\n }\n .offset-xl-8 {\n margin-left: 66.66666667%;\n }\n .offset-xl-9 {\n margin-left: 75%;\n }\n .offset-xl-10 {\n margin-left: 83.33333333%;\n }\n .offset-xl-11 {\n margin-left: 91.66666667%;\n }\n .g-xl-0,\n .gx-xl-0 {\n --bs-gutter-x: 0;\n }\n .g-xl-0,\n .gy-xl-0 {\n --bs-gutter-y: 0;\n }\n .g-xl-1,\n .gx-xl-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-xl-1,\n .gy-xl-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-xl-2,\n .gx-xl-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-xl-2,\n .gy-xl-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-xl-3,\n .gx-xl-3 {\n --bs-gutter-x: 1rem;\n }\n .g-xl-3,\n .gy-xl-3 {\n --bs-gutter-y: 1rem;\n }\n .g-xl-4,\n .gx-xl-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-xl-4,\n .gy-xl-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-xl-5,\n .gx-xl-5 {\n --bs-gutter-x: 3rem;\n }\n .g-xl-5,\n .gy-xl-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 1400px) {\n .col-xxl {\n flex: 1 0 0%;\n }\n .row-cols-xxl-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-xxl-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-xxl-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-xxl-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-xxl-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-xxl-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-xxl-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xxl-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-xxl-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-xxl-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xxl-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-xxl-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-xxl-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-xxl-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-xxl-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-xxl-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-xxl-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-xxl-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-xxl-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-xxl-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-xxl-0 {\n margin-left: 0;\n }\n .offset-xxl-1 {\n margin-left: 8.33333333%;\n }\n .offset-xxl-2 {\n margin-left: 16.66666667%;\n }\n .offset-xxl-3 {\n margin-left: 25%;\n }\n .offset-xxl-4 {\n margin-left: 33.33333333%;\n }\n .offset-xxl-5 {\n margin-left: 41.66666667%;\n }\n .offset-xxl-6 {\n margin-left: 50%;\n }\n .offset-xxl-7 {\n margin-left: 58.33333333%;\n }\n .offset-xxl-8 {\n margin-left: 66.66666667%;\n }\n .offset-xxl-9 {\n margin-left: 75%;\n }\n .offset-xxl-10 {\n margin-left: 83.33333333%;\n }\n .offset-xxl-11 {\n margin-left: 91.66666667%;\n }\n .g-xxl-0,\n .gx-xxl-0 {\n --bs-gutter-x: 0;\n }\n .g-xxl-0,\n .gy-xxl-0 {\n --bs-gutter-y: 0;\n }\n .g-xxl-1,\n .gx-xxl-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-xxl-1,\n .gy-xxl-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-xxl-2,\n .gx-xxl-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-xxl-2,\n .gy-xxl-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-xxl-3,\n .gx-xxl-3 {\n --bs-gutter-x: 1rem;\n }\n .g-xxl-3,\n .gy-xxl-3 {\n --bs-gutter-y: 1rem;\n }\n .g-xxl-4,\n .gx-xxl-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-xxl-4,\n .gy-xxl-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-xxl-5,\n .gx-xxl-5 {\n --bs-gutter-x: 3rem;\n }\n .g-xxl-5,\n .gy-xxl-5 {\n --bs-gutter-y: 3rem;\n }\n}\n.d-inline {\n display: inline !important;\n}\n\n.d-inline-block {\n display: inline-block !important;\n}\n\n.d-block {\n display: block !important;\n}\n\n.d-grid {\n display: grid !important;\n}\n\n.d-inline-grid {\n display: inline-grid !important;\n}\n\n.d-table {\n display: table !important;\n}\n\n.d-table-row {\n display: table-row !important;\n}\n\n.d-table-cell {\n display: table-cell !important;\n}\n\n.d-flex {\n display: flex !important;\n}\n\n.d-inline-flex {\n display: inline-flex !important;\n}\n\n.d-none {\n display: none !important;\n}\n\n.flex-fill {\n flex: 1 1 auto !important;\n}\n\n.flex-row {\n flex-direction: row !important;\n}\n\n.flex-column {\n flex-direction: column !important;\n}\n\n.flex-row-reverse {\n flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n flex-direction: column-reverse !important;\n}\n\n.flex-grow-0 {\n flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n flex-shrink: 1 !important;\n}\n\n.flex-wrap {\n flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n}\n\n.justify-content-start {\n justify-content: flex-start !important;\n}\n\n.justify-content-end {\n justify-content: flex-end !important;\n}\n\n.justify-content-center {\n justify-content: center !important;\n}\n\n.justify-content-between {\n justify-content: space-between !important;\n}\n\n.justify-content-around {\n justify-content: space-around !important;\n}\n\n.justify-content-evenly {\n justify-content: space-evenly !important;\n}\n\n.align-items-start {\n align-items: flex-start !important;\n}\n\n.align-items-end {\n align-items: flex-end !important;\n}\n\n.align-items-center {\n align-items: center !important;\n}\n\n.align-items-baseline {\n align-items: baseline !important;\n}\n\n.align-items-stretch {\n align-items: stretch !important;\n}\n\n.align-content-start {\n align-content: flex-start !important;\n}\n\n.align-content-end {\n align-content: flex-end !important;\n}\n\n.align-content-center {\n align-content: center !important;\n}\n\n.align-content-between {\n align-content: space-between !important;\n}\n\n.align-content-around {\n align-content: space-around !important;\n}\n\n.align-content-stretch {\n align-content: stretch !important;\n}\n\n.align-self-auto {\n align-self: auto !important;\n}\n\n.align-self-start {\n align-self: flex-start !important;\n}\n\n.align-self-end {\n align-self: flex-end !important;\n}\n\n.align-self-center {\n align-self: center !important;\n}\n\n.align-self-baseline {\n align-self: baseline !important;\n}\n\n.align-self-stretch {\n align-self: stretch !important;\n}\n\n.order-first {\n order: -1 !important;\n}\n\n.order-0 {\n order: 0 !important;\n}\n\n.order-1 {\n order: 1 !important;\n}\n\n.order-2 {\n order: 2 !important;\n}\n\n.order-3 {\n order: 3 !important;\n}\n\n.order-4 {\n order: 4 !important;\n}\n\n.order-5 {\n order: 5 !important;\n}\n\n.order-last {\n order: 6 !important;\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mx-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n}\n\n.mx-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n}\n\n.mx-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n}\n\n.mx-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n}\n\n.mx-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n}\n\n.mx-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n}\n\n.mx-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n}\n\n.my-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n.my-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n}\n\n.my-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n}\n\n.my-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n}\n\n.my-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n}\n\n.my-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n}\n\n.my-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n}\n\n.mt-0 {\n margin-top: 0 !important;\n}\n\n.mt-1 {\n margin-top: 0.25rem !important;\n}\n\n.mt-2 {\n margin-top: 0.5rem !important;\n}\n\n.mt-3 {\n margin-top: 1rem !important;\n}\n\n.mt-4 {\n margin-top: 1.5rem !important;\n}\n\n.mt-5 {\n margin-top: 3rem !important;\n}\n\n.mt-auto {\n margin-top: auto !important;\n}\n\n.me-0 {\n margin-right: 0 !important;\n}\n\n.me-1 {\n margin-right: 0.25rem !important;\n}\n\n.me-2 {\n margin-right: 0.5rem !important;\n}\n\n.me-3 {\n margin-right: 1rem !important;\n}\n\n.me-4 {\n margin-right: 1.5rem !important;\n}\n\n.me-5 {\n margin-right: 3rem !important;\n}\n\n.me-auto {\n margin-right: auto !important;\n}\n\n.mb-0 {\n margin-bottom: 0 !important;\n}\n\n.mb-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.mb-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.mb-3 {\n margin-bottom: 1rem !important;\n}\n\n.mb-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.mb-5 {\n margin-bottom: 3rem !important;\n}\n\n.mb-auto {\n margin-bottom: auto !important;\n}\n\n.ms-0 {\n margin-left: 0 !important;\n}\n\n.ms-1 {\n margin-left: 0.25rem !important;\n}\n\n.ms-2 {\n margin-left: 0.5rem !important;\n}\n\n.ms-3 {\n margin-left: 1rem !important;\n}\n\n.ms-4 {\n margin-left: 1.5rem !important;\n}\n\n.ms-5 {\n margin-left: 3rem !important;\n}\n\n.ms-auto {\n margin-left: auto !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.px-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n}\n\n.px-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n}\n\n.px-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n}\n\n.px-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n}\n\n.px-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n}\n\n.px-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n}\n\n.py-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n}\n\n.py-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n}\n\n.py-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n}\n\n.py-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n}\n\n.py-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n}\n\n.py-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n}\n\n.pt-0 {\n padding-top: 0 !important;\n}\n\n.pt-1 {\n padding-top: 0.25rem !important;\n}\n\n.pt-2 {\n padding-top: 0.5rem !important;\n}\n\n.pt-3 {\n padding-top: 1rem !important;\n}\n\n.pt-4 {\n padding-top: 1.5rem !important;\n}\n\n.pt-5 {\n padding-top: 3rem !important;\n}\n\n.pe-0 {\n padding-right: 0 !important;\n}\n\n.pe-1 {\n padding-right: 0.25rem !important;\n}\n\n.pe-2 {\n padding-right: 0.5rem !important;\n}\n\n.pe-3 {\n padding-right: 1rem !important;\n}\n\n.pe-4 {\n padding-right: 1.5rem !important;\n}\n\n.pe-5 {\n padding-right: 3rem !important;\n}\n\n.pb-0 {\n padding-bottom: 0 !important;\n}\n\n.pb-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pb-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pb-3 {\n padding-bottom: 1rem !important;\n}\n\n.pb-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pb-5 {\n padding-bottom: 3rem !important;\n}\n\n.ps-0 {\n padding-left: 0 !important;\n}\n\n.ps-1 {\n padding-left: 0.25rem !important;\n}\n\n.ps-2 {\n padding-left: 0.5rem !important;\n}\n\n.ps-3 {\n padding-left: 1rem !important;\n}\n\n.ps-4 {\n padding-left: 1.5rem !important;\n}\n\n.ps-5 {\n padding-left: 3rem !important;\n}\n\n@media (min-width: 576px) {\n .d-sm-inline {\n display: inline !important;\n }\n .d-sm-inline-block {\n display: inline-block !important;\n }\n .d-sm-block {\n display: block !important;\n }\n .d-sm-grid {\n display: grid !important;\n }\n .d-sm-inline-grid {\n display: inline-grid !important;\n }\n .d-sm-table {\n display: table !important;\n }\n .d-sm-table-row {\n display: table-row !important;\n }\n .d-sm-table-cell {\n display: table-cell !important;\n }\n .d-sm-flex {\n display: flex !important;\n }\n .d-sm-inline-flex {\n display: inline-flex !important;\n }\n .d-sm-none {\n display: none !important;\n }\n .flex-sm-fill {\n flex: 1 1 auto !important;\n }\n .flex-sm-row {\n flex-direction: row !important;\n }\n .flex-sm-column {\n flex-direction: column !important;\n }\n .flex-sm-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-sm-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-sm-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-sm-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-sm-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-sm-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-sm-wrap {\n flex-wrap: wrap !important;\n }\n .flex-sm-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-sm-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-sm-start {\n justify-content: flex-start !important;\n }\n .justify-content-sm-end {\n justify-content: flex-end !important;\n }\n .justify-content-sm-center {\n justify-content: center !important;\n }\n .justify-content-sm-between {\n justify-content: space-between !important;\n }\n .justify-content-sm-around {\n justify-content: space-around !important;\n }\n .justify-content-sm-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-sm-start {\n align-items: flex-start !important;\n }\n .align-items-sm-end {\n align-items: flex-end !important;\n }\n .align-items-sm-center {\n align-items: center !important;\n }\n .align-items-sm-baseline {\n align-items: baseline !important;\n }\n .align-items-sm-stretch {\n align-items: stretch !important;\n }\n .align-content-sm-start {\n align-content: flex-start !important;\n }\n .align-content-sm-end {\n align-content: flex-end !important;\n }\n .align-content-sm-center {\n align-content: center !important;\n }\n .align-content-sm-between {\n align-content: space-between !important;\n }\n .align-content-sm-around {\n align-content: space-around !important;\n }\n .align-content-sm-stretch {\n align-content: stretch !important;\n }\n .align-self-sm-auto {\n align-self: auto !important;\n }\n .align-self-sm-start {\n align-self: flex-start !important;\n }\n .align-self-sm-end {\n align-self: flex-end !important;\n }\n .align-self-sm-center {\n align-self: center !important;\n }\n .align-self-sm-baseline {\n align-self: baseline !important;\n }\n .align-self-sm-stretch {\n align-self: stretch !important;\n }\n .order-sm-first {\n order: -1 !important;\n }\n .order-sm-0 {\n order: 0 !important;\n }\n .order-sm-1 {\n order: 1 !important;\n }\n .order-sm-2 {\n order: 2 !important;\n }\n .order-sm-3 {\n order: 3 !important;\n }\n .order-sm-4 {\n order: 4 !important;\n }\n .order-sm-5 {\n order: 5 !important;\n }\n .order-sm-last {\n order: 6 !important;\n }\n .m-sm-0 {\n margin: 0 !important;\n }\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n .m-sm-3 {\n margin: 1rem !important;\n }\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n .m-sm-5 {\n margin: 3rem !important;\n }\n .m-sm-auto {\n margin: auto !important;\n }\n .mx-sm-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-sm-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-sm-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-sm-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-sm-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-sm-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-sm-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-sm-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-sm-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-sm-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-sm-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-sm-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-sm-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-sm-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-sm-0 {\n margin-top: 0 !important;\n }\n .mt-sm-1 {\n margin-top: 0.25rem !important;\n }\n .mt-sm-2 {\n margin-top: 0.5rem !important;\n }\n .mt-sm-3 {\n margin-top: 1rem !important;\n }\n .mt-sm-4 {\n margin-top: 1.5rem !important;\n }\n .mt-sm-5 {\n margin-top: 3rem !important;\n }\n .mt-sm-auto {\n margin-top: auto !important;\n }\n .me-sm-0 {\n margin-right: 0 !important;\n }\n .me-sm-1 {\n margin-right: 0.25rem !important;\n }\n .me-sm-2 {\n margin-right: 0.5rem !important;\n }\n .me-sm-3 {\n margin-right: 1rem !important;\n }\n .me-sm-4 {\n margin-right: 1.5rem !important;\n }\n .me-sm-5 {\n margin-right: 3rem !important;\n }\n .me-sm-auto {\n margin-right: auto !important;\n }\n .mb-sm-0 {\n margin-bottom: 0 !important;\n }\n .mb-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-sm-3 {\n margin-bottom: 1rem !important;\n }\n .mb-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-sm-5 {\n margin-bottom: 3rem !important;\n }\n .mb-sm-auto {\n margin-bottom: auto !important;\n }\n .ms-sm-0 {\n margin-left: 0 !important;\n }\n .ms-sm-1 {\n margin-left: 0.25rem !important;\n }\n .ms-sm-2 {\n margin-left: 0.5rem !important;\n }\n .ms-sm-3 {\n margin-left: 1rem !important;\n }\n .ms-sm-4 {\n margin-left: 1.5rem !important;\n }\n .ms-sm-5 {\n margin-left: 3rem !important;\n }\n .ms-sm-auto {\n margin-left: auto !important;\n }\n .p-sm-0 {\n padding: 0 !important;\n }\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n .p-sm-3 {\n padding: 1rem !important;\n }\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n .p-sm-5 {\n padding: 3rem !important;\n }\n .px-sm-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-sm-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-sm-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-sm-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-sm-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-sm-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-sm-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-sm-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-sm-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-sm-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-sm-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-sm-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-sm-0 {\n padding-top: 0 !important;\n }\n .pt-sm-1 {\n padding-top: 0.25rem !important;\n }\n .pt-sm-2 {\n padding-top: 0.5rem !important;\n }\n .pt-sm-3 {\n padding-top: 1rem !important;\n }\n .pt-sm-4 {\n padding-top: 1.5rem !important;\n }\n .pt-sm-5 {\n padding-top: 3rem !important;\n }\n .pe-sm-0 {\n padding-right: 0 !important;\n }\n .pe-sm-1 {\n padding-right: 0.25rem !important;\n }\n .pe-sm-2 {\n padding-right: 0.5rem !important;\n }\n .pe-sm-3 {\n padding-right: 1rem !important;\n }\n .pe-sm-4 {\n padding-right: 1.5rem !important;\n }\n .pe-sm-5 {\n padding-right: 3rem !important;\n }\n .pb-sm-0 {\n padding-bottom: 0 !important;\n }\n .pb-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-sm-3 {\n padding-bottom: 1rem !important;\n }\n .pb-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-sm-5 {\n padding-bottom: 3rem !important;\n }\n .ps-sm-0 {\n padding-left: 0 !important;\n }\n .ps-sm-1 {\n padding-left: 0.25rem !important;\n }\n .ps-sm-2 {\n padding-left: 0.5rem !important;\n }\n .ps-sm-3 {\n padding-left: 1rem !important;\n }\n .ps-sm-4 {\n padding-left: 1.5rem !important;\n }\n .ps-sm-5 {\n padding-left: 3rem !important;\n }\n}\n@media (min-width: 768px) {\n .d-md-inline {\n display: inline !important;\n }\n .d-md-inline-block {\n display: inline-block !important;\n }\n .d-md-block {\n display: block !important;\n }\n .d-md-grid {\n display: grid !important;\n }\n .d-md-inline-grid {\n display: inline-grid !important;\n }\n .d-md-table {\n display: table !important;\n }\n .d-md-table-row {\n display: table-row !important;\n }\n .d-md-table-cell {\n display: table-cell !important;\n }\n .d-md-flex {\n display: flex !important;\n }\n .d-md-inline-flex {\n display: inline-flex !important;\n }\n .d-md-none {\n display: none !important;\n }\n .flex-md-fill {\n flex: 1 1 auto !important;\n }\n .flex-md-row {\n flex-direction: row !important;\n }\n .flex-md-column {\n flex-direction: column !important;\n }\n .flex-md-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-md-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-md-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-md-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-md-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-md-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-md-wrap {\n flex-wrap: wrap !important;\n }\n .flex-md-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-md-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-md-start {\n justify-content: flex-start !important;\n }\n .justify-content-md-end {\n justify-content: flex-end !important;\n }\n .justify-content-md-center {\n justify-content: center !important;\n }\n .justify-content-md-between {\n justify-content: space-between !important;\n }\n .justify-content-md-around {\n justify-content: space-around !important;\n }\n .justify-content-md-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-md-start {\n align-items: flex-start !important;\n }\n .align-items-md-end {\n align-items: flex-end !important;\n }\n .align-items-md-center {\n align-items: center !important;\n }\n .align-items-md-baseline {\n align-items: baseline !important;\n }\n .align-items-md-stretch {\n align-items: stretch !important;\n }\n .align-content-md-start {\n align-content: flex-start !important;\n }\n .align-content-md-end {\n align-content: flex-end !important;\n }\n .align-content-md-center {\n align-content: center !important;\n }\n .align-content-md-between {\n align-content: space-between !important;\n }\n .align-content-md-around {\n align-content: space-around !important;\n }\n .align-content-md-stretch {\n align-content: stretch !important;\n }\n .align-self-md-auto {\n align-self: auto !important;\n }\n .align-self-md-start {\n align-self: flex-start !important;\n }\n .align-self-md-end {\n align-self: flex-end !important;\n }\n .align-self-md-center {\n align-self: center !important;\n }\n .align-self-md-baseline {\n align-self: baseline !important;\n }\n .align-self-md-stretch {\n align-self: stretch !important;\n }\n .order-md-first {\n order: -1 !important;\n }\n .order-md-0 {\n order: 0 !important;\n }\n .order-md-1 {\n order: 1 !important;\n }\n .order-md-2 {\n order: 2 !important;\n }\n .order-md-3 {\n order: 3 !important;\n }\n .order-md-4 {\n order: 4 !important;\n }\n .order-md-5 {\n order: 5 !important;\n }\n .order-md-last {\n order: 6 !important;\n }\n .m-md-0 {\n margin: 0 !important;\n }\n .m-md-1 {\n margin: 0.25rem !important;\n }\n .m-md-2 {\n margin: 0.5rem !important;\n }\n .m-md-3 {\n margin: 1rem !important;\n }\n .m-md-4 {\n margin: 1.5rem !important;\n }\n .m-md-5 {\n margin: 3rem !important;\n }\n .m-md-auto {\n margin: auto !important;\n }\n .mx-md-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-md-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-md-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-md-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-md-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-md-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-md-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-md-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-md-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-md-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-md-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-md-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-md-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-md-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-md-0 {\n margin-top: 0 !important;\n }\n .mt-md-1 {\n margin-top: 0.25rem !important;\n }\n .mt-md-2 {\n margin-top: 0.5rem !important;\n }\n .mt-md-3 {\n margin-top: 1rem !important;\n }\n .mt-md-4 {\n margin-top: 1.5rem !important;\n }\n .mt-md-5 {\n margin-top: 3rem !important;\n }\n .mt-md-auto {\n margin-top: auto !important;\n }\n .me-md-0 {\n margin-right: 0 !important;\n }\n .me-md-1 {\n margin-right: 0.25rem !important;\n }\n .me-md-2 {\n margin-right: 0.5rem !important;\n }\n .me-md-3 {\n margin-right: 1rem !important;\n }\n .me-md-4 {\n margin-right: 1.5rem !important;\n }\n .me-md-5 {\n margin-right: 3rem !important;\n }\n .me-md-auto {\n margin-right: auto !important;\n }\n .mb-md-0 {\n margin-bottom: 0 !important;\n }\n .mb-md-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-md-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-md-3 {\n margin-bottom: 1rem !important;\n }\n .mb-md-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-md-5 {\n margin-bottom: 3rem !important;\n }\n .mb-md-auto {\n margin-bottom: auto !important;\n }\n .ms-md-0 {\n margin-left: 0 !important;\n }\n .ms-md-1 {\n margin-left: 0.25rem !important;\n }\n .ms-md-2 {\n margin-left: 0.5rem !important;\n }\n .ms-md-3 {\n margin-left: 1rem !important;\n }\n .ms-md-4 {\n margin-left: 1.5rem !important;\n }\n .ms-md-5 {\n margin-left: 3rem !important;\n }\n .ms-md-auto {\n margin-left: auto !important;\n }\n .p-md-0 {\n padding: 0 !important;\n }\n .p-md-1 {\n padding: 0.25rem !important;\n }\n .p-md-2 {\n padding: 0.5rem !important;\n }\n .p-md-3 {\n padding: 1rem !important;\n }\n .p-md-4 {\n padding: 1.5rem !important;\n }\n .p-md-5 {\n padding: 3rem !important;\n }\n .px-md-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-md-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-md-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-md-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-md-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-md-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-md-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-md-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-md-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-md-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-md-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-md-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-md-0 {\n padding-top: 0 !important;\n }\n .pt-md-1 {\n padding-top: 0.25rem !important;\n }\n .pt-md-2 {\n padding-top: 0.5rem !important;\n }\n .pt-md-3 {\n padding-top: 1rem !important;\n }\n .pt-md-4 {\n padding-top: 1.5rem !important;\n }\n .pt-md-5 {\n padding-top: 3rem !important;\n }\n .pe-md-0 {\n padding-right: 0 !important;\n }\n .pe-md-1 {\n padding-right: 0.25rem !important;\n }\n .pe-md-2 {\n padding-right: 0.5rem !important;\n }\n .pe-md-3 {\n padding-right: 1rem !important;\n }\n .pe-md-4 {\n padding-right: 1.5rem !important;\n }\n .pe-md-5 {\n padding-right: 3rem !important;\n }\n .pb-md-0 {\n padding-bottom: 0 !important;\n }\n .pb-md-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-md-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-md-3 {\n padding-bottom: 1rem !important;\n }\n .pb-md-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-md-5 {\n padding-bottom: 3rem !important;\n }\n .ps-md-0 {\n padding-left: 0 !important;\n }\n .ps-md-1 {\n padding-left: 0.25rem !important;\n }\n .ps-md-2 {\n padding-left: 0.5rem !important;\n }\n .ps-md-3 {\n padding-left: 1rem !important;\n }\n .ps-md-4 {\n padding-left: 1.5rem !important;\n }\n .ps-md-5 {\n padding-left: 3rem !important;\n }\n}\n@media (min-width: 992px) {\n .d-lg-inline {\n display: inline !important;\n }\n .d-lg-inline-block {\n display: inline-block !important;\n }\n .d-lg-block {\n display: block !important;\n }\n .d-lg-grid {\n display: grid !important;\n }\n .d-lg-inline-grid {\n display: inline-grid !important;\n }\n .d-lg-table {\n display: table !important;\n }\n .d-lg-table-row {\n display: table-row !important;\n }\n .d-lg-table-cell {\n display: table-cell !important;\n }\n .d-lg-flex {\n display: flex !important;\n }\n .d-lg-inline-flex {\n display: inline-flex !important;\n }\n .d-lg-none {\n display: none !important;\n }\n .flex-lg-fill {\n flex: 1 1 auto !important;\n }\n .flex-lg-row {\n flex-direction: row !important;\n }\n .flex-lg-column {\n flex-direction: column !important;\n }\n .flex-lg-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-lg-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-lg-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-lg-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-lg-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-lg-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-lg-wrap {\n flex-wrap: wrap !important;\n }\n .flex-lg-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-lg-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-lg-start {\n justify-content: flex-start !important;\n }\n .justify-content-lg-end {\n justify-content: flex-end !important;\n }\n .justify-content-lg-center {\n justify-content: center !important;\n }\n .justify-content-lg-between {\n justify-content: space-between !important;\n }\n .justify-content-lg-around {\n justify-content: space-around !important;\n }\n .justify-content-lg-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-lg-start {\n align-items: flex-start !important;\n }\n .align-items-lg-end {\n align-items: flex-end !important;\n }\n .align-items-lg-center {\n align-items: center !important;\n }\n .align-items-lg-baseline {\n align-items: baseline !important;\n }\n .align-items-lg-stretch {\n align-items: stretch !important;\n }\n .align-content-lg-start {\n align-content: flex-start !important;\n }\n .align-content-lg-end {\n align-content: flex-end !important;\n }\n .align-content-lg-center {\n align-content: center !important;\n }\n .align-content-lg-between {\n align-content: space-between !important;\n }\n .align-content-lg-around {\n align-content: space-around !important;\n }\n .align-content-lg-stretch {\n align-content: stretch !important;\n }\n .align-self-lg-auto {\n align-self: auto !important;\n }\n .align-self-lg-start {\n align-self: flex-start !important;\n }\n .align-self-lg-end {\n align-self: flex-end !important;\n }\n .align-self-lg-center {\n align-self: center !important;\n }\n .align-self-lg-baseline {\n align-self: baseline !important;\n }\n .align-self-lg-stretch {\n align-self: stretch !important;\n }\n .order-lg-first {\n order: -1 !important;\n }\n .order-lg-0 {\n order: 0 !important;\n }\n .order-lg-1 {\n order: 1 !important;\n }\n .order-lg-2 {\n order: 2 !important;\n }\n .order-lg-3 {\n order: 3 !important;\n }\n .order-lg-4 {\n order: 4 !important;\n }\n .order-lg-5 {\n order: 5 !important;\n }\n .order-lg-last {\n order: 6 !important;\n }\n .m-lg-0 {\n margin: 0 !important;\n }\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n .m-lg-3 {\n margin: 1rem !important;\n }\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n .m-lg-5 {\n margin: 3rem !important;\n }\n .m-lg-auto {\n margin: auto !important;\n }\n .mx-lg-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-lg-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-lg-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-lg-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-lg-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-lg-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-lg-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-lg-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-lg-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-lg-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-lg-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-lg-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-lg-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-lg-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-lg-0 {\n margin-top: 0 !important;\n }\n .mt-lg-1 {\n margin-top: 0.25rem !important;\n }\n .mt-lg-2 {\n margin-top: 0.5rem !important;\n }\n .mt-lg-3 {\n margin-top: 1rem !important;\n }\n .mt-lg-4 {\n margin-top: 1.5rem !important;\n }\n .mt-lg-5 {\n margin-top: 3rem !important;\n }\n .mt-lg-auto {\n margin-top: auto !important;\n }\n .me-lg-0 {\n margin-right: 0 !important;\n }\n .me-lg-1 {\n margin-right: 0.25rem !important;\n }\n .me-lg-2 {\n margin-right: 0.5rem !important;\n }\n .me-lg-3 {\n margin-right: 1rem !important;\n }\n .me-lg-4 {\n margin-right: 1.5rem !important;\n }\n .me-lg-5 {\n margin-right: 3rem !important;\n }\n .me-lg-auto {\n margin-right: auto !important;\n }\n .mb-lg-0 {\n margin-bottom: 0 !important;\n }\n .mb-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-lg-3 {\n margin-bottom: 1rem !important;\n }\n .mb-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-lg-5 {\n margin-bottom: 3rem !important;\n }\n .mb-lg-auto {\n margin-bottom: auto !important;\n }\n .ms-lg-0 {\n margin-left: 0 !important;\n }\n .ms-lg-1 {\n margin-left: 0.25rem !important;\n }\n .ms-lg-2 {\n margin-left: 0.5rem !important;\n }\n .ms-lg-3 {\n margin-left: 1rem !important;\n }\n .ms-lg-4 {\n margin-left: 1.5rem !important;\n }\n .ms-lg-5 {\n margin-left: 3rem !important;\n }\n .ms-lg-auto {\n margin-left: auto !important;\n }\n .p-lg-0 {\n padding: 0 !important;\n }\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n .p-lg-3 {\n padding: 1rem !important;\n }\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n .p-lg-5 {\n padding: 3rem !important;\n }\n .px-lg-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-lg-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-lg-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-lg-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-lg-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-lg-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-lg-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-lg-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-lg-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-lg-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-lg-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-lg-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-lg-0 {\n padding-top: 0 !important;\n }\n .pt-lg-1 {\n padding-top: 0.25rem !important;\n }\n .pt-lg-2 {\n padding-top: 0.5rem !important;\n }\n .pt-lg-3 {\n padding-top: 1rem !important;\n }\n .pt-lg-4 {\n padding-top: 1.5rem !important;\n }\n .pt-lg-5 {\n padding-top: 3rem !important;\n }\n .pe-lg-0 {\n padding-right: 0 !important;\n }\n .pe-lg-1 {\n padding-right: 0.25rem !important;\n }\n .pe-lg-2 {\n padding-right: 0.5rem !important;\n }\n .pe-lg-3 {\n padding-right: 1rem !important;\n }\n .pe-lg-4 {\n padding-right: 1.5rem !important;\n }\n .pe-lg-5 {\n padding-right: 3rem !important;\n }\n .pb-lg-0 {\n padding-bottom: 0 !important;\n }\n .pb-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-lg-3 {\n padding-bottom: 1rem !important;\n }\n .pb-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-lg-5 {\n padding-bottom: 3rem !important;\n }\n .ps-lg-0 {\n padding-left: 0 !important;\n }\n .ps-lg-1 {\n padding-left: 0.25rem !important;\n }\n .ps-lg-2 {\n padding-left: 0.5rem !important;\n }\n .ps-lg-3 {\n padding-left: 1rem !important;\n }\n .ps-lg-4 {\n padding-left: 1.5rem !important;\n }\n .ps-lg-5 {\n padding-left: 3rem !important;\n }\n}\n@media (min-width: 1200px) {\n .d-xl-inline {\n display: inline !important;\n }\n .d-xl-inline-block {\n display: inline-block !important;\n }\n .d-xl-block {\n display: block !important;\n }\n .d-xl-grid {\n display: grid !important;\n }\n .d-xl-inline-grid {\n display: inline-grid !important;\n }\n .d-xl-table {\n display: table !important;\n }\n .d-xl-table-row {\n display: table-row !important;\n }\n .d-xl-table-cell {\n display: table-cell !important;\n }\n .d-xl-flex {\n display: flex !important;\n }\n .d-xl-inline-flex {\n display: inline-flex !important;\n }\n .d-xl-none {\n display: none !important;\n }\n .flex-xl-fill {\n flex: 1 1 auto !important;\n }\n .flex-xl-row {\n flex-direction: row !important;\n }\n .flex-xl-column {\n flex-direction: column !important;\n }\n .flex-xl-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-xl-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-xl-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-xl-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-xl-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-xl-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-xl-wrap {\n flex-wrap: wrap !important;\n }\n .flex-xl-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-xl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-xl-start {\n justify-content: flex-start !important;\n }\n .justify-content-xl-end {\n justify-content: flex-end !important;\n }\n .justify-content-xl-center {\n justify-content: center !important;\n }\n .justify-content-xl-between {\n justify-content: space-between !important;\n }\n .justify-content-xl-around {\n justify-content: space-around !important;\n }\n .justify-content-xl-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-xl-start {\n align-items: flex-start !important;\n }\n .align-items-xl-end {\n align-items: flex-end !important;\n }\n .align-items-xl-center {\n align-items: center !important;\n }\n .align-items-xl-baseline {\n align-items: baseline !important;\n }\n .align-items-xl-stretch {\n align-items: stretch !important;\n }\n .align-content-xl-start {\n align-content: flex-start !important;\n }\n .align-content-xl-end {\n align-content: flex-end !important;\n }\n .align-content-xl-center {\n align-content: center !important;\n }\n .align-content-xl-between {\n align-content: space-between !important;\n }\n .align-content-xl-around {\n align-content: space-around !important;\n }\n .align-content-xl-stretch {\n align-content: stretch !important;\n }\n .align-self-xl-auto {\n align-self: auto !important;\n }\n .align-self-xl-start {\n align-self: flex-start !important;\n }\n .align-self-xl-end {\n align-self: flex-end !important;\n }\n .align-self-xl-center {\n align-self: center !important;\n }\n .align-self-xl-baseline {\n align-self: baseline !important;\n }\n .align-self-xl-stretch {\n align-self: stretch !important;\n }\n .order-xl-first {\n order: -1 !important;\n }\n .order-xl-0 {\n order: 0 !important;\n }\n .order-xl-1 {\n order: 1 !important;\n }\n .order-xl-2 {\n order: 2 !important;\n }\n .order-xl-3 {\n order: 3 !important;\n }\n .order-xl-4 {\n order: 4 !important;\n }\n .order-xl-5 {\n order: 5 !important;\n }\n .order-xl-last {\n order: 6 !important;\n }\n .m-xl-0 {\n margin: 0 !important;\n }\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n .m-xl-3 {\n margin: 1rem !important;\n }\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n .m-xl-5 {\n margin: 3rem !important;\n }\n .m-xl-auto {\n margin: auto !important;\n }\n .mx-xl-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-xl-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-xl-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-xl-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-xl-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-xl-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-xl-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-xl-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-xl-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-xl-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-xl-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-xl-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-xl-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-xl-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-xl-0 {\n margin-top: 0 !important;\n }\n .mt-xl-1 {\n margin-top: 0.25rem !important;\n }\n .mt-xl-2 {\n margin-top: 0.5rem !important;\n }\n .mt-xl-3 {\n margin-top: 1rem !important;\n }\n .mt-xl-4 {\n margin-top: 1.5rem !important;\n }\n .mt-xl-5 {\n margin-top: 3rem !important;\n }\n .mt-xl-auto {\n margin-top: auto !important;\n }\n .me-xl-0 {\n margin-right: 0 !important;\n }\n .me-xl-1 {\n margin-right: 0.25rem !important;\n }\n .me-xl-2 {\n margin-right: 0.5rem !important;\n }\n .me-xl-3 {\n margin-right: 1rem !important;\n }\n .me-xl-4 {\n margin-right: 1.5rem !important;\n }\n .me-xl-5 {\n margin-right: 3rem !important;\n }\n .me-xl-auto {\n margin-right: auto !important;\n }\n .mb-xl-0 {\n margin-bottom: 0 !important;\n }\n .mb-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-xl-3 {\n margin-bottom: 1rem !important;\n }\n .mb-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-xl-5 {\n margin-bottom: 3rem !important;\n }\n .mb-xl-auto {\n margin-bottom: auto !important;\n }\n .ms-xl-0 {\n margin-left: 0 !important;\n }\n .ms-xl-1 {\n margin-left: 0.25rem !important;\n }\n .ms-xl-2 {\n margin-left: 0.5rem !important;\n }\n .ms-xl-3 {\n margin-left: 1rem !important;\n }\n .ms-xl-4 {\n margin-left: 1.5rem !important;\n }\n .ms-xl-5 {\n margin-left: 3rem !important;\n }\n .ms-xl-auto {\n margin-left: auto !important;\n }\n .p-xl-0 {\n padding: 0 !important;\n }\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n .p-xl-3 {\n padding: 1rem !important;\n }\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n .p-xl-5 {\n padding: 3rem !important;\n }\n .px-xl-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-xl-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-xl-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-xl-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-xl-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-xl-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-xl-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-xl-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-xl-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-xl-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-xl-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-xl-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-xl-0 {\n padding-top: 0 !important;\n }\n .pt-xl-1 {\n padding-top: 0.25rem !important;\n }\n .pt-xl-2 {\n padding-top: 0.5rem !important;\n }\n .pt-xl-3 {\n padding-top: 1rem !important;\n }\n .pt-xl-4 {\n padding-top: 1.5rem !important;\n }\n .pt-xl-5 {\n padding-top: 3rem !important;\n }\n .pe-xl-0 {\n padding-right: 0 !important;\n }\n .pe-xl-1 {\n padding-right: 0.25rem !important;\n }\n .pe-xl-2 {\n padding-right: 0.5rem !important;\n }\n .pe-xl-3 {\n padding-right: 1rem !important;\n }\n .pe-xl-4 {\n padding-right: 1.5rem !important;\n }\n .pe-xl-5 {\n padding-right: 3rem !important;\n }\n .pb-xl-0 {\n padding-bottom: 0 !important;\n }\n .pb-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-xl-3 {\n padding-bottom: 1rem !important;\n }\n .pb-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-xl-5 {\n padding-bottom: 3rem !important;\n }\n .ps-xl-0 {\n padding-left: 0 !important;\n }\n .ps-xl-1 {\n padding-left: 0.25rem !important;\n }\n .ps-xl-2 {\n padding-left: 0.5rem !important;\n }\n .ps-xl-3 {\n padding-left: 1rem !important;\n }\n .ps-xl-4 {\n padding-left: 1.5rem !important;\n }\n .ps-xl-5 {\n padding-left: 3rem !important;\n }\n}\n@media (min-width: 1400px) {\n .d-xxl-inline {\n display: inline !important;\n }\n .d-xxl-inline-block {\n display: inline-block !important;\n }\n .d-xxl-block {\n display: block !important;\n }\n .d-xxl-grid {\n display: grid !important;\n }\n .d-xxl-inline-grid {\n display: inline-grid !important;\n }\n .d-xxl-table {\n display: table !important;\n }\n .d-xxl-table-row {\n display: table-row !important;\n }\n .d-xxl-table-cell {\n display: table-cell !important;\n }\n .d-xxl-flex {\n display: flex !important;\n }\n .d-xxl-inline-flex {\n display: inline-flex !important;\n }\n .d-xxl-none {\n display: none !important;\n }\n .flex-xxl-fill {\n flex: 1 1 auto !important;\n }\n .flex-xxl-row {\n flex-direction: row !important;\n }\n .flex-xxl-column {\n flex-direction: column !important;\n }\n .flex-xxl-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-xxl-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-xxl-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-xxl-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-xxl-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-xxl-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-xxl-wrap {\n flex-wrap: wrap !important;\n }\n .flex-xxl-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-xxl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-xxl-start {\n justify-content: flex-start !important;\n }\n .justify-content-xxl-end {\n justify-content: flex-end !important;\n }\n .justify-content-xxl-center {\n justify-content: center !important;\n }\n .justify-content-xxl-between {\n justify-content: space-between !important;\n }\n .justify-content-xxl-around {\n justify-content: space-around !important;\n }\n .justify-content-xxl-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-xxl-start {\n align-items: flex-start !important;\n }\n .align-items-xxl-end {\n align-items: flex-end !important;\n }\n .align-items-xxl-center {\n align-items: center !important;\n }\n .align-items-xxl-baseline {\n align-items: baseline !important;\n }\n .align-items-xxl-stretch {\n align-items: stretch !important;\n }\n .align-content-xxl-start {\n align-content: flex-start !important;\n }\n .align-content-xxl-end {\n align-content: flex-end !important;\n }\n .align-content-xxl-center {\n align-content: center !important;\n }\n .align-content-xxl-between {\n align-content: space-between !important;\n }\n .align-content-xxl-around {\n align-content: space-around !important;\n }\n .align-content-xxl-stretch {\n align-content: stretch !important;\n }\n .align-self-xxl-auto {\n align-self: auto !important;\n }\n .align-self-xxl-start {\n align-self: flex-start !important;\n }\n .align-self-xxl-end {\n align-self: flex-end !important;\n }\n .align-self-xxl-center {\n align-self: center !important;\n }\n .align-self-xxl-baseline {\n align-self: baseline !important;\n }\n .align-self-xxl-stretch {\n align-self: stretch !important;\n }\n .order-xxl-first {\n order: -1 !important;\n }\n .order-xxl-0 {\n order: 0 !important;\n }\n .order-xxl-1 {\n order: 1 !important;\n }\n .order-xxl-2 {\n order: 2 !important;\n }\n .order-xxl-3 {\n order: 3 !important;\n }\n .order-xxl-4 {\n order: 4 !important;\n }\n .order-xxl-5 {\n order: 5 !important;\n }\n .order-xxl-last {\n order: 6 !important;\n }\n .m-xxl-0 {\n margin: 0 !important;\n }\n .m-xxl-1 {\n margin: 0.25rem !important;\n }\n .m-xxl-2 {\n margin: 0.5rem !important;\n }\n .m-xxl-3 {\n margin: 1rem !important;\n }\n .m-xxl-4 {\n margin: 1.5rem !important;\n }\n .m-xxl-5 {\n margin: 3rem !important;\n }\n .m-xxl-auto {\n margin: auto !important;\n }\n .mx-xxl-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-xxl-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-xxl-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-xxl-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-xxl-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-xxl-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-xxl-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-xxl-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-xxl-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-xxl-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-xxl-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-xxl-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-xxl-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-xxl-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-xxl-0 {\n margin-top: 0 !important;\n }\n .mt-xxl-1 {\n margin-top: 0.25rem !important;\n }\n .mt-xxl-2 {\n margin-top: 0.5rem !important;\n }\n .mt-xxl-3 {\n margin-top: 1rem !important;\n }\n .mt-xxl-4 {\n margin-top: 1.5rem !important;\n }\n .mt-xxl-5 {\n margin-top: 3rem !important;\n }\n .mt-xxl-auto {\n margin-top: auto !important;\n }\n .me-xxl-0 {\n margin-right: 0 !important;\n }\n .me-xxl-1 {\n margin-right: 0.25rem !important;\n }\n .me-xxl-2 {\n margin-right: 0.5rem !important;\n }\n .me-xxl-3 {\n margin-right: 1rem !important;\n }\n .me-xxl-4 {\n margin-right: 1.5rem !important;\n }\n .me-xxl-5 {\n margin-right: 3rem !important;\n }\n .me-xxl-auto {\n margin-right: auto !important;\n }\n .mb-xxl-0 {\n margin-bottom: 0 !important;\n }\n .mb-xxl-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-xxl-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-xxl-3 {\n margin-bottom: 1rem !important;\n }\n .mb-xxl-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-xxl-5 {\n margin-bottom: 3rem !important;\n }\n .mb-xxl-auto {\n margin-bottom: auto !important;\n }\n .ms-xxl-0 {\n margin-left: 0 !important;\n }\n .ms-xxl-1 {\n margin-left: 0.25rem !important;\n }\n .ms-xxl-2 {\n margin-left: 0.5rem !important;\n }\n .ms-xxl-3 {\n margin-left: 1rem !important;\n }\n .ms-xxl-4 {\n margin-left: 1.5rem !important;\n }\n .ms-xxl-5 {\n margin-left: 3rem !important;\n }\n .ms-xxl-auto {\n margin-left: auto !important;\n }\n .p-xxl-0 {\n padding: 0 !important;\n }\n .p-xxl-1 {\n padding: 0.25rem !important;\n }\n .p-xxl-2 {\n padding: 0.5rem !important;\n }\n .p-xxl-3 {\n padding: 1rem !important;\n }\n .p-xxl-4 {\n padding: 1.5rem !important;\n }\n .p-xxl-5 {\n padding: 3rem !important;\n }\n .px-xxl-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-xxl-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-xxl-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-xxl-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-xxl-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-xxl-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-xxl-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-xxl-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-xxl-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-xxl-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-xxl-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-xxl-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-xxl-0 {\n padding-top: 0 !important;\n }\n .pt-xxl-1 {\n padding-top: 0.25rem !important;\n }\n .pt-xxl-2 {\n padding-top: 0.5rem !important;\n }\n .pt-xxl-3 {\n padding-top: 1rem !important;\n }\n .pt-xxl-4 {\n padding-top: 1.5rem !important;\n }\n .pt-xxl-5 {\n padding-top: 3rem !important;\n }\n .pe-xxl-0 {\n padding-right: 0 !important;\n }\n .pe-xxl-1 {\n padding-right: 0.25rem !important;\n }\n .pe-xxl-2 {\n padding-right: 0.5rem !important;\n }\n .pe-xxl-3 {\n padding-right: 1rem !important;\n }\n .pe-xxl-4 {\n padding-right: 1.5rem !important;\n }\n .pe-xxl-5 {\n padding-right: 3rem !important;\n }\n .pb-xxl-0 {\n padding-bottom: 0 !important;\n }\n .pb-xxl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-xxl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-xxl-3 {\n padding-bottom: 1rem !important;\n }\n .pb-xxl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-xxl-5 {\n padding-bottom: 3rem !important;\n }\n .ps-xxl-0 {\n padding-left: 0 !important;\n }\n .ps-xxl-1 {\n padding-left: 0.25rem !important;\n }\n .ps-xxl-2 {\n padding-left: 0.5rem !important;\n }\n .ps-xxl-3 {\n padding-left: 1rem !important;\n }\n .ps-xxl-4 {\n padding-left: 1.5rem !important;\n }\n .ps-xxl-5 {\n padding-left: 3rem !important;\n }\n}\n@media print {\n .d-print-inline {\n display: inline !important;\n }\n .d-print-inline-block {\n display: inline-block !important;\n }\n .d-print-block {\n display: block !important;\n }\n .d-print-grid {\n display: grid !important;\n }\n .d-print-inline-grid {\n display: inline-grid !important;\n }\n .d-print-table {\n display: table !important;\n }\n .d-print-table-row {\n display: table-row !important;\n }\n .d-print-table-cell {\n display: table-cell !important;\n }\n .d-print-flex {\n display: flex !important;\n }\n .d-print-inline-flex {\n display: inline-flex !important;\n }\n .d-print-none {\n display: none !important;\n }\n}\n\n/*# sourceMappingURL=bootstrap-grid.css.map */\n","// Breakpoint viewport sizes and media queries.\n//\n// Breakpoints are defined as a map of (name: minimum width), order from small to large:\n//\n// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)\n//\n// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.\n\n// Name of the next breakpoint, or null for the last breakpoint.\n//\n// >> breakpoint-next(sm)\n// md\n// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// md\n// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl xxl))\n// md\n@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {\n $n: index($breakpoint-names, $name);\n @if not $n {\n @error \"breakpoint `#{$name}` not found in `#{$breakpoints}`\";\n }\n @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);\n}\n\n// Minimum breakpoint width. Null for the smallest (first) breakpoint.\n//\n// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// 576px\n@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {\n $min: map-get($breakpoints, $name);\n @return if($min != 0, $min, null);\n}\n\n// Maximum breakpoint width.\n// The maximum value is reduced by 0.02px to work around the limitations of\n// `min-` and `max-` prefixes and viewports with fractional widths.\n// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max\n// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.\n// See https://bugs.webkit.org/show_bug.cgi?id=178261\n//\n// >> breakpoint-max(md, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// 767.98px\n@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {\n $max: map-get($breakpoints, $name);\n @return if($max and $max > 0, $max - .02, null);\n}\n\n// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front.\n// Useful for making responsive utilities.\n//\n// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// \"\" (Returns a blank string)\n// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// \"-sm\"\n@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {\n @return if(breakpoint-min($name, $breakpoints) == null, \"\", \"-#{$name}\");\n}\n\n// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.\n// Makes the @content apply to the given breakpoint and wider.\n@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n @if $min {\n @media (min-width: $min) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media of at most the maximum breakpoint width. No query for the largest breakpoint.\n// Makes the @content apply to the given breakpoint and narrower.\n@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {\n $max: breakpoint-max($name, $breakpoints);\n @if $max {\n @media (max-width: $max) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media that spans multiple breakpoint widths.\n// Makes the @content apply between the min and max breakpoints\n@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($lower, $breakpoints);\n $max: breakpoint-max($upper, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($lower, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($upper, $breakpoints) {\n @content;\n }\n }\n}\n\n// Media between the breakpoint's minimum and maximum widths.\n// No minimum for the smallest breakpoint, and no maximum for the largest one.\n// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.\n@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n $next: breakpoint-next($name, $breakpoints);\n $max: breakpoint-max($next, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($name, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($next, $breakpoints) {\n @content;\n }\n }\n}\n","// Variables\n//\n// Variables should follow the `$component-state-property-size` formula for\n// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.\n\n// Color system\n\n// scss-docs-start gray-color-variables\n$white: #fff !default;\n$gray-100: #f8f9fa !default;\n$gray-200: #e9ecef !default;\n$gray-300: #dee2e6 !default;\n$gray-400: #ced4da !default;\n$gray-500: #adb5bd !default;\n$gray-600: #6c757d !default;\n$gray-700: #495057 !default;\n$gray-800: #343a40 !default;\n$gray-900: #212529 !default;\n$black: #000 !default;\n// scss-docs-end gray-color-variables\n\n// fusv-disable\n// scss-docs-start gray-colors-map\n$grays: (\n \"100\": $gray-100,\n \"200\": $gray-200,\n \"300\": $gray-300,\n \"400\": $gray-400,\n \"500\": $gray-500,\n \"600\": $gray-600,\n \"700\": $gray-700,\n \"800\": $gray-800,\n \"900\": $gray-900\n) !default;\n// scss-docs-end gray-colors-map\n// fusv-enable\n\n// scss-docs-start color-variables\n$blue: #0d6efd !default;\n$indigo: #6610f2 !default;\n$purple: #6f42c1 !default;\n$pink: #d63384 !default;\n$red: #dc3545 !default;\n$orange: #fd7e14 !default;\n$yellow: #ffc107 !default;\n$green: #198754 !default;\n$teal: #20c997 !default;\n$cyan: #0dcaf0 !default;\n// scss-docs-end color-variables\n\n// scss-docs-start colors-map\n$colors: (\n \"blue\": $blue,\n \"indigo\": $indigo,\n \"purple\": $purple,\n \"pink\": $pink,\n \"red\": $red,\n \"orange\": $orange,\n \"yellow\": $yellow,\n \"green\": $green,\n \"teal\": $teal,\n \"cyan\": $cyan,\n \"black\": $black,\n \"white\": $white,\n \"gray\": $gray-600,\n \"gray-dark\": $gray-800\n) !default;\n// scss-docs-end colors-map\n\n// The contrast ratio to reach against white, to determine if color changes from \"light\" to \"dark\". Acceptable values for WCAG 2.0 are 3, 4.5 and 7.\n// See https://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast\n$min-contrast-ratio: 4.5 !default;\n\n// Customize the light and dark text colors for use in our color contrast function.\n$color-contrast-dark: $black !default;\n$color-contrast-light: $white !default;\n\n// fusv-disable\n$blue-100: tint-color($blue, 80%) !default;\n$blue-200: tint-color($blue, 60%) !default;\n$blue-300: tint-color($blue, 40%) !default;\n$blue-400: tint-color($blue, 20%) !default;\n$blue-500: $blue !default;\n$blue-600: shade-color($blue, 20%) !default;\n$blue-700: shade-color($blue, 40%) !default;\n$blue-800: shade-color($blue, 60%) !default;\n$blue-900: shade-color($blue, 80%) !default;\n\n$indigo-100: tint-color($indigo, 80%) !default;\n$indigo-200: tint-color($indigo, 60%) !default;\n$indigo-300: tint-color($indigo, 40%) !default;\n$indigo-400: tint-color($indigo, 20%) !default;\n$indigo-500: $indigo !default;\n$indigo-600: shade-color($indigo, 20%) !default;\n$indigo-700: shade-color($indigo, 40%) !default;\n$indigo-800: shade-color($indigo, 60%) !default;\n$indigo-900: shade-color($indigo, 80%) !default;\n\n$purple-100: tint-color($purple, 80%) !default;\n$purple-200: tint-color($purple, 60%) !default;\n$purple-300: tint-color($purple, 40%) !default;\n$purple-400: tint-color($purple, 20%) !default;\n$purple-500: $purple !default;\n$purple-600: shade-color($purple, 20%) !default;\n$purple-700: shade-color($purple, 40%) !default;\n$purple-800: shade-color($purple, 60%) !default;\n$purple-900: shade-color($purple, 80%) !default;\n\n$pink-100: tint-color($pink, 80%) !default;\n$pink-200: tint-color($pink, 60%) !default;\n$pink-300: tint-color($pink, 40%) !default;\n$pink-400: tint-color($pink, 20%) !default;\n$pink-500: $pink !default;\n$pink-600: shade-color($pink, 20%) !default;\n$pink-700: shade-color($pink, 40%) !default;\n$pink-800: shade-color($pink, 60%) !default;\n$pink-900: shade-color($pink, 80%) !default;\n\n$red-100: tint-color($red, 80%) !default;\n$red-200: tint-color($red, 60%) !default;\n$red-300: tint-color($red, 40%) !default;\n$red-400: tint-color($red, 20%) !default;\n$red-500: $red !default;\n$red-600: shade-color($red, 20%) !default;\n$red-700: shade-color($red, 40%) !default;\n$red-800: shade-color($red, 60%) !default;\n$red-900: shade-color($red, 80%) !default;\n\n$orange-100: tint-color($orange, 80%) !default;\n$orange-200: tint-color($orange, 60%) !default;\n$orange-300: tint-color($orange, 40%) !default;\n$orange-400: tint-color($orange, 20%) !default;\n$orange-500: $orange !default;\n$orange-600: shade-color($orange, 20%) !default;\n$orange-700: shade-color($orange, 40%) !default;\n$orange-800: shade-color($orange, 60%) !default;\n$orange-900: shade-color($orange, 80%) !default;\n\n$yellow-100: tint-color($yellow, 80%) !default;\n$yellow-200: tint-color($yellow, 60%) !default;\n$yellow-300: tint-color($yellow, 40%) !default;\n$yellow-400: tint-color($yellow, 20%) !default;\n$yellow-500: $yellow !default;\n$yellow-600: shade-color($yellow, 20%) !default;\n$yellow-700: shade-color($yellow, 40%) !default;\n$yellow-800: shade-color($yellow, 60%) !default;\n$yellow-900: shade-color($yellow, 80%) !default;\n\n$green-100: tint-color($green, 80%) !default;\n$green-200: tint-color($green, 60%) !default;\n$green-300: tint-color($green, 40%) !default;\n$green-400: tint-color($green, 20%) !default;\n$green-500: $green !default;\n$green-600: shade-color($green, 20%) !default;\n$green-700: shade-color($green, 40%) !default;\n$green-800: shade-color($green, 60%) !default;\n$green-900: shade-color($green, 80%) !default;\n\n$teal-100: tint-color($teal, 80%) !default;\n$teal-200: tint-color($teal, 60%) !default;\n$teal-300: tint-color($teal, 40%) !default;\n$teal-400: tint-color($teal, 20%) !default;\n$teal-500: $teal !default;\n$teal-600: shade-color($teal, 20%) !default;\n$teal-700: shade-color($teal, 40%) !default;\n$teal-800: shade-color($teal, 60%) !default;\n$teal-900: shade-color($teal, 80%) !default;\n\n$cyan-100: tint-color($cyan, 80%) !default;\n$cyan-200: tint-color($cyan, 60%) !default;\n$cyan-300: tint-color($cyan, 40%) !default;\n$cyan-400: tint-color($cyan, 20%) !default;\n$cyan-500: $cyan !default;\n$cyan-600: shade-color($cyan, 20%) !default;\n$cyan-700: shade-color($cyan, 40%) !default;\n$cyan-800: shade-color($cyan, 60%) !default;\n$cyan-900: shade-color($cyan, 80%) !default;\n\n$blues: (\n \"blue-100\": $blue-100,\n \"blue-200\": $blue-200,\n \"blue-300\": $blue-300,\n \"blue-400\": $blue-400,\n \"blue-500\": $blue-500,\n \"blue-600\": $blue-600,\n \"blue-700\": $blue-700,\n \"blue-800\": $blue-800,\n \"blue-900\": $blue-900\n) !default;\n\n$indigos: (\n \"indigo-100\": $indigo-100,\n \"indigo-200\": $indigo-200,\n \"indigo-300\": $indigo-300,\n \"indigo-400\": $indigo-400,\n \"indigo-500\": $indigo-500,\n \"indigo-600\": $indigo-600,\n \"indigo-700\": $indigo-700,\n \"indigo-800\": $indigo-800,\n \"indigo-900\": $indigo-900\n) !default;\n\n$purples: (\n \"purple-100\": $purple-100,\n \"purple-200\": $purple-200,\n \"purple-300\": $purple-300,\n \"purple-400\": $purple-400,\n \"purple-500\": $purple-500,\n \"purple-600\": $purple-600,\n \"purple-700\": $purple-700,\n \"purple-800\": $purple-800,\n \"purple-900\": $purple-900\n) !default;\n\n$pinks: (\n \"pink-100\": $pink-100,\n \"pink-200\": $pink-200,\n \"pink-300\": $pink-300,\n \"pink-400\": $pink-400,\n \"pink-500\": $pink-500,\n \"pink-600\": $pink-600,\n \"pink-700\": $pink-700,\n \"pink-800\": $pink-800,\n \"pink-900\": $pink-900\n) !default;\n\n$reds: (\n \"red-100\": $red-100,\n \"red-200\": $red-200,\n \"red-300\": $red-300,\n \"red-400\": $red-400,\n \"red-500\": $red-500,\n \"red-600\": $red-600,\n \"red-700\": $red-700,\n \"red-800\": $red-800,\n \"red-900\": $red-900\n) !default;\n\n$oranges: (\n \"orange-100\": $orange-100,\n \"orange-200\": $orange-200,\n \"orange-300\": $orange-300,\n \"orange-400\": $orange-400,\n \"orange-500\": $orange-500,\n \"orange-600\": $orange-600,\n \"orange-700\": $orange-700,\n \"orange-800\": $orange-800,\n \"orange-900\": $orange-900\n) !default;\n\n$yellows: (\n \"yellow-100\": $yellow-100,\n \"yellow-200\": $yellow-200,\n \"yellow-300\": $yellow-300,\n \"yellow-400\": $yellow-400,\n \"yellow-500\": $yellow-500,\n \"yellow-600\": $yellow-600,\n \"yellow-700\": $yellow-700,\n \"yellow-800\": $yellow-800,\n \"yellow-900\": $yellow-900\n) !default;\n\n$greens: (\n \"green-100\": $green-100,\n \"green-200\": $green-200,\n \"green-300\": $green-300,\n \"green-400\": $green-400,\n \"green-500\": $green-500,\n \"green-600\": $green-600,\n \"green-700\": $green-700,\n \"green-800\": $green-800,\n \"green-900\": $green-900\n) !default;\n\n$teals: (\n \"teal-100\": $teal-100,\n \"teal-200\": $teal-200,\n \"teal-300\": $teal-300,\n \"teal-400\": $teal-400,\n \"teal-500\": $teal-500,\n \"teal-600\": $teal-600,\n \"teal-700\": $teal-700,\n \"teal-800\": $teal-800,\n \"teal-900\": $teal-900\n) !default;\n\n$cyans: (\n \"cyan-100\": $cyan-100,\n \"cyan-200\": $cyan-200,\n \"cyan-300\": $cyan-300,\n \"cyan-400\": $cyan-400,\n \"cyan-500\": $cyan-500,\n \"cyan-600\": $cyan-600,\n \"cyan-700\": $cyan-700,\n \"cyan-800\": $cyan-800,\n \"cyan-900\": $cyan-900\n) !default;\n// fusv-enable\n\n// scss-docs-start theme-color-variables\n$primary: $blue !default;\n$secondary: $gray-600 !default;\n$success: $green !default;\n$info: $cyan !default;\n$warning: $yellow !default;\n$danger: $red !default;\n$light: $gray-100 !default;\n$dark: $gray-900 !default;\n// scss-docs-end theme-color-variables\n\n// scss-docs-start theme-colors-map\n$theme-colors: (\n \"primary\": $primary,\n \"secondary\": $secondary,\n \"success\": $success,\n \"info\": $info,\n \"warning\": $warning,\n \"danger\": $danger,\n \"light\": $light,\n \"dark\": $dark\n) !default;\n// scss-docs-end theme-colors-map\n\n// scss-docs-start theme-text-variables\n$primary-text-emphasis: shade-color($primary, 60%) !default;\n$secondary-text-emphasis: shade-color($secondary, 60%) !default;\n$success-text-emphasis: shade-color($success, 60%) !default;\n$info-text-emphasis: shade-color($info, 60%) !default;\n$warning-text-emphasis: shade-color($warning, 60%) !default;\n$danger-text-emphasis: shade-color($danger, 60%) !default;\n$light-text-emphasis: $gray-700 !default;\n$dark-text-emphasis: $gray-700 !default;\n// scss-docs-end theme-text-variables\n\n// scss-docs-start theme-bg-subtle-variables\n$primary-bg-subtle: tint-color($primary, 80%) !default;\n$secondary-bg-subtle: tint-color($secondary, 80%) !default;\n$success-bg-subtle: tint-color($success, 80%) !default;\n$info-bg-subtle: tint-color($info, 80%) !default;\n$warning-bg-subtle: tint-color($warning, 80%) !default;\n$danger-bg-subtle: tint-color($danger, 80%) !default;\n$light-bg-subtle: mix($gray-100, $white) !default;\n$dark-bg-subtle: $gray-400 !default;\n// scss-docs-end theme-bg-subtle-variables\n\n// scss-docs-start theme-border-subtle-variables\n$primary-border-subtle: tint-color($primary, 60%) !default;\n$secondary-border-subtle: tint-color($secondary, 60%) !default;\n$success-border-subtle: tint-color($success, 60%) !default;\n$info-border-subtle: tint-color($info, 60%) !default;\n$warning-border-subtle: tint-color($warning, 60%) !default;\n$danger-border-subtle: tint-color($danger, 60%) !default;\n$light-border-subtle: $gray-200 !default;\n$dark-border-subtle: $gray-500 !default;\n// scss-docs-end theme-border-subtle-variables\n\n// Characters which are escaped by the escape-svg function\n$escaped-characters: (\n (\"<\", \"%3c\"),\n (\">\", \"%3e\"),\n (\"#\", \"%23\"),\n (\"(\", \"%28\"),\n (\")\", \"%29\"),\n) !default;\n\n// Options\n//\n// Quickly modify global styling by enabling or disabling optional features.\n\n$enable-caret: true !default;\n$enable-rounded: true !default;\n$enable-shadows: false !default;\n$enable-gradients: false !default;\n$enable-transitions: true !default;\n$enable-reduced-motion: true !default;\n$enable-smooth-scroll: true !default;\n$enable-grid-classes: true !default;\n$enable-container-classes: true !default;\n$enable-cssgrid: false !default;\n$enable-button-pointers: true !default;\n$enable-rfs: true !default;\n$enable-validation-icons: true !default;\n$enable-negative-margins: false !default;\n$enable-deprecation-messages: true !default;\n$enable-important-utilities: true !default;\n\n$enable-dark-mode: true !default;\n$color-mode-type: data !default; // `data` or `media-query`\n\n// Prefix for :root CSS variables\n\n$variable-prefix: bs- !default; // Deprecated in v5.2.0 for the shorter `$prefix`\n$prefix: $variable-prefix !default;\n\n// Gradient\n//\n// The gradient which is added to components if `$enable-gradients` is `true`\n// This gradient is also added to elements with `.bg-gradient`\n// scss-docs-start variable-gradient\n$gradient: linear-gradient(180deg, rgba($white, .15), rgba($white, 0)) !default;\n// scss-docs-end variable-gradient\n\n// Spacing\n//\n// Control the default styling of most Bootstrap elements by modifying these\n// variables. Mostly focused on spacing.\n// You can add more entries to the $spacers map, should you need more variation.\n\n// scss-docs-start spacer-variables-maps\n$spacer: 1rem !default;\n$spacers: (\n 0: 0,\n 1: $spacer * .25,\n 2: $spacer * .5,\n 3: $spacer,\n 4: $spacer * 1.5,\n 5: $spacer * 3,\n) !default;\n// scss-docs-end spacer-variables-maps\n\n// Position\n//\n// Define the edge positioning anchors of the position utilities.\n\n// scss-docs-start position-map\n$position-values: (\n 0: 0,\n 50: 50%,\n 100: 100%\n) !default;\n// scss-docs-end position-map\n\n// Body\n//\n// Settings for the `` element.\n\n$body-text-align: null !default;\n$body-color: $gray-900 !default;\n$body-bg: $white !default;\n\n$body-secondary-color: rgba($body-color, .75) !default;\n$body-secondary-bg: $gray-200 !default;\n\n$body-tertiary-color: rgba($body-color, .5) !default;\n$body-tertiary-bg: $gray-100 !default;\n\n$body-emphasis-color: $black !default;\n\n// Links\n//\n// Style anchor elements.\n\n$link-color: $primary !default;\n$link-decoration: underline !default;\n$link-shade-percentage: 20% !default;\n$link-hover-color: shift-color($link-color, $link-shade-percentage) !default;\n$link-hover-decoration: null !default;\n\n$stretched-link-pseudo-element: after !default;\n$stretched-link-z-index: 1 !default;\n\n// Icon links\n// scss-docs-start icon-link-variables\n$icon-link-gap: .375rem !default;\n$icon-link-underline-offset: .25em !default;\n$icon-link-icon-size: 1em !default;\n$icon-link-icon-transition: .2s ease-in-out transform !default;\n$icon-link-icon-transform: translate3d(.25em, 0, 0) !default;\n// scss-docs-end icon-link-variables\n\n// Paragraphs\n//\n// Style p element.\n\n$paragraph-margin-bottom: 1rem !default;\n\n\n// Grid breakpoints\n//\n// Define the minimum dimensions at which your layout will change,\n// adapting to different screen sizes, for use in media queries.\n\n// scss-docs-start grid-breakpoints\n$grid-breakpoints: (\n xs: 0,\n sm: 576px,\n md: 768px,\n lg: 992px,\n xl: 1200px,\n xxl: 1400px\n) !default;\n// scss-docs-end grid-breakpoints\n\n@include _assert-ascending($grid-breakpoints, \"$grid-breakpoints\");\n@include _assert-starts-at-zero($grid-breakpoints, \"$grid-breakpoints\");\n\n\n// Grid containers\n//\n// Define the maximum width of `.container` for different screen sizes.\n\n// scss-docs-start container-max-widths\n$container-max-widths: (\n sm: 540px,\n md: 720px,\n lg: 960px,\n xl: 1140px,\n xxl: 1320px\n) !default;\n// scss-docs-end container-max-widths\n\n@include _assert-ascending($container-max-widths, \"$container-max-widths\");\n\n\n// Grid columns\n//\n// Set the number of columns and specify the width of the gutters.\n\n$grid-columns: 12 !default;\n$grid-gutter-width: 1.5rem !default;\n$grid-row-columns: 6 !default;\n\n// Container padding\n\n$container-padding-x: $grid-gutter-width !default;\n\n\n// Components\n//\n// Define common padding and border radius sizes and more.\n\n// scss-docs-start border-variables\n$border-width: 1px !default;\n$border-widths: (\n 1: 1px,\n 2: 2px,\n 3: 3px,\n 4: 4px,\n 5: 5px\n) !default;\n$border-style: solid !default;\n$border-color: $gray-300 !default;\n$border-color-translucent: rgba($black, .175) !default;\n// scss-docs-end border-variables\n\n// scss-docs-start border-radius-variables\n$border-radius: .375rem !default;\n$border-radius-sm: .25rem !default;\n$border-radius-lg: .5rem !default;\n$border-radius-xl: 1rem !default;\n$border-radius-xxl: 2rem !default;\n$border-radius-pill: 50rem !default;\n// scss-docs-end border-radius-variables\n// fusv-disable\n$border-radius-2xl: $border-radius-xxl !default; // Deprecated in v5.3.0\n// fusv-enable\n\n// scss-docs-start box-shadow-variables\n$box-shadow: 0 .5rem 1rem rgba($black, .15) !default;\n$box-shadow-sm: 0 .125rem .25rem rgba($black, .075) !default;\n$box-shadow-lg: 0 1rem 3rem rgba($black, .175) !default;\n$box-shadow-inset: inset 0 1px 2px rgba($black, .075) !default;\n// scss-docs-end box-shadow-variables\n\n$component-active-color: $white !default;\n$component-active-bg: $primary !default;\n\n// scss-docs-start focus-ring-variables\n$focus-ring-width: .25rem !default;\n$focus-ring-opacity: .25 !default;\n$focus-ring-color: rgba($primary, $focus-ring-opacity) !default;\n$focus-ring-blur: 0 !default;\n$focus-ring-box-shadow: 0 0 $focus-ring-blur $focus-ring-width $focus-ring-color !default;\n// scss-docs-end focus-ring-variables\n\n// scss-docs-start caret-variables\n$caret-width: .3em !default;\n$caret-vertical-align: $caret-width * .85 !default;\n$caret-spacing: $caret-width * .85 !default;\n// scss-docs-end caret-variables\n\n$transition-base: all .2s ease-in-out !default;\n$transition-fade: opacity .15s linear !default;\n// scss-docs-start collapse-transition\n$transition-collapse: height .35s ease !default;\n$transition-collapse-width: width .35s ease !default;\n// scss-docs-end collapse-transition\n\n// stylelint-disable function-disallowed-list\n// scss-docs-start aspect-ratios\n$aspect-ratios: (\n \"1x1\": 100%,\n \"4x3\": calc(3 / 4 * 100%),\n \"16x9\": calc(9 / 16 * 100%),\n \"21x9\": calc(9 / 21 * 100%)\n) !default;\n// scss-docs-end aspect-ratios\n// stylelint-enable function-disallowed-list\n\n// Typography\n//\n// Font, line-height, and color for body text, headings, and more.\n\n// scss-docs-start font-variables\n// stylelint-disable value-keyword-case\n$font-family-sans-serif: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", \"Liberation Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\" !default;\n$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace !default;\n// stylelint-enable value-keyword-case\n$font-family-base: var(--#{$prefix}font-sans-serif) !default;\n$font-family-code: var(--#{$prefix}font-monospace) !default;\n\n// $font-size-root affects the value of `rem`, which is used for as well font sizes, paddings, and margins\n// $font-size-base affects the font size of the body text\n$font-size-root: null !default;\n$font-size-base: 1rem !default; // Assumes the browser default, typically `16px`\n$font-size-sm: $font-size-base * .875 !default;\n$font-size-lg: $font-size-base * 1.25 !default;\n\n$font-weight-lighter: lighter !default;\n$font-weight-light: 300 !default;\n$font-weight-normal: 400 !default;\n$font-weight-medium: 500 !default;\n$font-weight-semibold: 600 !default;\n$font-weight-bold: 700 !default;\n$font-weight-bolder: bolder !default;\n\n$font-weight-base: $font-weight-normal !default;\n\n$line-height-base: 1.5 !default;\n$line-height-sm: 1.25 !default;\n$line-height-lg: 2 !default;\n\n$h1-font-size: $font-size-base * 2.5 !default;\n$h2-font-size: $font-size-base * 2 !default;\n$h3-font-size: $font-size-base * 1.75 !default;\n$h4-font-size: $font-size-base * 1.5 !default;\n$h5-font-size: $font-size-base * 1.25 !default;\n$h6-font-size: $font-size-base !default;\n// scss-docs-end font-variables\n\n// scss-docs-start font-sizes\n$font-sizes: (\n 1: $h1-font-size,\n 2: $h2-font-size,\n 3: $h3-font-size,\n 4: $h4-font-size,\n 5: $h5-font-size,\n 6: $h6-font-size\n) !default;\n// scss-docs-end font-sizes\n\n// scss-docs-start headings-variables\n$headings-margin-bottom: $spacer * .5 !default;\n$headings-font-family: null !default;\n$headings-font-style: null !default;\n$headings-font-weight: 500 !default;\n$headings-line-height: 1.2 !default;\n$headings-color: inherit !default;\n// scss-docs-end headings-variables\n\n// scss-docs-start display-headings\n$display-font-sizes: (\n 1: 5rem,\n 2: 4.5rem,\n 3: 4rem,\n 4: 3.5rem,\n 5: 3rem,\n 6: 2.5rem\n) !default;\n\n$display-font-family: null !default;\n$display-font-style: null !default;\n$display-font-weight: 300 !default;\n$display-line-height: $headings-line-height !default;\n// scss-docs-end display-headings\n\n// scss-docs-start type-variables\n$lead-font-size: $font-size-base * 1.25 !default;\n$lead-font-weight: 300 !default;\n\n$small-font-size: .875em !default;\n\n$sub-sup-font-size: .75em !default;\n\n// fusv-disable\n$text-muted: var(--#{$prefix}secondary-color) !default; // Deprecated in 5.3.0\n// fusv-enable\n\n$initialism-font-size: $small-font-size !default;\n\n$blockquote-margin-y: $spacer !default;\n$blockquote-font-size: $font-size-base * 1.25 !default;\n$blockquote-footer-color: $gray-600 !default;\n$blockquote-footer-font-size: $small-font-size !default;\n\n$hr-margin-y: $spacer !default;\n$hr-color: inherit !default;\n\n// fusv-disable\n$hr-bg-color: null !default; // Deprecated in v5.2.0\n$hr-height: null !default; // Deprecated in v5.2.0\n// fusv-enable\n\n$hr-border-color: null !default; // Allows for inherited colors\n$hr-border-width: var(--#{$prefix}border-width) !default;\n$hr-opacity: .25 !default;\n\n// scss-docs-start vr-variables\n$vr-border-width: var(--#{$prefix}border-width) !default;\n// scss-docs-end vr-variables\n\n$legend-margin-bottom: .5rem !default;\n$legend-font-size: 1.5rem !default;\n$legend-font-weight: null !default;\n\n$dt-font-weight: $font-weight-bold !default;\n\n$list-inline-padding: .5rem !default;\n\n$mark-padding: .1875em !default;\n$mark-color: $body-color !default;\n$mark-bg: $yellow-100 !default;\n// scss-docs-end type-variables\n\n\n// Tables\n//\n// Customizes the `.table` component with basic values, each used across all table variations.\n\n// scss-docs-start table-variables\n$table-cell-padding-y: .5rem !default;\n$table-cell-padding-x: .5rem !default;\n$table-cell-padding-y-sm: .25rem !default;\n$table-cell-padding-x-sm: .25rem !default;\n\n$table-cell-vertical-align: top !default;\n\n$table-color: var(--#{$prefix}emphasis-color) !default;\n$table-bg: var(--#{$prefix}body-bg) !default;\n$table-accent-bg: transparent !default;\n\n$table-th-font-weight: null !default;\n\n$table-striped-color: $table-color !default;\n$table-striped-bg-factor: .05 !default;\n$table-striped-bg: rgba(var(--#{$prefix}emphasis-color-rgb), $table-striped-bg-factor) !default;\n\n$table-active-color: $table-color !default;\n$table-active-bg-factor: .1 !default;\n$table-active-bg: rgba(var(--#{$prefix}emphasis-color-rgb), $table-active-bg-factor) !default;\n\n$table-hover-color: $table-color !default;\n$table-hover-bg-factor: .075 !default;\n$table-hover-bg: rgba(var(--#{$prefix}emphasis-color-rgb), $table-hover-bg-factor) !default;\n\n$table-border-factor: .2 !default;\n$table-border-width: var(--#{$prefix}border-width) !default;\n$table-border-color: var(--#{$prefix}border-color) !default;\n\n$table-striped-order: odd !default;\n$table-striped-columns-order: even !default;\n\n$table-group-separator-color: currentcolor !default;\n\n$table-caption-color: var(--#{$prefix}secondary-color) !default;\n\n$table-bg-scale: -80% !default;\n// scss-docs-end table-variables\n\n// scss-docs-start table-loop\n$table-variants: (\n \"primary\": shift-color($primary, $table-bg-scale),\n \"secondary\": shift-color($secondary, $table-bg-scale),\n \"success\": shift-color($success, $table-bg-scale),\n \"info\": shift-color($info, $table-bg-scale),\n \"warning\": shift-color($warning, $table-bg-scale),\n \"danger\": shift-color($danger, $table-bg-scale),\n \"light\": $light,\n \"dark\": $dark,\n) !default;\n// scss-docs-end table-loop\n\n\n// Buttons + Forms\n//\n// Shared variables that are reassigned to `$input-` and `$btn-` specific variables.\n\n// scss-docs-start input-btn-variables\n$input-btn-padding-y: .375rem !default;\n$input-btn-padding-x: .75rem !default;\n$input-btn-font-family: null !default;\n$input-btn-font-size: $font-size-base !default;\n$input-btn-line-height: $line-height-base !default;\n\n$input-btn-focus-width: $focus-ring-width !default;\n$input-btn-focus-color-opacity: $focus-ring-opacity !default;\n$input-btn-focus-color: $focus-ring-color !default;\n$input-btn-focus-blur: $focus-ring-blur !default;\n$input-btn-focus-box-shadow: $focus-ring-box-shadow !default;\n\n$input-btn-padding-y-sm: .25rem !default;\n$input-btn-padding-x-sm: .5rem !default;\n$input-btn-font-size-sm: $font-size-sm !default;\n\n$input-btn-padding-y-lg: .5rem !default;\n$input-btn-padding-x-lg: 1rem !default;\n$input-btn-font-size-lg: $font-size-lg !default;\n\n$input-btn-border-width: var(--#{$prefix}border-width) !default;\n// scss-docs-end input-btn-variables\n\n\n// Buttons\n//\n// For each of Bootstrap's buttons, define text, background, and border color.\n\n// scss-docs-start btn-variables\n$btn-color: var(--#{$prefix}body-color) !default;\n$btn-padding-y: $input-btn-padding-y !default;\n$btn-padding-x: $input-btn-padding-x !default;\n$btn-font-family: $input-btn-font-family !default;\n$btn-font-size: $input-btn-font-size !default;\n$btn-line-height: $input-btn-line-height !default;\n$btn-white-space: null !default; // Set to `nowrap` to prevent text wrapping\n\n$btn-padding-y-sm: $input-btn-padding-y-sm !default;\n$btn-padding-x-sm: $input-btn-padding-x-sm !default;\n$btn-font-size-sm: $input-btn-font-size-sm !default;\n\n$btn-padding-y-lg: $input-btn-padding-y-lg !default;\n$btn-padding-x-lg: $input-btn-padding-x-lg !default;\n$btn-font-size-lg: $input-btn-font-size-lg !default;\n\n$btn-border-width: $input-btn-border-width !default;\n\n$btn-font-weight: $font-weight-normal !default;\n$btn-box-shadow: inset 0 1px 0 rgba($white, .15), 0 1px 1px rgba($black, .075) !default;\n$btn-focus-width: $input-btn-focus-width !default;\n$btn-focus-box-shadow: $input-btn-focus-box-shadow !default;\n$btn-disabled-opacity: .65 !default;\n$btn-active-box-shadow: inset 0 3px 5px rgba($black, .125) !default;\n\n$btn-link-color: var(--#{$prefix}link-color) !default;\n$btn-link-hover-color: var(--#{$prefix}link-hover-color) !default;\n$btn-link-disabled-color: $gray-600 !default;\n$btn-link-focus-shadow-rgb: to-rgb(mix(color-contrast($link-color), $link-color, 15%)) !default;\n\n// Allows for customizing button radius independently from global border radius\n$btn-border-radius: var(--#{$prefix}border-radius) !default;\n$btn-border-radius-sm: var(--#{$prefix}border-radius-sm) !default;\n$btn-border-radius-lg: var(--#{$prefix}border-radius-lg) !default;\n\n$btn-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n$btn-hover-bg-shade-amount: 15% !default;\n$btn-hover-bg-tint-amount: 15% !default;\n$btn-hover-border-shade-amount: 20% !default;\n$btn-hover-border-tint-amount: 10% !default;\n$btn-active-bg-shade-amount: 20% !default;\n$btn-active-bg-tint-amount: 20% !default;\n$btn-active-border-shade-amount: 25% !default;\n$btn-active-border-tint-amount: 10% !default;\n// scss-docs-end btn-variables\n\n\n// Forms\n\n// scss-docs-start form-text-variables\n$form-text-margin-top: .25rem !default;\n$form-text-font-size: $small-font-size !default;\n$form-text-font-style: null !default;\n$form-text-font-weight: null !default;\n$form-text-color: var(--#{$prefix}secondary-color) !default;\n// scss-docs-end form-text-variables\n\n// scss-docs-start form-label-variables\n$form-label-margin-bottom: .5rem !default;\n$form-label-font-size: null !default;\n$form-label-font-style: null !default;\n$form-label-font-weight: null !default;\n$form-label-color: null !default;\n// scss-docs-end form-label-variables\n\n// scss-docs-start form-input-variables\n$input-padding-y: $input-btn-padding-y !default;\n$input-padding-x: $input-btn-padding-x !default;\n$input-font-family: $input-btn-font-family !default;\n$input-font-size: $input-btn-font-size !default;\n$input-font-weight: $font-weight-base !default;\n$input-line-height: $input-btn-line-height !default;\n\n$input-padding-y-sm: $input-btn-padding-y-sm !default;\n$input-padding-x-sm: $input-btn-padding-x-sm !default;\n$input-font-size-sm: $input-btn-font-size-sm !default;\n\n$input-padding-y-lg: $input-btn-padding-y-lg !default;\n$input-padding-x-lg: $input-btn-padding-x-lg !default;\n$input-font-size-lg: $input-btn-font-size-lg !default;\n\n$input-bg: var(--#{$prefix}body-bg) !default;\n$input-disabled-color: null !default;\n$input-disabled-bg: var(--#{$prefix}secondary-bg) !default;\n$input-disabled-border-color: null !default;\n\n$input-color: var(--#{$prefix}body-color) !default;\n$input-border-color: var(--#{$prefix}border-color) !default;\n$input-border-width: $input-btn-border-width !default;\n$input-box-shadow: var(--#{$prefix}box-shadow-inset) !default;\n\n$input-border-radius: var(--#{$prefix}border-radius) !default;\n$input-border-radius-sm: var(--#{$prefix}border-radius-sm) !default;\n$input-border-radius-lg: var(--#{$prefix}border-radius-lg) !default;\n\n$input-focus-bg: $input-bg !default;\n$input-focus-border-color: tint-color($component-active-bg, 50%) !default;\n$input-focus-color: $input-color !default;\n$input-focus-width: $input-btn-focus-width !default;\n$input-focus-box-shadow: $input-btn-focus-box-shadow !default;\n\n$input-placeholder-color: var(--#{$prefix}secondary-color) !default;\n$input-plaintext-color: var(--#{$prefix}body-color) !default;\n\n$input-height-border: calc(#{$input-border-width} * 2) !default; // stylelint-disable-line function-disallowed-list\n\n$input-height-inner: add($input-line-height * 1em, $input-padding-y * 2) !default;\n$input-height-inner-half: add($input-line-height * .5em, $input-padding-y) !default;\n$input-height-inner-quarter: add($input-line-height * .25em, $input-padding-y * .5) !default;\n\n$input-height: add($input-line-height * 1em, add($input-padding-y * 2, $input-height-border, false)) !default;\n$input-height-sm: add($input-line-height * 1em, add($input-padding-y-sm * 2, $input-height-border, false)) !default;\n$input-height-lg: add($input-line-height * 1em, add($input-padding-y-lg * 2, $input-height-border, false)) !default;\n\n$input-transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n$form-color-width: 3rem !default;\n// scss-docs-end form-input-variables\n\n// scss-docs-start form-check-variables\n$form-check-input-width: 1em !default;\n$form-check-min-height: $font-size-base * $line-height-base !default;\n$form-check-padding-start: $form-check-input-width + .5em !default;\n$form-check-margin-bottom: .125rem !default;\n$form-check-label-color: null !default;\n$form-check-label-cursor: null !default;\n$form-check-transition: null !default;\n\n$form-check-input-active-filter: brightness(90%) !default;\n\n$form-check-input-bg: $input-bg !default;\n$form-check-input-border: var(--#{$prefix}border-width) solid var(--#{$prefix}border-color) !default;\n$form-check-input-border-radius: .25em !default;\n$form-check-radio-border-radius: 50% !default;\n$form-check-input-focus-border: $input-focus-border-color !default;\n$form-check-input-focus-box-shadow: $focus-ring-box-shadow !default;\n\n$form-check-input-checked-color: $component-active-color !default;\n$form-check-input-checked-bg-color: $component-active-bg !default;\n$form-check-input-checked-border-color: $form-check-input-checked-bg-color !default;\n$form-check-input-checked-bg-image: url(\"data:image/svg+xml,\") !default;\n$form-check-radio-checked-bg-image: url(\"data:image/svg+xml,\") !default;\n\n$form-check-input-indeterminate-color: $component-active-color !default;\n$form-check-input-indeterminate-bg-color: $component-active-bg !default;\n$form-check-input-indeterminate-border-color: $form-check-input-indeterminate-bg-color !default;\n$form-check-input-indeterminate-bg-image: url(\"data:image/svg+xml,\") !default;\n\n$form-check-input-disabled-opacity: .5 !default;\n$form-check-label-disabled-opacity: $form-check-input-disabled-opacity !default;\n$form-check-btn-check-disabled-opacity: $btn-disabled-opacity !default;\n\n$form-check-inline-margin-end: 1rem !default;\n// scss-docs-end form-check-variables\n\n// scss-docs-start form-switch-variables\n$form-switch-color: rgba($black, .25) !default;\n$form-switch-width: 2em !default;\n$form-switch-padding-start: $form-switch-width + .5em !default;\n$form-switch-bg-image: url(\"data:image/svg+xml,\") !default;\n$form-switch-border-radius: $form-switch-width !default;\n$form-switch-transition: background-position .15s ease-in-out !default;\n\n$form-switch-focus-color: $input-focus-border-color !default;\n$form-switch-focus-bg-image: url(\"data:image/svg+xml,\") !default;\n\n$form-switch-checked-color: $component-active-color !default;\n$form-switch-checked-bg-image: url(\"data:image/svg+xml,\") !default;\n$form-switch-checked-bg-position: right center !default;\n// scss-docs-end form-switch-variables\n\n// scss-docs-start input-group-variables\n$input-group-addon-padding-y: $input-padding-y !default;\n$input-group-addon-padding-x: $input-padding-x !default;\n$input-group-addon-font-weight: $input-font-weight !default;\n$input-group-addon-color: $input-color !default;\n$input-group-addon-bg: var(--#{$prefix}tertiary-bg) !default;\n$input-group-addon-border-color: $input-border-color !default;\n// scss-docs-end input-group-variables\n\n// scss-docs-start form-select-variables\n$form-select-padding-y: $input-padding-y !default;\n$form-select-padding-x: $input-padding-x !default;\n$form-select-font-family: $input-font-family !default;\n$form-select-font-size: $input-font-size !default;\n$form-select-indicator-padding: $form-select-padding-x * 3 !default; // Extra padding for background-image\n$form-select-font-weight: $input-font-weight !default;\n$form-select-line-height: $input-line-height !default;\n$form-select-color: $input-color !default;\n$form-select-bg: $input-bg !default;\n$form-select-disabled-color: null !default;\n$form-select-disabled-bg: $input-disabled-bg !default;\n$form-select-disabled-border-color: $input-disabled-border-color !default;\n$form-select-bg-position: right $form-select-padding-x center !default;\n$form-select-bg-size: 16px 12px !default; // In pixels because image dimensions\n$form-select-indicator-color: $gray-800 !default;\n$form-select-indicator: url(\"data:image/svg+xml,\") !default;\n\n$form-select-feedback-icon-padding-end: $form-select-padding-x * 2.5 + $form-select-indicator-padding !default;\n$form-select-feedback-icon-position: center right $form-select-indicator-padding !default;\n$form-select-feedback-icon-size: $input-height-inner-half $input-height-inner-half !default;\n\n$form-select-border-width: $input-border-width !default;\n$form-select-border-color: $input-border-color !default;\n$form-select-border-radius: $input-border-radius !default;\n$form-select-box-shadow: var(--#{$prefix}box-shadow-inset) !default;\n\n$form-select-focus-border-color: $input-focus-border-color !default;\n$form-select-focus-width: $input-focus-width !default;\n$form-select-focus-box-shadow: 0 0 0 $form-select-focus-width $input-btn-focus-color !default;\n\n$form-select-padding-y-sm: $input-padding-y-sm !default;\n$form-select-padding-x-sm: $input-padding-x-sm !default;\n$form-select-font-size-sm: $input-font-size-sm !default;\n$form-select-border-radius-sm: $input-border-radius-sm !default;\n\n$form-select-padding-y-lg: $input-padding-y-lg !default;\n$form-select-padding-x-lg: $input-padding-x-lg !default;\n$form-select-font-size-lg: $input-font-size-lg !default;\n$form-select-border-radius-lg: $input-border-radius-lg !default;\n\n$form-select-transition: $input-transition !default;\n// scss-docs-end form-select-variables\n\n// scss-docs-start form-range-variables\n$form-range-track-width: 100% !default;\n$form-range-track-height: .5rem !default;\n$form-range-track-cursor: pointer !default;\n$form-range-track-bg: var(--#{$prefix}secondary-bg) !default;\n$form-range-track-border-radius: 1rem !default;\n$form-range-track-box-shadow: var(--#{$prefix}box-shadow-inset) !default;\n\n$form-range-thumb-width: 1rem !default;\n$form-range-thumb-height: $form-range-thumb-width !default;\n$form-range-thumb-bg: $component-active-bg !default;\n$form-range-thumb-border: 0 !default;\n$form-range-thumb-border-radius: 1rem !default;\n$form-range-thumb-box-shadow: 0 .1rem .25rem rgba($black, .1) !default;\n$form-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-focus-box-shadow !default;\n$form-range-thumb-focus-box-shadow-width: $input-focus-width !default; // For focus box shadow issue in Edge\n$form-range-thumb-active-bg: tint-color($component-active-bg, 70%) !default;\n$form-range-thumb-disabled-bg: var(--#{$prefix}secondary-color) !default;\n$form-range-thumb-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n// scss-docs-end form-range-variables\n\n// scss-docs-start form-file-variables\n$form-file-button-color: $input-color !default;\n$form-file-button-bg: var(--#{$prefix}tertiary-bg) !default;\n$form-file-button-hover-bg: var(--#{$prefix}secondary-bg) !default;\n// scss-docs-end form-file-variables\n\n// scss-docs-start form-floating-variables\n$form-floating-height: add(3.5rem, $input-height-border) !default;\n$form-floating-line-height: 1.25 !default;\n$form-floating-padding-x: $input-padding-x !default;\n$form-floating-padding-y: 1rem !default;\n$form-floating-input-padding-t: 1.625rem !default;\n$form-floating-input-padding-b: .625rem !default;\n$form-floating-label-height: 1.5em !default;\n$form-floating-label-opacity: .65 !default;\n$form-floating-label-transform: scale(.85) translateY(-.5rem) translateX(.15rem) !default;\n$form-floating-label-disabled-color: $gray-600 !default;\n$form-floating-transition: opacity .1s ease-in-out, transform .1s ease-in-out !default;\n// scss-docs-end form-floating-variables\n\n// Form validation\n\n// scss-docs-start form-feedback-variables\n$form-feedback-margin-top: $form-text-margin-top !default;\n$form-feedback-font-size: $form-text-font-size !default;\n$form-feedback-font-style: $form-text-font-style !default;\n$form-feedback-valid-color: $success !default;\n$form-feedback-invalid-color: $danger !default;\n\n$form-feedback-icon-valid-color: $form-feedback-valid-color !default;\n$form-feedback-icon-valid: url(\"data:image/svg+xml,\") !default;\n$form-feedback-icon-invalid-color: $form-feedback-invalid-color !default;\n$form-feedback-icon-invalid: url(\"data:image/svg+xml,\") !default;\n// scss-docs-end form-feedback-variables\n\n// scss-docs-start form-validation-colors\n$form-valid-color: $form-feedback-valid-color !default;\n$form-valid-border-color: $form-feedback-valid-color !default;\n$form-invalid-color: $form-feedback-invalid-color !default;\n$form-invalid-border-color: $form-feedback-invalid-color !default;\n// scss-docs-end form-validation-colors\n\n// scss-docs-start form-validation-states\n$form-validation-states: (\n \"valid\": (\n \"color\": var(--#{$prefix}form-valid-color),\n \"icon\": $form-feedback-icon-valid,\n \"tooltip-color\": #fff,\n \"tooltip-bg-color\": var(--#{$prefix}success),\n \"focus-box-shadow\": 0 0 $input-btn-focus-blur $input-focus-width rgba(var(--#{$prefix}success-rgb), $input-btn-focus-color-opacity),\n \"border-color\": var(--#{$prefix}form-valid-border-color),\n ),\n \"invalid\": (\n \"color\": var(--#{$prefix}form-invalid-color),\n \"icon\": $form-feedback-icon-invalid,\n \"tooltip-color\": #fff,\n \"tooltip-bg-color\": var(--#{$prefix}danger),\n \"focus-box-shadow\": 0 0 $input-btn-focus-blur $input-focus-width rgba(var(--#{$prefix}danger-rgb), $input-btn-focus-color-opacity),\n \"border-color\": var(--#{$prefix}form-invalid-border-color),\n )\n) !default;\n// scss-docs-end form-validation-states\n\n// Z-index master list\n//\n// Warning: Avoid customizing these values. They're used for a bird's eye view\n// of components dependent on the z-axis and are designed to all work together.\n\n// scss-docs-start zindex-stack\n$zindex-dropdown: 1000 !default;\n$zindex-sticky: 1020 !default;\n$zindex-fixed: 1030 !default;\n$zindex-offcanvas-backdrop: 1040 !default;\n$zindex-offcanvas: 1045 !default;\n$zindex-modal-backdrop: 1050 !default;\n$zindex-modal: 1055 !default;\n$zindex-popover: 1070 !default;\n$zindex-tooltip: 1080 !default;\n$zindex-toast: 1090 !default;\n// scss-docs-end zindex-stack\n\n// scss-docs-start zindex-levels-map\n$zindex-levels: (\n n1: -1,\n 0: 0,\n 1: 1,\n 2: 2,\n 3: 3\n) !default;\n// scss-docs-end zindex-levels-map\n\n\n// Navs\n\n// scss-docs-start nav-variables\n$nav-link-padding-y: .5rem !default;\n$nav-link-padding-x: 1rem !default;\n$nav-link-font-size: null !default;\n$nav-link-font-weight: null !default;\n$nav-link-color: var(--#{$prefix}link-color) !default;\n$nav-link-hover-color: var(--#{$prefix}link-hover-color) !default;\n$nav-link-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out !default;\n$nav-link-disabled-color: var(--#{$prefix}secondary-color) !default;\n$nav-link-focus-box-shadow: $focus-ring-box-shadow !default;\n\n$nav-tabs-border-color: var(--#{$prefix}border-color) !default;\n$nav-tabs-border-width: var(--#{$prefix}border-width) !default;\n$nav-tabs-border-radius: var(--#{$prefix}border-radius) !default;\n$nav-tabs-link-hover-border-color: var(--#{$prefix}secondary-bg) var(--#{$prefix}secondary-bg) $nav-tabs-border-color !default;\n$nav-tabs-link-active-color: var(--#{$prefix}emphasis-color) !default;\n$nav-tabs-link-active-bg: var(--#{$prefix}body-bg) !default;\n$nav-tabs-link-active-border-color: var(--#{$prefix}border-color) var(--#{$prefix}border-color) $nav-tabs-link-active-bg !default;\n\n$nav-pills-border-radius: var(--#{$prefix}border-radius) !default;\n$nav-pills-link-active-color: $component-active-color !default;\n$nav-pills-link-active-bg: $component-active-bg !default;\n\n$nav-underline-gap: 1rem !default;\n$nav-underline-border-width: .125rem !default;\n$nav-underline-link-active-color: var(--#{$prefix}emphasis-color) !default;\n// scss-docs-end nav-variables\n\n\n// Navbar\n\n// scss-docs-start navbar-variables\n$navbar-padding-y: $spacer * .5 !default;\n$navbar-padding-x: null !default;\n\n$navbar-nav-link-padding-x: .5rem !default;\n\n$navbar-brand-font-size: $font-size-lg !default;\n// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link\n$nav-link-height: $font-size-base * $line-height-base + $nav-link-padding-y * 2 !default;\n$navbar-brand-height: $navbar-brand-font-size * $line-height-base !default;\n$navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) * .5 !default;\n$navbar-brand-margin-end: 1rem !default;\n\n$navbar-toggler-padding-y: .25rem !default;\n$navbar-toggler-padding-x: .75rem !default;\n$navbar-toggler-font-size: $font-size-lg !default;\n$navbar-toggler-border-radius: $btn-border-radius !default;\n$navbar-toggler-focus-width: $btn-focus-width !default;\n$navbar-toggler-transition: box-shadow .15s ease-in-out !default;\n\n$navbar-light-color: rgba(var(--#{$prefix}emphasis-color-rgb), .65) !default;\n$navbar-light-hover-color: rgba(var(--#{$prefix}emphasis-color-rgb), .8) !default;\n$navbar-light-active-color: rgba(var(--#{$prefix}emphasis-color-rgb), 1) !default;\n$navbar-light-disabled-color: rgba(var(--#{$prefix}emphasis-color-rgb), .3) !default;\n$navbar-light-icon-color: rgba($body-color, .75) !default;\n$navbar-light-toggler-icon-bg: url(\"data:image/svg+xml,\") !default;\n$navbar-light-toggler-border-color: rgba(var(--#{$prefix}emphasis-color-rgb), .15) !default;\n$navbar-light-brand-color: $navbar-light-active-color !default;\n$navbar-light-brand-hover-color: $navbar-light-active-color !default;\n// scss-docs-end navbar-variables\n\n// scss-docs-start navbar-dark-variables\n$navbar-dark-color: rgba($white, .55) !default;\n$navbar-dark-hover-color: rgba($white, .75) !default;\n$navbar-dark-active-color: $white !default;\n$navbar-dark-disabled-color: rgba($white, .25) !default;\n$navbar-dark-icon-color: $navbar-dark-color !default;\n$navbar-dark-toggler-icon-bg: url(\"data:image/svg+xml,\") !default;\n$navbar-dark-toggler-border-color: rgba($white, .1) !default;\n$navbar-dark-brand-color: $navbar-dark-active-color !default;\n$navbar-dark-brand-hover-color: $navbar-dark-active-color !default;\n// scss-docs-end navbar-dark-variables\n\n\n// Dropdowns\n//\n// Dropdown menu container and contents.\n\n// scss-docs-start dropdown-variables\n$dropdown-min-width: 10rem !default;\n$dropdown-padding-x: 0 !default;\n$dropdown-padding-y: .5rem !default;\n$dropdown-spacer: .125rem !default;\n$dropdown-font-size: $font-size-base !default;\n$dropdown-color: var(--#{$prefix}body-color) !default;\n$dropdown-bg: var(--#{$prefix}body-bg) !default;\n$dropdown-border-color: var(--#{$prefix}border-color-translucent) !default;\n$dropdown-border-radius: var(--#{$prefix}border-radius) !default;\n$dropdown-border-width: var(--#{$prefix}border-width) !default;\n$dropdown-inner-border-radius: calc(#{$dropdown-border-radius} - #{$dropdown-border-width}) !default; // stylelint-disable-line function-disallowed-list\n$dropdown-divider-bg: $dropdown-border-color !default;\n$dropdown-divider-margin-y: $spacer * .5 !default;\n$dropdown-box-shadow: var(--#{$prefix}box-shadow) !default;\n\n$dropdown-link-color: var(--#{$prefix}body-color) !default;\n$dropdown-link-hover-color: $dropdown-link-color !default;\n$dropdown-link-hover-bg: var(--#{$prefix}tertiary-bg) !default;\n\n$dropdown-link-active-color: $component-active-color !default;\n$dropdown-link-active-bg: $component-active-bg !default;\n\n$dropdown-link-disabled-color: var(--#{$prefix}tertiary-color) !default;\n\n$dropdown-item-padding-y: $spacer * .25 !default;\n$dropdown-item-padding-x: $spacer !default;\n\n$dropdown-header-color: $gray-600 !default;\n$dropdown-header-padding-x: $dropdown-item-padding-x !default;\n$dropdown-header-padding-y: $dropdown-padding-y !default;\n// fusv-disable\n$dropdown-header-padding: $dropdown-header-padding-y $dropdown-header-padding-x !default; // Deprecated in v5.2.0\n// fusv-enable\n// scss-docs-end dropdown-variables\n\n// scss-docs-start dropdown-dark-variables\n$dropdown-dark-color: $gray-300 !default;\n$dropdown-dark-bg: $gray-800 !default;\n$dropdown-dark-border-color: $dropdown-border-color !default;\n$dropdown-dark-divider-bg: $dropdown-divider-bg !default;\n$dropdown-dark-box-shadow: null !default;\n$dropdown-dark-link-color: $dropdown-dark-color !default;\n$dropdown-dark-link-hover-color: $white !default;\n$dropdown-dark-link-hover-bg: rgba($white, .15) !default;\n$dropdown-dark-link-active-color: $dropdown-link-active-color !default;\n$dropdown-dark-link-active-bg: $dropdown-link-active-bg !default;\n$dropdown-dark-link-disabled-color: $gray-500 !default;\n$dropdown-dark-header-color: $gray-500 !default;\n// scss-docs-end dropdown-dark-variables\n\n\n// Pagination\n\n// scss-docs-start pagination-variables\n$pagination-padding-y: .375rem !default;\n$pagination-padding-x: .75rem !default;\n$pagination-padding-y-sm: .25rem !default;\n$pagination-padding-x-sm: .5rem !default;\n$pagination-padding-y-lg: .75rem !default;\n$pagination-padding-x-lg: 1.5rem !default;\n\n$pagination-font-size: $font-size-base !default;\n\n$pagination-color: var(--#{$prefix}link-color) !default;\n$pagination-bg: var(--#{$prefix}body-bg) !default;\n$pagination-border-radius: var(--#{$prefix}border-radius) !default;\n$pagination-border-width: var(--#{$prefix}border-width) !default;\n$pagination-margin-start: calc(#{$pagination-border-width} * -1) !default; // stylelint-disable-line function-disallowed-list\n$pagination-border-color: var(--#{$prefix}border-color) !default;\n\n$pagination-focus-color: var(--#{$prefix}link-hover-color) !default;\n$pagination-focus-bg: var(--#{$prefix}secondary-bg) !default;\n$pagination-focus-box-shadow: $focus-ring-box-shadow !default;\n$pagination-focus-outline: 0 !default;\n\n$pagination-hover-color: var(--#{$prefix}link-hover-color) !default;\n$pagination-hover-bg: var(--#{$prefix}tertiary-bg) !default;\n$pagination-hover-border-color: var(--#{$prefix}border-color) !default; // Todo in v6: remove this?\n\n$pagination-active-color: $component-active-color !default;\n$pagination-active-bg: $component-active-bg !default;\n$pagination-active-border-color: $component-active-bg !default;\n\n$pagination-disabled-color: var(--#{$prefix}secondary-color) !default;\n$pagination-disabled-bg: var(--#{$prefix}secondary-bg) !default;\n$pagination-disabled-border-color: var(--#{$prefix}border-color) !default;\n\n$pagination-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n$pagination-border-radius-sm: var(--#{$prefix}border-radius-sm) !default;\n$pagination-border-radius-lg: var(--#{$prefix}border-radius-lg) !default;\n// scss-docs-end pagination-variables\n\n\n// Placeholders\n\n// scss-docs-start placeholders\n$placeholder-opacity-max: .5 !default;\n$placeholder-opacity-min: .2 !default;\n// scss-docs-end placeholders\n\n// Cards\n\n// scss-docs-start card-variables\n$card-spacer-y: $spacer !default;\n$card-spacer-x: $spacer !default;\n$card-title-spacer-y: $spacer * .5 !default;\n$card-title-color: null !default;\n$card-subtitle-color: null !default;\n$card-border-width: var(--#{$prefix}border-width) !default;\n$card-border-color: var(--#{$prefix}border-color-translucent) !default;\n$card-border-radius: var(--#{$prefix}border-radius) !default;\n$card-box-shadow: null !default;\n$card-inner-border-radius: subtract($card-border-radius, $card-border-width) !default;\n$card-cap-padding-y: $card-spacer-y * .5 !default;\n$card-cap-padding-x: $card-spacer-x !default;\n$card-cap-bg: rgba(var(--#{$prefix}body-color-rgb), .03) !default;\n$card-cap-color: null !default;\n$card-height: null !default;\n$card-color: null !default;\n$card-bg: var(--#{$prefix}body-bg) !default;\n$card-img-overlay-padding: $spacer !default;\n$card-group-margin: $grid-gutter-width * .5 !default;\n// scss-docs-end card-variables\n\n// Accordion\n\n// scss-docs-start accordion-variables\n$accordion-padding-y: 1rem !default;\n$accordion-padding-x: 1.25rem !default;\n$accordion-color: var(--#{$prefix}body-color) !default;\n$accordion-bg: var(--#{$prefix}body-bg) !default;\n$accordion-border-width: var(--#{$prefix}border-width) !default;\n$accordion-border-color: var(--#{$prefix}border-color) !default;\n$accordion-border-radius: var(--#{$prefix}border-radius) !default;\n$accordion-inner-border-radius: subtract($accordion-border-radius, $accordion-border-width) !default;\n\n$accordion-body-padding-y: $accordion-padding-y !default;\n$accordion-body-padding-x: $accordion-padding-x !default;\n\n$accordion-button-padding-y: $accordion-padding-y !default;\n$accordion-button-padding-x: $accordion-padding-x !default;\n$accordion-button-color: var(--#{$prefix}body-color) !default;\n$accordion-button-bg: var(--#{$prefix}accordion-bg) !default;\n$accordion-transition: $btn-transition, border-radius .15s ease !default;\n$accordion-button-active-bg: var(--#{$prefix}primary-bg-subtle) !default;\n$accordion-button-active-color: var(--#{$prefix}primary-text-emphasis) !default;\n\n// fusv-disable\n$accordion-button-focus-border-color: $input-focus-border-color !default; // Deprecated in v5.3.3\n// fusv-enable\n$accordion-button-focus-box-shadow: $btn-focus-box-shadow !default;\n\n$accordion-icon-width: 1.25rem !default;\n$accordion-icon-color: $body-color !default;\n$accordion-icon-active-color: $primary-text-emphasis !default;\n$accordion-icon-transition: transform .2s ease-in-out !default;\n$accordion-icon-transform: rotate(-180deg) !default;\n\n$accordion-button-icon: url(\"data:image/svg+xml,\") !default;\n$accordion-button-active-icon: url(\"data:image/svg+xml,\") !default;\n// scss-docs-end accordion-variables\n\n// Tooltips\n\n// scss-docs-start tooltip-variables\n$tooltip-font-size: $font-size-sm !default;\n$tooltip-max-width: 200px !default;\n$tooltip-color: var(--#{$prefix}body-bg) !default;\n$tooltip-bg: var(--#{$prefix}emphasis-color) !default;\n$tooltip-border-radius: var(--#{$prefix}border-radius) !default;\n$tooltip-opacity: .9 !default;\n$tooltip-padding-y: $spacer * .25 !default;\n$tooltip-padding-x: $spacer * .5 !default;\n$tooltip-margin: null !default; // TODO: remove this in v6\n\n$tooltip-arrow-width: .8rem !default;\n$tooltip-arrow-height: .4rem !default;\n// fusv-disable\n$tooltip-arrow-color: null !default; // Deprecated in Bootstrap 5.2.0 for CSS variables\n// fusv-enable\n// scss-docs-end tooltip-variables\n\n// Form tooltips must come after regular tooltips\n// scss-docs-start tooltip-feedback-variables\n$form-feedback-tooltip-padding-y: $tooltip-padding-y !default;\n$form-feedback-tooltip-padding-x: $tooltip-padding-x !default;\n$form-feedback-tooltip-font-size: $tooltip-font-size !default;\n$form-feedback-tooltip-line-height: null !default;\n$form-feedback-tooltip-opacity: $tooltip-opacity !default;\n$form-feedback-tooltip-border-radius: $tooltip-border-radius !default;\n// scss-docs-end tooltip-feedback-variables\n\n\n// Popovers\n\n// scss-docs-start popover-variables\n$popover-font-size: $font-size-sm !default;\n$popover-bg: var(--#{$prefix}body-bg) !default;\n$popover-max-width: 276px !default;\n$popover-border-width: var(--#{$prefix}border-width) !default;\n$popover-border-color: var(--#{$prefix}border-color-translucent) !default;\n$popover-border-radius: var(--#{$prefix}border-radius-lg) !default;\n$popover-inner-border-radius: calc(#{$popover-border-radius} - #{$popover-border-width}) !default; // stylelint-disable-line function-disallowed-list\n$popover-box-shadow: var(--#{$prefix}box-shadow) !default;\n\n$popover-header-font-size: $font-size-base !default;\n$popover-header-bg: var(--#{$prefix}secondary-bg) !default;\n$popover-header-color: $headings-color !default;\n$popover-header-padding-y: .5rem !default;\n$popover-header-padding-x: $spacer !default;\n\n$popover-body-color: var(--#{$prefix}body-color) !default;\n$popover-body-padding-y: $spacer !default;\n$popover-body-padding-x: $spacer !default;\n\n$popover-arrow-width: 1rem !default;\n$popover-arrow-height: .5rem !default;\n// scss-docs-end popover-variables\n\n// fusv-disable\n// Deprecated in Bootstrap 5.2.0 for CSS variables\n$popover-arrow-color: $popover-bg !default;\n$popover-arrow-outer-color: var(--#{$prefix}border-color-translucent) !default;\n// fusv-enable\n\n\n// Toasts\n\n// scss-docs-start toast-variables\n$toast-max-width: 350px !default;\n$toast-padding-x: .75rem !default;\n$toast-padding-y: .5rem !default;\n$toast-font-size: .875rem !default;\n$toast-color: null !default;\n$toast-background-color: rgba(var(--#{$prefix}body-bg-rgb), .85) !default;\n$toast-border-width: var(--#{$prefix}border-width) !default;\n$toast-border-color: var(--#{$prefix}border-color-translucent) !default;\n$toast-border-radius: var(--#{$prefix}border-radius) !default;\n$toast-box-shadow: var(--#{$prefix}box-shadow) !default;\n$toast-spacing: $container-padding-x !default;\n\n$toast-header-color: var(--#{$prefix}secondary-color) !default;\n$toast-header-background-color: rgba(var(--#{$prefix}body-bg-rgb), .85) !default;\n$toast-header-border-color: $toast-border-color !default;\n// scss-docs-end toast-variables\n\n\n// Badges\n\n// scss-docs-start badge-variables\n$badge-font-size: .75em !default;\n$badge-font-weight: $font-weight-bold !default;\n$badge-color: $white !default;\n$badge-padding-y: .35em !default;\n$badge-padding-x: .65em !default;\n$badge-border-radius: var(--#{$prefix}border-radius) !default;\n// scss-docs-end badge-variables\n\n\n// Modals\n\n// scss-docs-start modal-variables\n$modal-inner-padding: $spacer !default;\n\n$modal-footer-margin-between: .5rem !default;\n\n$modal-dialog-margin: .5rem !default;\n$modal-dialog-margin-y-sm-up: 1.75rem !default;\n\n$modal-title-line-height: $line-height-base !default;\n\n$modal-content-color: null !default;\n$modal-content-bg: var(--#{$prefix}body-bg) !default;\n$modal-content-border-color: var(--#{$prefix}border-color-translucent) !default;\n$modal-content-border-width: var(--#{$prefix}border-width) !default;\n$modal-content-border-radius: var(--#{$prefix}border-radius-lg) !default;\n$modal-content-inner-border-radius: subtract($modal-content-border-radius, $modal-content-border-width) !default;\n$modal-content-box-shadow-xs: var(--#{$prefix}box-shadow-sm) !default;\n$modal-content-box-shadow-sm-up: var(--#{$prefix}box-shadow) !default;\n\n$modal-backdrop-bg: $black !default;\n$modal-backdrop-opacity: .5 !default;\n\n$modal-header-border-color: var(--#{$prefix}border-color) !default;\n$modal-header-border-width: $modal-content-border-width !default;\n$modal-header-padding-y: $modal-inner-padding !default;\n$modal-header-padding-x: $modal-inner-padding !default;\n$modal-header-padding: $modal-header-padding-y $modal-header-padding-x !default; // Keep this for backwards compatibility\n\n$modal-footer-bg: null !default;\n$modal-footer-border-color: $modal-header-border-color !default;\n$modal-footer-border-width: $modal-header-border-width !default;\n\n$modal-sm: 300px !default;\n$modal-md: 500px !default;\n$modal-lg: 800px !default;\n$modal-xl: 1140px !default;\n\n$modal-fade-transform: translate(0, -50px) !default;\n$modal-show-transform: none !default;\n$modal-transition: transform .3s ease-out !default;\n$modal-scale-transform: scale(1.02) !default;\n// scss-docs-end modal-variables\n\n\n// Alerts\n//\n// Define alert colors, border radius, and padding.\n\n// scss-docs-start alert-variables\n$alert-padding-y: $spacer !default;\n$alert-padding-x: $spacer !default;\n$alert-margin-bottom: 1rem !default;\n$alert-border-radius: var(--#{$prefix}border-radius) !default;\n$alert-link-font-weight: $font-weight-bold !default;\n$alert-border-width: var(--#{$prefix}border-width) !default;\n$alert-dismissible-padding-r: $alert-padding-x * 3 !default; // 3x covers width of x plus default padding on either side\n// scss-docs-end alert-variables\n\n// fusv-disable\n$alert-bg-scale: -80% !default; // Deprecated in v5.2.0, to be removed in v6\n$alert-border-scale: -70% !default; // Deprecated in v5.2.0, to be removed in v6\n$alert-color-scale: 40% !default; // Deprecated in v5.2.0, to be removed in v6\n// fusv-enable\n\n// Progress bars\n\n// scss-docs-start progress-variables\n$progress-height: 1rem !default;\n$progress-font-size: $font-size-base * .75 !default;\n$progress-bg: var(--#{$prefix}secondary-bg) !default;\n$progress-border-radius: var(--#{$prefix}border-radius) !default;\n$progress-box-shadow: var(--#{$prefix}box-shadow-inset) !default;\n$progress-bar-color: $white !default;\n$progress-bar-bg: $primary !default;\n$progress-bar-animation-timing: 1s linear infinite !default;\n$progress-bar-transition: width .6s ease !default;\n// scss-docs-end progress-variables\n\n\n// List group\n\n// scss-docs-start list-group-variables\n$list-group-color: var(--#{$prefix}body-color) !default;\n$list-group-bg: var(--#{$prefix}body-bg) !default;\n$list-group-border-color: var(--#{$prefix}border-color) !default;\n$list-group-border-width: var(--#{$prefix}border-width) !default;\n$list-group-border-radius: var(--#{$prefix}border-radius) !default;\n\n$list-group-item-padding-y: $spacer * .5 !default;\n$list-group-item-padding-x: $spacer !default;\n// fusv-disable\n$list-group-item-bg-scale: -80% !default; // Deprecated in v5.3.0\n$list-group-item-color-scale: 40% !default; // Deprecated in v5.3.0\n// fusv-enable\n\n$list-group-hover-bg: var(--#{$prefix}tertiary-bg) !default;\n$list-group-active-color: $component-active-color !default;\n$list-group-active-bg: $component-active-bg !default;\n$list-group-active-border-color: $list-group-active-bg !default;\n\n$list-group-disabled-color: var(--#{$prefix}secondary-color) !default;\n$list-group-disabled-bg: $list-group-bg !default;\n\n$list-group-action-color: var(--#{$prefix}secondary-color) !default;\n$list-group-action-hover-color: var(--#{$prefix}emphasis-color) !default;\n\n$list-group-action-active-color: var(--#{$prefix}body-color) !default;\n$list-group-action-active-bg: var(--#{$prefix}secondary-bg) !default;\n// scss-docs-end list-group-variables\n\n\n// Image thumbnails\n\n// scss-docs-start thumbnail-variables\n$thumbnail-padding: .25rem !default;\n$thumbnail-bg: var(--#{$prefix}body-bg) !default;\n$thumbnail-border-width: var(--#{$prefix}border-width) !default;\n$thumbnail-border-color: var(--#{$prefix}border-color) !default;\n$thumbnail-border-radius: var(--#{$prefix}border-radius) !default;\n$thumbnail-box-shadow: var(--#{$prefix}box-shadow-sm) !default;\n// scss-docs-end thumbnail-variables\n\n\n// Figures\n\n// scss-docs-start figure-variables\n$figure-caption-font-size: $small-font-size !default;\n$figure-caption-color: var(--#{$prefix}secondary-color) !default;\n// scss-docs-end figure-variables\n\n\n// Breadcrumbs\n\n// scss-docs-start breadcrumb-variables\n$breadcrumb-font-size: null !default;\n$breadcrumb-padding-y: 0 !default;\n$breadcrumb-padding-x: 0 !default;\n$breadcrumb-item-padding-x: .5rem !default;\n$breadcrumb-margin-bottom: 1rem !default;\n$breadcrumb-bg: null !default;\n$breadcrumb-divider-color: var(--#{$prefix}secondary-color) !default;\n$breadcrumb-active-color: var(--#{$prefix}secondary-color) !default;\n$breadcrumb-divider: quote(\"/\") !default;\n$breadcrumb-divider-flipped: $breadcrumb-divider !default;\n$breadcrumb-border-radius: null !default;\n// scss-docs-end breadcrumb-variables\n\n// Carousel\n\n// scss-docs-start carousel-variables\n$carousel-control-color: $white !default;\n$carousel-control-width: 15% !default;\n$carousel-control-opacity: .5 !default;\n$carousel-control-hover-opacity: .9 !default;\n$carousel-control-transition: opacity .15s ease !default;\n\n$carousel-indicator-width: 30px !default;\n$carousel-indicator-height: 3px !default;\n$carousel-indicator-hit-area-height: 10px !default;\n$carousel-indicator-spacer: 3px !default;\n$carousel-indicator-opacity: .5 !default;\n$carousel-indicator-active-bg: $white !default;\n$carousel-indicator-active-opacity: 1 !default;\n$carousel-indicator-transition: opacity .6s ease !default;\n\n$carousel-caption-width: 70% !default;\n$carousel-caption-color: $white !default;\n$carousel-caption-padding-y: 1.25rem !default;\n$carousel-caption-spacer: 1.25rem !default;\n\n$carousel-control-icon-width: 2rem !default;\n\n$carousel-control-prev-icon-bg: url(\"data:image/svg+xml,\") !default;\n$carousel-control-next-icon-bg: url(\"data:image/svg+xml,\") !default;\n\n$carousel-transition-duration: .6s !default;\n$carousel-transition: transform $carousel-transition-duration ease-in-out !default; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`)\n// scss-docs-end carousel-variables\n\n// scss-docs-start carousel-dark-variables\n$carousel-dark-indicator-active-bg: $black !default;\n$carousel-dark-caption-color: $black !default;\n$carousel-dark-control-icon-filter: invert(1) grayscale(100) !default;\n// scss-docs-end carousel-dark-variables\n\n\n// Spinners\n\n// scss-docs-start spinner-variables\n$spinner-width: 2rem !default;\n$spinner-height: $spinner-width !default;\n$spinner-vertical-align: -.125em !default;\n$spinner-border-width: .25em !default;\n$spinner-animation-speed: .75s !default;\n\n$spinner-width-sm: 1rem !default;\n$spinner-height-sm: $spinner-width-sm !default;\n$spinner-border-width-sm: .2em !default;\n// scss-docs-end spinner-variables\n\n\n// Close\n\n// scss-docs-start close-variables\n$btn-close-width: 1em !default;\n$btn-close-height: $btn-close-width !default;\n$btn-close-padding-x: .25em !default;\n$btn-close-padding-y: $btn-close-padding-x !default;\n$btn-close-color: $black !default;\n$btn-close-bg: url(\"data:image/svg+xml,\") !default;\n$btn-close-focus-shadow: $focus-ring-box-shadow !default;\n$btn-close-opacity: .5 !default;\n$btn-close-hover-opacity: .75 !default;\n$btn-close-focus-opacity: 1 !default;\n$btn-close-disabled-opacity: .25 !default;\n$btn-close-white-filter: invert(1) grayscale(100%) brightness(200%) !default;\n// scss-docs-end close-variables\n\n\n// Offcanvas\n\n// scss-docs-start offcanvas-variables\n$offcanvas-padding-y: $modal-inner-padding !default;\n$offcanvas-padding-x: $modal-inner-padding !default;\n$offcanvas-horizontal-width: 400px !default;\n$offcanvas-vertical-height: 30vh !default;\n$offcanvas-transition-duration: .3s !default;\n$offcanvas-border-color: $modal-content-border-color !default;\n$offcanvas-border-width: $modal-content-border-width !default;\n$offcanvas-title-line-height: $modal-title-line-height !default;\n$offcanvas-bg-color: var(--#{$prefix}body-bg) !default;\n$offcanvas-color: var(--#{$prefix}body-color) !default;\n$offcanvas-box-shadow: $modal-content-box-shadow-xs !default;\n$offcanvas-backdrop-bg: $modal-backdrop-bg !default;\n$offcanvas-backdrop-opacity: $modal-backdrop-opacity !default;\n// scss-docs-end offcanvas-variables\n\n// Code\n\n$code-font-size: $small-font-size !default;\n$code-color: $pink !default;\n\n$kbd-padding-y: .1875rem !default;\n$kbd-padding-x: .375rem !default;\n$kbd-font-size: $code-font-size !default;\n$kbd-color: var(--#{$prefix}body-bg) !default;\n$kbd-bg: var(--#{$prefix}body-color) !default;\n$nested-kbd-font-weight: null !default; // Deprecated in v5.2.0, removing in v6\n\n$pre-color: null !default;\n\n@import \"variables-dark\"; // TODO: can be removed safely in v6, only here to avoid breaking changes in v5.3\n","// Row\n//\n// Rows contain your columns.\n\n:root {\n @each $name, $value in $grid-breakpoints {\n --#{$prefix}breakpoint-#{$name}: #{$value};\n }\n}\n\n@if $enable-grid-classes {\n .row {\n @include make-row();\n\n > * {\n @include make-col-ready();\n }\n }\n}\n\n@if $enable-cssgrid {\n .grid {\n display: grid;\n grid-template-rows: repeat(var(--#{$prefix}rows, 1), 1fr);\n grid-template-columns: repeat(var(--#{$prefix}columns, #{$grid-columns}), 1fr);\n gap: var(--#{$prefix}gap, #{$grid-gutter-width});\n\n @include make-cssgrid();\n }\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n@if $enable-grid-classes {\n @include make-grid-columns();\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n@mixin make-row($gutter: $grid-gutter-width) {\n --#{$prefix}gutter-x: #{$gutter};\n --#{$prefix}gutter-y: 0;\n display: flex;\n flex-wrap: wrap;\n // TODO: Revisit calc order after https://github.com/react-bootstrap/react-bootstrap/issues/6039 is fixed\n margin-top: calc(-1 * var(--#{$prefix}gutter-y)); // stylelint-disable-line function-disallowed-list\n margin-right: calc(-.5 * var(--#{$prefix}gutter-x)); // stylelint-disable-line function-disallowed-list\n margin-left: calc(-.5 * var(--#{$prefix}gutter-x)); // stylelint-disable-line function-disallowed-list\n}\n\n@mixin make-col-ready() {\n // Add box sizing if only the grid is loaded\n box-sizing: if(variable-exists(include-column-box-sizing) and $include-column-box-sizing, border-box, null);\n // Prevent columns from becoming too narrow when at smaller grid tiers by\n // always setting `width: 100%;`. This works because we set the width\n // later on to override this initial width.\n flex-shrink: 0;\n width: 100%;\n max-width: 100%; // Prevent `.col-auto`, `.col` (& responsive variants) from breaking out the grid\n padding-right: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list\n padding-left: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list\n margin-top: var(--#{$prefix}gutter-y);\n}\n\n@mixin make-col($size: false, $columns: $grid-columns) {\n @if $size {\n flex: 0 0 auto;\n width: percentage(divide($size, $columns));\n\n } @else {\n flex: 1 1 0;\n max-width: 100%;\n }\n}\n\n@mixin make-col-auto() {\n flex: 0 0 auto;\n width: auto;\n}\n\n@mixin make-col-offset($size, $columns: $grid-columns) {\n $num: divide($size, $columns);\n margin-left: if($num == 0, 0, percentage($num));\n}\n\n// Row columns\n//\n// Specify on a parent element(e.g., .row) to force immediate children into NN\n// number of columns. Supports wrapping to new lines, but does not do a Masonry\n// style grid.\n@mixin row-cols($count) {\n > * {\n flex: 0 0 auto;\n width: percentage(divide(1, $count));\n }\n}\n\n// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `$grid-columns`.\n\n@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {\n @each $breakpoint in map-keys($breakpoints) {\n $infix: breakpoint-infix($breakpoint, $breakpoints);\n\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n // Provide basic `.col-{bp}` classes for equal-width flexbox columns\n .col#{$infix} {\n flex: 1 0 0%; // Flexbugs #4: https://github.com/philipwalton/flexbugs#flexbug-4\n }\n\n .row-cols#{$infix}-auto > * {\n @include make-col-auto();\n }\n\n @if $grid-row-columns > 0 {\n @for $i from 1 through $grid-row-columns {\n .row-cols#{$infix}-#{$i} {\n @include row-cols($i);\n }\n }\n }\n\n .col#{$infix}-auto {\n @include make-col-auto();\n }\n\n @if $columns > 0 {\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @include make-col($i, $columns);\n }\n }\n\n // `$columns - 1` because offsetting by the width of an entire row isn't possible\n @for $i from 0 through ($columns - 1) {\n @if not ($infix == \"\" and $i == 0) { // Avoid emitting useless .offset-0\n .offset#{$infix}-#{$i} {\n @include make-col-offset($i, $columns);\n }\n }\n }\n }\n\n // Gutters\n //\n // Make use of `.g-*`, `.gx-*` or `.gy-*` utilities to change spacing between the columns.\n @each $key, $value in $gutters {\n .g#{$infix}-#{$key},\n .gx#{$infix}-#{$key} {\n --#{$prefix}gutter-x: #{$value};\n }\n\n .g#{$infix}-#{$key},\n .gy#{$infix}-#{$key} {\n --#{$prefix}gutter-y: #{$value};\n }\n }\n }\n }\n}\n\n@mixin make-cssgrid($columns: $grid-columns, $breakpoints: $grid-breakpoints) {\n @each $breakpoint in map-keys($breakpoints) {\n $infix: breakpoint-infix($breakpoint, $breakpoints);\n\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n @if $columns > 0 {\n @for $i from 1 through $columns {\n .g-col#{$infix}-#{$i} {\n grid-column: auto / span $i;\n }\n }\n\n // Start with `1` because `0` is an invalid value.\n // Ends with `$columns - 1` because offsetting by the width of an entire row isn't possible.\n @for $i from 1 through ($columns - 1) {\n .g-start#{$infix}-#{$i} {\n grid-column-start: $i;\n }\n }\n }\n }\n }\n}\n","// Utility generator\n// Used to generate utilities & print utilities\n@mixin generate-utility($utility, $infix: \"\", $is-rfs-media-query: false) {\n $values: map-get($utility, values);\n\n // If the values are a list or string, convert it into a map\n @if type-of($values) == \"string\" or type-of(nth($values, 1)) != \"list\" {\n $values: zip($values, $values);\n }\n\n @each $key, $value in $values {\n $properties: map-get($utility, property);\n\n // Multiple properties are possible, for example with vertical or horizontal margins or paddings\n @if type-of($properties) == \"string\" {\n $properties: append((), $properties);\n }\n\n // Use custom class if present\n $property-class: if(map-has-key($utility, class), map-get($utility, class), nth($properties, 1));\n $property-class: if($property-class == null, \"\", $property-class);\n\n // Use custom CSS variable name if present, otherwise default to `class`\n $css-variable-name: if(map-has-key($utility, css-variable-name), map-get($utility, css-variable-name), map-get($utility, class));\n\n // State params to generate pseudo-classes\n $state: if(map-has-key($utility, state), map-get($utility, state), ());\n\n $infix: if($property-class == \"\" and str-slice($infix, 1, 1) == \"-\", str-slice($infix, 2), $infix);\n\n // Don't prefix if value key is null (e.g. with shadow class)\n $property-class-modifier: if($key, if($property-class == \"\" and $infix == \"\", \"\", \"-\") + $key, \"\");\n\n @if map-get($utility, rfs) {\n // Inside the media query\n @if $is-rfs-media-query {\n $val: rfs-value($value);\n\n // Do not render anything if fluid and non fluid values are the same\n $value: if($val == rfs-fluid-value($value), null, $val);\n }\n @else {\n $value: rfs-fluid-value($value);\n }\n }\n\n $is-css-var: map-get($utility, css-var);\n $is-local-vars: map-get($utility, local-vars);\n $is-rtl: map-get($utility, rtl);\n\n @if $value != null {\n @if $is-rtl == false {\n /* rtl:begin:remove */\n }\n\n @if $is-css-var {\n .#{$property-class + $infix + $property-class-modifier} {\n --#{$prefix}#{$css-variable-name}: #{$value};\n }\n\n @each $pseudo in $state {\n .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} {\n --#{$prefix}#{$css-variable-name}: #{$value};\n }\n }\n } @else {\n .#{$property-class + $infix + $property-class-modifier} {\n @each $property in $properties {\n @if $is-local-vars {\n @each $local-var, $variable in $is-local-vars {\n --#{$prefix}#{$local-var}: #{$variable};\n }\n }\n #{$property}: $value if($enable-important-utilities, !important, null);\n }\n }\n\n @each $pseudo in $state {\n .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} {\n @each $property in $properties {\n @if $is-local-vars {\n @each $local-var, $variable in $is-local-vars {\n --#{$prefix}#{$local-var}: #{$variable};\n }\n }\n #{$property}: $value if($enable-important-utilities, !important, null);\n }\n }\n }\n }\n\n @if $is-rtl == false {\n /* rtl:end:remove */\n }\n }\n }\n}\n","// Loop over each breakpoint\n@each $breakpoint in map-keys($grid-breakpoints) {\n\n // Generate media query if needed\n @include media-breakpoint-up($breakpoint) {\n $infix: breakpoint-infix($breakpoint, $grid-breakpoints);\n\n // Loop over each utility property\n @each $key, $utility in $utilities {\n // The utility can be disabled with `false`, thus check if the utility is a map first\n // Only proceed if responsive media queries are enabled or if it's the base media query\n @if type-of($utility) == \"map\" and (map-get($utility, responsive) or $infix == \"\") {\n @include generate-utility($utility, $infix);\n }\n }\n }\n}\n\n// RFS rescaling\n@media (min-width: $rfs-mq-value) {\n @each $breakpoint in map-keys($grid-breakpoints) {\n $infix: breakpoint-infix($breakpoint, $grid-breakpoints);\n\n @if (map-get($grid-breakpoints, $breakpoint) < $rfs-breakpoint) {\n // Loop over each utility property\n @each $key, $utility in $utilities {\n // The utility can be disabled with `false`, thus check if the utility is a map first\n // Only proceed if responsive media queries are enabled or if it's the base media query\n @if type-of($utility) == \"map\" and map-get($utility, rfs) and (map-get($utility, responsive) or $infix == \"\") {\n @include generate-utility($utility, $infix, true);\n }\n }\n }\n }\n}\n\n\n// Print utilities\n@media print {\n @each $key, $utility in $utilities {\n // The utility can be disabled with `false`, thus check if the utility is a map first\n // Then check if the utility needs print styles\n @if type-of($utility) == \"map\" and map-get($utility, print) == true {\n @include generate-utility($utility, \"-print\");\n }\n }\n}\n"]} \ No newline at end of file diff --git a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css b/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css deleted file mode 100644 index 49b843b..0000000 --- a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Bootstrap Grid v5.3.3 (https://getbootstrap.com/) - * Copyright 2011-2024 The Bootstrap Authors - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}:root{--bs-breakpoint-xs:0;--bs-breakpoint-sm:576px;--bs-breakpoint-md:768px;--bs-breakpoint-lg:992px;--bs-breakpoint-xl:1200px;--bs-breakpoint-xxl:1400px}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{box-sizing:border-box;flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.66666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.66666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.66666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.66666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.66666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.66666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-inline-grid{display:inline-grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}@media (min-width:576px){.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-inline-grid{display:inline-grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}}@media (min-width:768px){.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-inline-grid{display:inline-grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}}@media (min-width:992px){.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-inline-grid{display:inline-grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}}@media (min-width:1200px){.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-inline-grid{display:inline-grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}}@media (min-width:1400px){.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-inline-grid{display:inline-grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-inline-grid{display:inline-grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} -/*# sourceMappingURL=bootstrap-grid.min.css.map */ \ No newline at end of file diff --git a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map b/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map deleted file mode 100644 index a0db8b5..0000000 --- a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../scss/mixins/_banner.scss","../../scss/_containers.scss","dist/css/bootstrap-grid.css","../../scss/mixins/_container.scss","../../scss/mixins/_breakpoints.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_utilities.scss","../../scss/utilities/_api.scss"],"names":[],"mappings":"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","sourcesContent":["@mixin bsBanner($file) {\n /*!\n * Bootstrap #{$file} v5.3.3 (https://getbootstrap.com/)\n * Copyright 2011-2024 The Bootstrap Authors\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n}\n","// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n@if $enable-container-classes {\n // Single container class with breakpoint max-widths\n .container,\n // 100% wide container at all breakpoints\n .container-fluid {\n @include make-container();\n }\n\n // Responsive containers that are 100% wide until a breakpoint\n @each $breakpoint, $container-max-width in $container-max-widths {\n .container-#{$breakpoint} {\n @extend .container-fluid;\n }\n\n @include media-breakpoint-up($breakpoint, $grid-breakpoints) {\n %responsive-container-#{$breakpoint} {\n max-width: $container-max-width;\n }\n\n // Extend each breakpoint which is smaller or equal to the current breakpoint\n $extend-breakpoint: true;\n\n @each $name, $width in $grid-breakpoints {\n @if ($extend-breakpoint) {\n .container#{breakpoint-infix($name, $grid-breakpoints)} {\n @extend %responsive-container-#{$breakpoint};\n }\n\n // Once the current breakpoint is reached, stop extending\n @if ($breakpoint == $name) {\n $extend-breakpoint: false;\n }\n }\n }\n }\n }\n}\n","/*!\n * Bootstrap Grid v5.3.3 (https://getbootstrap.com/)\n * Copyright 2011-2024 The Bootstrap Authors\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n.container,\n.container-fluid,\n.container-xxl,\n.container-xl,\n.container-lg,\n.container-md,\n.container-sm {\n --bs-gutter-x: 1.5rem;\n --bs-gutter-y: 0;\n width: 100%;\n padding-right: calc(var(--bs-gutter-x) * 0.5);\n padding-left: calc(var(--bs-gutter-x) * 0.5);\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container-sm, .container {\n max-width: 540px;\n }\n}\n@media (min-width: 768px) {\n .container-md, .container-sm, .container {\n max-width: 720px;\n }\n}\n@media (min-width: 992px) {\n .container-lg, .container-md, .container-sm, .container {\n max-width: 960px;\n }\n}\n@media (min-width: 1200px) {\n .container-xl, .container-lg, .container-md, .container-sm, .container {\n max-width: 1140px;\n }\n}\n@media (min-width: 1400px) {\n .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container {\n max-width: 1320px;\n }\n}\n:root {\n --bs-breakpoint-xs: 0;\n --bs-breakpoint-sm: 576px;\n --bs-breakpoint-md: 768px;\n --bs-breakpoint-lg: 992px;\n --bs-breakpoint-xl: 1200px;\n --bs-breakpoint-xxl: 1400px;\n}\n\n.row {\n --bs-gutter-x: 1.5rem;\n --bs-gutter-y: 0;\n display: flex;\n flex-wrap: wrap;\n margin-top: calc(-1 * var(--bs-gutter-y));\n margin-right: calc(-0.5 * var(--bs-gutter-x));\n margin-left: calc(-0.5 * var(--bs-gutter-x));\n}\n.row > * {\n box-sizing: border-box;\n flex-shrink: 0;\n width: 100%;\n max-width: 100%;\n padding-right: calc(var(--bs-gutter-x) * 0.5);\n padding-left: calc(var(--bs-gutter-x) * 0.5);\n margin-top: var(--bs-gutter-y);\n}\n\n.col {\n flex: 1 0 0%;\n}\n\n.row-cols-auto > * {\n flex: 0 0 auto;\n width: auto;\n}\n\n.row-cols-1 > * {\n flex: 0 0 auto;\n width: 100%;\n}\n\n.row-cols-2 > * {\n flex: 0 0 auto;\n width: 50%;\n}\n\n.row-cols-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n}\n\n.row-cols-4 > * {\n flex: 0 0 auto;\n width: 25%;\n}\n\n.row-cols-5 > * {\n flex: 0 0 auto;\n width: 20%;\n}\n\n.row-cols-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n}\n\n.col-auto {\n flex: 0 0 auto;\n width: auto;\n}\n\n.col-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n}\n\n.col-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n}\n\n.col-3 {\n flex: 0 0 auto;\n width: 25%;\n}\n\n.col-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n}\n\n.col-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n}\n\n.col-6 {\n flex: 0 0 auto;\n width: 50%;\n}\n\n.col-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n}\n\n.col-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n}\n\n.col-9 {\n flex: 0 0 auto;\n width: 75%;\n}\n\n.col-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n}\n\n.col-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n}\n\n.col-12 {\n flex: 0 0 auto;\n width: 100%;\n}\n\n.offset-1 {\n margin-left: 8.33333333%;\n}\n\n.offset-2 {\n margin-left: 16.66666667%;\n}\n\n.offset-3 {\n margin-left: 25%;\n}\n\n.offset-4 {\n margin-left: 33.33333333%;\n}\n\n.offset-5 {\n margin-left: 41.66666667%;\n}\n\n.offset-6 {\n margin-left: 50%;\n}\n\n.offset-7 {\n margin-left: 58.33333333%;\n}\n\n.offset-8 {\n margin-left: 66.66666667%;\n}\n\n.offset-9 {\n margin-left: 75%;\n}\n\n.offset-10 {\n margin-left: 83.33333333%;\n}\n\n.offset-11 {\n margin-left: 91.66666667%;\n}\n\n.g-0,\n.gx-0 {\n --bs-gutter-x: 0;\n}\n\n.g-0,\n.gy-0 {\n --bs-gutter-y: 0;\n}\n\n.g-1,\n.gx-1 {\n --bs-gutter-x: 0.25rem;\n}\n\n.g-1,\n.gy-1 {\n --bs-gutter-y: 0.25rem;\n}\n\n.g-2,\n.gx-2 {\n --bs-gutter-x: 0.5rem;\n}\n\n.g-2,\n.gy-2 {\n --bs-gutter-y: 0.5rem;\n}\n\n.g-3,\n.gx-3 {\n --bs-gutter-x: 1rem;\n}\n\n.g-3,\n.gy-3 {\n --bs-gutter-y: 1rem;\n}\n\n.g-4,\n.gx-4 {\n --bs-gutter-x: 1.5rem;\n}\n\n.g-4,\n.gy-4 {\n --bs-gutter-y: 1.5rem;\n}\n\n.g-5,\n.gx-5 {\n --bs-gutter-x: 3rem;\n}\n\n.g-5,\n.gy-5 {\n --bs-gutter-y: 3rem;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n flex: 1 0 0%;\n }\n .row-cols-sm-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-sm-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-sm-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-sm-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-sm-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-sm-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-sm-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-sm-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-sm-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-sm-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-sm-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-sm-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-sm-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-sm-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-sm-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-sm-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-sm-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-sm-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-sm-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-sm-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-sm-0 {\n margin-left: 0;\n }\n .offset-sm-1 {\n margin-left: 8.33333333%;\n }\n .offset-sm-2 {\n margin-left: 16.66666667%;\n }\n .offset-sm-3 {\n margin-left: 25%;\n }\n .offset-sm-4 {\n margin-left: 33.33333333%;\n }\n .offset-sm-5 {\n margin-left: 41.66666667%;\n }\n .offset-sm-6 {\n margin-left: 50%;\n }\n .offset-sm-7 {\n margin-left: 58.33333333%;\n }\n .offset-sm-8 {\n margin-left: 66.66666667%;\n }\n .offset-sm-9 {\n margin-left: 75%;\n }\n .offset-sm-10 {\n margin-left: 83.33333333%;\n }\n .offset-sm-11 {\n margin-left: 91.66666667%;\n }\n .g-sm-0,\n .gx-sm-0 {\n --bs-gutter-x: 0;\n }\n .g-sm-0,\n .gy-sm-0 {\n --bs-gutter-y: 0;\n }\n .g-sm-1,\n .gx-sm-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-sm-1,\n .gy-sm-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-sm-2,\n .gx-sm-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-sm-2,\n .gy-sm-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-sm-3,\n .gx-sm-3 {\n --bs-gutter-x: 1rem;\n }\n .g-sm-3,\n .gy-sm-3 {\n --bs-gutter-y: 1rem;\n }\n .g-sm-4,\n .gx-sm-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-sm-4,\n .gy-sm-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-sm-5,\n .gx-sm-5 {\n --bs-gutter-x: 3rem;\n }\n .g-sm-5,\n .gy-sm-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 768px) {\n .col-md {\n flex: 1 0 0%;\n }\n .row-cols-md-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-md-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-md-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-md-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-md-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-md-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-md-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-md-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-md-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-md-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-md-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-md-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-md-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-md-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-md-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-md-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-md-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-md-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-md-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-md-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-md-0 {\n margin-left: 0;\n }\n .offset-md-1 {\n margin-left: 8.33333333%;\n }\n .offset-md-2 {\n margin-left: 16.66666667%;\n }\n .offset-md-3 {\n margin-left: 25%;\n }\n .offset-md-4 {\n margin-left: 33.33333333%;\n }\n .offset-md-5 {\n margin-left: 41.66666667%;\n }\n .offset-md-6 {\n margin-left: 50%;\n }\n .offset-md-7 {\n margin-left: 58.33333333%;\n }\n .offset-md-8 {\n margin-left: 66.66666667%;\n }\n .offset-md-9 {\n margin-left: 75%;\n }\n .offset-md-10 {\n margin-left: 83.33333333%;\n }\n .offset-md-11 {\n margin-left: 91.66666667%;\n }\n .g-md-0,\n .gx-md-0 {\n --bs-gutter-x: 0;\n }\n .g-md-0,\n .gy-md-0 {\n --bs-gutter-y: 0;\n }\n .g-md-1,\n .gx-md-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-md-1,\n .gy-md-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-md-2,\n .gx-md-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-md-2,\n .gy-md-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-md-3,\n .gx-md-3 {\n --bs-gutter-x: 1rem;\n }\n .g-md-3,\n .gy-md-3 {\n --bs-gutter-y: 1rem;\n }\n .g-md-4,\n .gx-md-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-md-4,\n .gy-md-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-md-5,\n .gx-md-5 {\n --bs-gutter-x: 3rem;\n }\n .g-md-5,\n .gy-md-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 992px) {\n .col-lg {\n flex: 1 0 0%;\n }\n .row-cols-lg-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-lg-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-lg-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-lg-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-lg-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-lg-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-lg-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-lg-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-lg-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-lg-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-lg-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-lg-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-lg-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-lg-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-lg-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-lg-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-lg-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-lg-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-lg-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-lg-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-lg-0 {\n margin-left: 0;\n }\n .offset-lg-1 {\n margin-left: 8.33333333%;\n }\n .offset-lg-2 {\n margin-left: 16.66666667%;\n }\n .offset-lg-3 {\n margin-left: 25%;\n }\n .offset-lg-4 {\n margin-left: 33.33333333%;\n }\n .offset-lg-5 {\n margin-left: 41.66666667%;\n }\n .offset-lg-6 {\n margin-left: 50%;\n }\n .offset-lg-7 {\n margin-left: 58.33333333%;\n }\n .offset-lg-8 {\n margin-left: 66.66666667%;\n }\n .offset-lg-9 {\n margin-left: 75%;\n }\n .offset-lg-10 {\n margin-left: 83.33333333%;\n }\n .offset-lg-11 {\n margin-left: 91.66666667%;\n }\n .g-lg-0,\n .gx-lg-0 {\n --bs-gutter-x: 0;\n }\n .g-lg-0,\n .gy-lg-0 {\n --bs-gutter-y: 0;\n }\n .g-lg-1,\n .gx-lg-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-lg-1,\n .gy-lg-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-lg-2,\n .gx-lg-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-lg-2,\n .gy-lg-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-lg-3,\n .gx-lg-3 {\n --bs-gutter-x: 1rem;\n }\n .g-lg-3,\n .gy-lg-3 {\n --bs-gutter-y: 1rem;\n }\n .g-lg-4,\n .gx-lg-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-lg-4,\n .gy-lg-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-lg-5,\n .gx-lg-5 {\n --bs-gutter-x: 3rem;\n }\n .g-lg-5,\n .gy-lg-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 1200px) {\n .col-xl {\n flex: 1 0 0%;\n }\n .row-cols-xl-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-xl-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-xl-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-xl-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-xl-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-xl-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-xl-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xl-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-xl-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-xl-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xl-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-xl-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-xl-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-xl-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-xl-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-xl-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-xl-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-xl-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-xl-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-xl-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-xl-0 {\n margin-left: 0;\n }\n .offset-xl-1 {\n margin-left: 8.33333333%;\n }\n .offset-xl-2 {\n margin-left: 16.66666667%;\n }\n .offset-xl-3 {\n margin-left: 25%;\n }\n .offset-xl-4 {\n margin-left: 33.33333333%;\n }\n .offset-xl-5 {\n margin-left: 41.66666667%;\n }\n .offset-xl-6 {\n margin-left: 50%;\n }\n .offset-xl-7 {\n margin-left: 58.33333333%;\n }\n .offset-xl-8 {\n margin-left: 66.66666667%;\n }\n .offset-xl-9 {\n margin-left: 75%;\n }\n .offset-xl-10 {\n margin-left: 83.33333333%;\n }\n .offset-xl-11 {\n margin-left: 91.66666667%;\n }\n .g-xl-0,\n .gx-xl-0 {\n --bs-gutter-x: 0;\n }\n .g-xl-0,\n .gy-xl-0 {\n --bs-gutter-y: 0;\n }\n .g-xl-1,\n .gx-xl-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-xl-1,\n .gy-xl-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-xl-2,\n .gx-xl-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-xl-2,\n .gy-xl-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-xl-3,\n .gx-xl-3 {\n --bs-gutter-x: 1rem;\n }\n .g-xl-3,\n .gy-xl-3 {\n --bs-gutter-y: 1rem;\n }\n .g-xl-4,\n .gx-xl-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-xl-4,\n .gy-xl-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-xl-5,\n .gx-xl-5 {\n --bs-gutter-x: 3rem;\n }\n .g-xl-5,\n .gy-xl-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 1400px) {\n .col-xxl {\n flex: 1 0 0%;\n }\n .row-cols-xxl-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-xxl-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-xxl-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-xxl-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-xxl-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-xxl-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-xxl-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xxl-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-xxl-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-xxl-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xxl-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-xxl-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-xxl-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-xxl-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-xxl-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-xxl-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-xxl-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-xxl-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-xxl-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-xxl-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-xxl-0 {\n margin-left: 0;\n }\n .offset-xxl-1 {\n margin-left: 8.33333333%;\n }\n .offset-xxl-2 {\n margin-left: 16.66666667%;\n }\n .offset-xxl-3 {\n margin-left: 25%;\n }\n .offset-xxl-4 {\n margin-left: 33.33333333%;\n }\n .offset-xxl-5 {\n margin-left: 41.66666667%;\n }\n .offset-xxl-6 {\n margin-left: 50%;\n }\n .offset-xxl-7 {\n margin-left: 58.33333333%;\n }\n .offset-xxl-8 {\n margin-left: 66.66666667%;\n }\n .offset-xxl-9 {\n margin-left: 75%;\n }\n .offset-xxl-10 {\n margin-left: 83.33333333%;\n }\n .offset-xxl-11 {\n margin-left: 91.66666667%;\n }\n .g-xxl-0,\n .gx-xxl-0 {\n --bs-gutter-x: 0;\n }\n .g-xxl-0,\n .gy-xxl-0 {\n --bs-gutter-y: 0;\n }\n .g-xxl-1,\n .gx-xxl-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-xxl-1,\n .gy-xxl-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-xxl-2,\n .gx-xxl-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-xxl-2,\n .gy-xxl-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-xxl-3,\n .gx-xxl-3 {\n --bs-gutter-x: 1rem;\n }\n .g-xxl-3,\n .gy-xxl-3 {\n --bs-gutter-y: 1rem;\n }\n .g-xxl-4,\n .gx-xxl-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-xxl-4,\n .gy-xxl-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-xxl-5,\n .gx-xxl-5 {\n --bs-gutter-x: 3rem;\n }\n .g-xxl-5,\n .gy-xxl-5 {\n --bs-gutter-y: 3rem;\n }\n}\n.d-inline {\n display: inline !important;\n}\n\n.d-inline-block {\n display: inline-block !important;\n}\n\n.d-block {\n display: block !important;\n}\n\n.d-grid {\n display: grid !important;\n}\n\n.d-inline-grid {\n display: inline-grid !important;\n}\n\n.d-table {\n display: table !important;\n}\n\n.d-table-row {\n display: table-row !important;\n}\n\n.d-table-cell {\n display: table-cell !important;\n}\n\n.d-flex {\n display: flex !important;\n}\n\n.d-inline-flex {\n display: inline-flex !important;\n}\n\n.d-none {\n display: none !important;\n}\n\n.flex-fill {\n flex: 1 1 auto !important;\n}\n\n.flex-row {\n flex-direction: row !important;\n}\n\n.flex-column {\n flex-direction: column !important;\n}\n\n.flex-row-reverse {\n flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n flex-direction: column-reverse !important;\n}\n\n.flex-grow-0 {\n flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n flex-shrink: 1 !important;\n}\n\n.flex-wrap {\n flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n}\n\n.justify-content-start {\n justify-content: flex-start !important;\n}\n\n.justify-content-end {\n justify-content: flex-end !important;\n}\n\n.justify-content-center {\n justify-content: center !important;\n}\n\n.justify-content-between {\n justify-content: space-between !important;\n}\n\n.justify-content-around {\n justify-content: space-around !important;\n}\n\n.justify-content-evenly {\n justify-content: space-evenly !important;\n}\n\n.align-items-start {\n align-items: flex-start !important;\n}\n\n.align-items-end {\n align-items: flex-end !important;\n}\n\n.align-items-center {\n align-items: center !important;\n}\n\n.align-items-baseline {\n align-items: baseline !important;\n}\n\n.align-items-stretch {\n align-items: stretch !important;\n}\n\n.align-content-start {\n align-content: flex-start !important;\n}\n\n.align-content-end {\n align-content: flex-end !important;\n}\n\n.align-content-center {\n align-content: center !important;\n}\n\n.align-content-between {\n align-content: space-between !important;\n}\n\n.align-content-around {\n align-content: space-around !important;\n}\n\n.align-content-stretch {\n align-content: stretch !important;\n}\n\n.align-self-auto {\n align-self: auto !important;\n}\n\n.align-self-start {\n align-self: flex-start !important;\n}\n\n.align-self-end {\n align-self: flex-end !important;\n}\n\n.align-self-center {\n align-self: center !important;\n}\n\n.align-self-baseline {\n align-self: baseline !important;\n}\n\n.align-self-stretch {\n align-self: stretch !important;\n}\n\n.order-first {\n order: -1 !important;\n}\n\n.order-0 {\n order: 0 !important;\n}\n\n.order-1 {\n order: 1 !important;\n}\n\n.order-2 {\n order: 2 !important;\n}\n\n.order-3 {\n order: 3 !important;\n}\n\n.order-4 {\n order: 4 !important;\n}\n\n.order-5 {\n order: 5 !important;\n}\n\n.order-last {\n order: 6 !important;\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mx-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n}\n\n.mx-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n}\n\n.mx-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n}\n\n.mx-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n}\n\n.mx-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n}\n\n.mx-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n}\n\n.mx-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n}\n\n.my-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n.my-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n}\n\n.my-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n}\n\n.my-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n}\n\n.my-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n}\n\n.my-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n}\n\n.my-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n}\n\n.mt-0 {\n margin-top: 0 !important;\n}\n\n.mt-1 {\n margin-top: 0.25rem !important;\n}\n\n.mt-2 {\n margin-top: 0.5rem !important;\n}\n\n.mt-3 {\n margin-top: 1rem !important;\n}\n\n.mt-4 {\n margin-top: 1.5rem !important;\n}\n\n.mt-5 {\n margin-top: 3rem !important;\n}\n\n.mt-auto {\n margin-top: auto !important;\n}\n\n.me-0 {\n margin-right: 0 !important;\n}\n\n.me-1 {\n margin-right: 0.25rem !important;\n}\n\n.me-2 {\n margin-right: 0.5rem !important;\n}\n\n.me-3 {\n margin-right: 1rem !important;\n}\n\n.me-4 {\n margin-right: 1.5rem !important;\n}\n\n.me-5 {\n margin-right: 3rem !important;\n}\n\n.me-auto {\n margin-right: auto !important;\n}\n\n.mb-0 {\n margin-bottom: 0 !important;\n}\n\n.mb-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.mb-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.mb-3 {\n margin-bottom: 1rem !important;\n}\n\n.mb-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.mb-5 {\n margin-bottom: 3rem !important;\n}\n\n.mb-auto {\n margin-bottom: auto !important;\n}\n\n.ms-0 {\n margin-left: 0 !important;\n}\n\n.ms-1 {\n margin-left: 0.25rem !important;\n}\n\n.ms-2 {\n margin-left: 0.5rem !important;\n}\n\n.ms-3 {\n margin-left: 1rem !important;\n}\n\n.ms-4 {\n margin-left: 1.5rem !important;\n}\n\n.ms-5 {\n margin-left: 3rem !important;\n}\n\n.ms-auto {\n margin-left: auto !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.px-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n}\n\n.px-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n}\n\n.px-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n}\n\n.px-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n}\n\n.px-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n}\n\n.px-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n}\n\n.py-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n}\n\n.py-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n}\n\n.py-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n}\n\n.py-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n}\n\n.py-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n}\n\n.py-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n}\n\n.pt-0 {\n padding-top: 0 !important;\n}\n\n.pt-1 {\n padding-top: 0.25rem !important;\n}\n\n.pt-2 {\n padding-top: 0.5rem !important;\n}\n\n.pt-3 {\n padding-top: 1rem !important;\n}\n\n.pt-4 {\n padding-top: 1.5rem !important;\n}\n\n.pt-5 {\n padding-top: 3rem !important;\n}\n\n.pe-0 {\n padding-right: 0 !important;\n}\n\n.pe-1 {\n padding-right: 0.25rem !important;\n}\n\n.pe-2 {\n padding-right: 0.5rem !important;\n}\n\n.pe-3 {\n padding-right: 1rem !important;\n}\n\n.pe-4 {\n padding-right: 1.5rem !important;\n}\n\n.pe-5 {\n padding-right: 3rem !important;\n}\n\n.pb-0 {\n padding-bottom: 0 !important;\n}\n\n.pb-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pb-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pb-3 {\n padding-bottom: 1rem !important;\n}\n\n.pb-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pb-5 {\n padding-bottom: 3rem !important;\n}\n\n.ps-0 {\n padding-left: 0 !important;\n}\n\n.ps-1 {\n padding-left: 0.25rem !important;\n}\n\n.ps-2 {\n padding-left: 0.5rem !important;\n}\n\n.ps-3 {\n padding-left: 1rem !important;\n}\n\n.ps-4 {\n padding-left: 1.5rem !important;\n}\n\n.ps-5 {\n padding-left: 3rem !important;\n}\n\n@media (min-width: 576px) {\n .d-sm-inline {\n display: inline !important;\n }\n .d-sm-inline-block {\n display: inline-block !important;\n }\n .d-sm-block {\n display: block !important;\n }\n .d-sm-grid {\n display: grid !important;\n }\n .d-sm-inline-grid {\n display: inline-grid !important;\n }\n .d-sm-table {\n display: table !important;\n }\n .d-sm-table-row {\n display: table-row !important;\n }\n .d-sm-table-cell {\n display: table-cell !important;\n }\n .d-sm-flex {\n display: flex !important;\n }\n .d-sm-inline-flex {\n display: inline-flex !important;\n }\n .d-sm-none {\n display: none !important;\n }\n .flex-sm-fill {\n flex: 1 1 auto !important;\n }\n .flex-sm-row {\n flex-direction: row !important;\n }\n .flex-sm-column {\n flex-direction: column !important;\n }\n .flex-sm-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-sm-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-sm-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-sm-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-sm-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-sm-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-sm-wrap {\n flex-wrap: wrap !important;\n }\n .flex-sm-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-sm-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-sm-start {\n justify-content: flex-start !important;\n }\n .justify-content-sm-end {\n justify-content: flex-end !important;\n }\n .justify-content-sm-center {\n justify-content: center !important;\n }\n .justify-content-sm-between {\n justify-content: space-between !important;\n }\n .justify-content-sm-around {\n justify-content: space-around !important;\n }\n .justify-content-sm-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-sm-start {\n align-items: flex-start !important;\n }\n .align-items-sm-end {\n align-items: flex-end !important;\n }\n .align-items-sm-center {\n align-items: center !important;\n }\n .align-items-sm-baseline {\n align-items: baseline !important;\n }\n .align-items-sm-stretch {\n align-items: stretch !important;\n }\n .align-content-sm-start {\n align-content: flex-start !important;\n }\n .align-content-sm-end {\n align-content: flex-end !important;\n }\n .align-content-sm-center {\n align-content: center !important;\n }\n .align-content-sm-between {\n align-content: space-between !important;\n }\n .align-content-sm-around {\n align-content: space-around !important;\n }\n .align-content-sm-stretch {\n align-content: stretch !important;\n }\n .align-self-sm-auto {\n align-self: auto !important;\n }\n .align-self-sm-start {\n align-self: flex-start !important;\n }\n .align-self-sm-end {\n align-self: flex-end !important;\n }\n .align-self-sm-center {\n align-self: center !important;\n }\n .align-self-sm-baseline {\n align-self: baseline !important;\n }\n .align-self-sm-stretch {\n align-self: stretch !important;\n }\n .order-sm-first {\n order: -1 !important;\n }\n .order-sm-0 {\n order: 0 !important;\n }\n .order-sm-1 {\n order: 1 !important;\n }\n .order-sm-2 {\n order: 2 !important;\n }\n .order-sm-3 {\n order: 3 !important;\n }\n .order-sm-4 {\n order: 4 !important;\n }\n .order-sm-5 {\n order: 5 !important;\n }\n .order-sm-last {\n order: 6 !important;\n }\n .m-sm-0 {\n margin: 0 !important;\n }\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n .m-sm-3 {\n margin: 1rem !important;\n }\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n .m-sm-5 {\n margin: 3rem !important;\n }\n .m-sm-auto {\n margin: auto !important;\n }\n .mx-sm-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-sm-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-sm-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-sm-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-sm-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-sm-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-sm-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-sm-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-sm-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-sm-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-sm-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-sm-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-sm-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-sm-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-sm-0 {\n margin-top: 0 !important;\n }\n .mt-sm-1 {\n margin-top: 0.25rem !important;\n }\n .mt-sm-2 {\n margin-top: 0.5rem !important;\n }\n .mt-sm-3 {\n margin-top: 1rem !important;\n }\n .mt-sm-4 {\n margin-top: 1.5rem !important;\n }\n .mt-sm-5 {\n margin-top: 3rem !important;\n }\n .mt-sm-auto {\n margin-top: auto !important;\n }\n .me-sm-0 {\n margin-right: 0 !important;\n }\n .me-sm-1 {\n margin-right: 0.25rem !important;\n }\n .me-sm-2 {\n margin-right: 0.5rem !important;\n }\n .me-sm-3 {\n margin-right: 1rem !important;\n }\n .me-sm-4 {\n margin-right: 1.5rem !important;\n }\n .me-sm-5 {\n margin-right: 3rem !important;\n }\n .me-sm-auto {\n margin-right: auto !important;\n }\n .mb-sm-0 {\n margin-bottom: 0 !important;\n }\n .mb-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-sm-3 {\n margin-bottom: 1rem !important;\n }\n .mb-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-sm-5 {\n margin-bottom: 3rem !important;\n }\n .mb-sm-auto {\n margin-bottom: auto !important;\n }\n .ms-sm-0 {\n margin-left: 0 !important;\n }\n .ms-sm-1 {\n margin-left: 0.25rem !important;\n }\n .ms-sm-2 {\n margin-left: 0.5rem !important;\n }\n .ms-sm-3 {\n margin-left: 1rem !important;\n }\n .ms-sm-4 {\n margin-left: 1.5rem !important;\n }\n .ms-sm-5 {\n margin-left: 3rem !important;\n }\n .ms-sm-auto {\n margin-left: auto !important;\n }\n .p-sm-0 {\n padding: 0 !important;\n }\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n .p-sm-3 {\n padding: 1rem !important;\n }\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n .p-sm-5 {\n padding: 3rem !important;\n }\n .px-sm-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-sm-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-sm-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-sm-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-sm-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-sm-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-sm-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-sm-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-sm-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-sm-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-sm-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-sm-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-sm-0 {\n padding-top: 0 !important;\n }\n .pt-sm-1 {\n padding-top: 0.25rem !important;\n }\n .pt-sm-2 {\n padding-top: 0.5rem !important;\n }\n .pt-sm-3 {\n padding-top: 1rem !important;\n }\n .pt-sm-4 {\n padding-top: 1.5rem !important;\n }\n .pt-sm-5 {\n padding-top: 3rem !important;\n }\n .pe-sm-0 {\n padding-right: 0 !important;\n }\n .pe-sm-1 {\n padding-right: 0.25rem !important;\n }\n .pe-sm-2 {\n padding-right: 0.5rem !important;\n }\n .pe-sm-3 {\n padding-right: 1rem !important;\n }\n .pe-sm-4 {\n padding-right: 1.5rem !important;\n }\n .pe-sm-5 {\n padding-right: 3rem !important;\n }\n .pb-sm-0 {\n padding-bottom: 0 !important;\n }\n .pb-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-sm-3 {\n padding-bottom: 1rem !important;\n }\n .pb-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-sm-5 {\n padding-bottom: 3rem !important;\n }\n .ps-sm-0 {\n padding-left: 0 !important;\n }\n .ps-sm-1 {\n padding-left: 0.25rem !important;\n }\n .ps-sm-2 {\n padding-left: 0.5rem !important;\n }\n .ps-sm-3 {\n padding-left: 1rem !important;\n }\n .ps-sm-4 {\n padding-left: 1.5rem !important;\n }\n .ps-sm-5 {\n padding-left: 3rem !important;\n }\n}\n@media (min-width: 768px) {\n .d-md-inline {\n display: inline !important;\n }\n .d-md-inline-block {\n display: inline-block !important;\n }\n .d-md-block {\n display: block !important;\n }\n .d-md-grid {\n display: grid !important;\n }\n .d-md-inline-grid {\n display: inline-grid !important;\n }\n .d-md-table {\n display: table !important;\n }\n .d-md-table-row {\n display: table-row !important;\n }\n .d-md-table-cell {\n display: table-cell !important;\n }\n .d-md-flex {\n display: flex !important;\n }\n .d-md-inline-flex {\n display: inline-flex !important;\n }\n .d-md-none {\n display: none !important;\n }\n .flex-md-fill {\n flex: 1 1 auto !important;\n }\n .flex-md-row {\n flex-direction: row !important;\n }\n .flex-md-column {\n flex-direction: column !important;\n }\n .flex-md-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-md-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-md-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-md-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-md-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-md-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-md-wrap {\n flex-wrap: wrap !important;\n }\n .flex-md-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-md-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-md-start {\n justify-content: flex-start !important;\n }\n .justify-content-md-end {\n justify-content: flex-end !important;\n }\n .justify-content-md-center {\n justify-content: center !important;\n }\n .justify-content-md-between {\n justify-content: space-between !important;\n }\n .justify-content-md-around {\n justify-content: space-around !important;\n }\n .justify-content-md-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-md-start {\n align-items: flex-start !important;\n }\n .align-items-md-end {\n align-items: flex-end !important;\n }\n .align-items-md-center {\n align-items: center !important;\n }\n .align-items-md-baseline {\n align-items: baseline !important;\n }\n .align-items-md-stretch {\n align-items: stretch !important;\n }\n .align-content-md-start {\n align-content: flex-start !important;\n }\n .align-content-md-end {\n align-content: flex-end !important;\n }\n .align-content-md-center {\n align-content: center !important;\n }\n .align-content-md-between {\n align-content: space-between !important;\n }\n .align-content-md-around {\n align-content: space-around !important;\n }\n .align-content-md-stretch {\n align-content: stretch !important;\n }\n .align-self-md-auto {\n align-self: auto !important;\n }\n .align-self-md-start {\n align-self: flex-start !important;\n }\n .align-self-md-end {\n align-self: flex-end !important;\n }\n .align-self-md-center {\n align-self: center !important;\n }\n .align-self-md-baseline {\n align-self: baseline !important;\n }\n .align-self-md-stretch {\n align-self: stretch !important;\n }\n .order-md-first {\n order: -1 !important;\n }\n .order-md-0 {\n order: 0 !important;\n }\n .order-md-1 {\n order: 1 !important;\n }\n .order-md-2 {\n order: 2 !important;\n }\n .order-md-3 {\n order: 3 !important;\n }\n .order-md-4 {\n order: 4 !important;\n }\n .order-md-5 {\n order: 5 !important;\n }\n .order-md-last {\n order: 6 !important;\n }\n .m-md-0 {\n margin: 0 !important;\n }\n .m-md-1 {\n margin: 0.25rem !important;\n }\n .m-md-2 {\n margin: 0.5rem !important;\n }\n .m-md-3 {\n margin: 1rem !important;\n }\n .m-md-4 {\n margin: 1.5rem !important;\n }\n .m-md-5 {\n margin: 3rem !important;\n }\n .m-md-auto {\n margin: auto !important;\n }\n .mx-md-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-md-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-md-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-md-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-md-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-md-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-md-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-md-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-md-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-md-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-md-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-md-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-md-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-md-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-md-0 {\n margin-top: 0 !important;\n }\n .mt-md-1 {\n margin-top: 0.25rem !important;\n }\n .mt-md-2 {\n margin-top: 0.5rem !important;\n }\n .mt-md-3 {\n margin-top: 1rem !important;\n }\n .mt-md-4 {\n margin-top: 1.5rem !important;\n }\n .mt-md-5 {\n margin-top: 3rem !important;\n }\n .mt-md-auto {\n margin-top: auto !important;\n }\n .me-md-0 {\n margin-right: 0 !important;\n }\n .me-md-1 {\n margin-right: 0.25rem !important;\n }\n .me-md-2 {\n margin-right: 0.5rem !important;\n }\n .me-md-3 {\n margin-right: 1rem !important;\n }\n .me-md-4 {\n margin-right: 1.5rem !important;\n }\n .me-md-5 {\n margin-right: 3rem !important;\n }\n .me-md-auto {\n margin-right: auto !important;\n }\n .mb-md-0 {\n margin-bottom: 0 !important;\n }\n .mb-md-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-md-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-md-3 {\n margin-bottom: 1rem !important;\n }\n .mb-md-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-md-5 {\n margin-bottom: 3rem !important;\n }\n .mb-md-auto {\n margin-bottom: auto !important;\n }\n .ms-md-0 {\n margin-left: 0 !important;\n }\n .ms-md-1 {\n margin-left: 0.25rem !important;\n }\n .ms-md-2 {\n margin-left: 0.5rem !important;\n }\n .ms-md-3 {\n margin-left: 1rem !important;\n }\n .ms-md-4 {\n margin-left: 1.5rem !important;\n }\n .ms-md-5 {\n margin-left: 3rem !important;\n }\n .ms-md-auto {\n margin-left: auto !important;\n }\n .p-md-0 {\n padding: 0 !important;\n }\n .p-md-1 {\n padding: 0.25rem !important;\n }\n .p-md-2 {\n padding: 0.5rem !important;\n }\n .p-md-3 {\n padding: 1rem !important;\n }\n .p-md-4 {\n padding: 1.5rem !important;\n }\n .p-md-5 {\n padding: 3rem !important;\n }\n .px-md-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-md-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-md-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-md-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-md-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-md-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-md-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-md-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-md-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-md-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-md-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-md-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-md-0 {\n padding-top: 0 !important;\n }\n .pt-md-1 {\n padding-top: 0.25rem !important;\n }\n .pt-md-2 {\n padding-top: 0.5rem !important;\n }\n .pt-md-3 {\n padding-top: 1rem !important;\n }\n .pt-md-4 {\n padding-top: 1.5rem !important;\n }\n .pt-md-5 {\n padding-top: 3rem !important;\n }\n .pe-md-0 {\n padding-right: 0 !important;\n }\n .pe-md-1 {\n padding-right: 0.25rem !important;\n }\n .pe-md-2 {\n padding-right: 0.5rem !important;\n }\n .pe-md-3 {\n padding-right: 1rem !important;\n }\n .pe-md-4 {\n padding-right: 1.5rem !important;\n }\n .pe-md-5 {\n padding-right: 3rem !important;\n }\n .pb-md-0 {\n padding-bottom: 0 !important;\n }\n .pb-md-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-md-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-md-3 {\n padding-bottom: 1rem !important;\n }\n .pb-md-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-md-5 {\n padding-bottom: 3rem !important;\n }\n .ps-md-0 {\n padding-left: 0 !important;\n }\n .ps-md-1 {\n padding-left: 0.25rem !important;\n }\n .ps-md-2 {\n padding-left: 0.5rem !important;\n }\n .ps-md-3 {\n padding-left: 1rem !important;\n }\n .ps-md-4 {\n padding-left: 1.5rem !important;\n }\n .ps-md-5 {\n padding-left: 3rem !important;\n }\n}\n@media (min-width: 992px) {\n .d-lg-inline {\n display: inline !important;\n }\n .d-lg-inline-block {\n display: inline-block !important;\n }\n .d-lg-block {\n display: block !important;\n }\n .d-lg-grid {\n display: grid !important;\n }\n .d-lg-inline-grid {\n display: inline-grid !important;\n }\n .d-lg-table {\n display: table !important;\n }\n .d-lg-table-row {\n display: table-row !important;\n }\n .d-lg-table-cell {\n display: table-cell !important;\n }\n .d-lg-flex {\n display: flex !important;\n }\n .d-lg-inline-flex {\n display: inline-flex !important;\n }\n .d-lg-none {\n display: none !important;\n }\n .flex-lg-fill {\n flex: 1 1 auto !important;\n }\n .flex-lg-row {\n flex-direction: row !important;\n }\n .flex-lg-column {\n flex-direction: column !important;\n }\n .flex-lg-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-lg-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-lg-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-lg-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-lg-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-lg-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-lg-wrap {\n flex-wrap: wrap !important;\n }\n .flex-lg-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-lg-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-lg-start {\n justify-content: flex-start !important;\n }\n .justify-content-lg-end {\n justify-content: flex-end !important;\n }\n .justify-content-lg-center {\n justify-content: center !important;\n }\n .justify-content-lg-between {\n justify-content: space-between !important;\n }\n .justify-content-lg-around {\n justify-content: space-around !important;\n }\n .justify-content-lg-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-lg-start {\n align-items: flex-start !important;\n }\n .align-items-lg-end {\n align-items: flex-end !important;\n }\n .align-items-lg-center {\n align-items: center !important;\n }\n .align-items-lg-baseline {\n align-items: baseline !important;\n }\n .align-items-lg-stretch {\n align-items: stretch !important;\n }\n .align-content-lg-start {\n align-content: flex-start !important;\n }\n .align-content-lg-end {\n align-content: flex-end !important;\n }\n .align-content-lg-center {\n align-content: center !important;\n }\n .align-content-lg-between {\n align-content: space-between !important;\n }\n .align-content-lg-around {\n align-content: space-around !important;\n }\n .align-content-lg-stretch {\n align-content: stretch !important;\n }\n .align-self-lg-auto {\n align-self: auto !important;\n }\n .align-self-lg-start {\n align-self: flex-start !important;\n }\n .align-self-lg-end {\n align-self: flex-end !important;\n }\n .align-self-lg-center {\n align-self: center !important;\n }\n .align-self-lg-baseline {\n align-self: baseline !important;\n }\n .align-self-lg-stretch {\n align-self: stretch !important;\n }\n .order-lg-first {\n order: -1 !important;\n }\n .order-lg-0 {\n order: 0 !important;\n }\n .order-lg-1 {\n order: 1 !important;\n }\n .order-lg-2 {\n order: 2 !important;\n }\n .order-lg-3 {\n order: 3 !important;\n }\n .order-lg-4 {\n order: 4 !important;\n }\n .order-lg-5 {\n order: 5 !important;\n }\n .order-lg-last {\n order: 6 !important;\n }\n .m-lg-0 {\n margin: 0 !important;\n }\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n .m-lg-3 {\n margin: 1rem !important;\n }\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n .m-lg-5 {\n margin: 3rem !important;\n }\n .m-lg-auto {\n margin: auto !important;\n }\n .mx-lg-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-lg-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-lg-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-lg-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-lg-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-lg-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-lg-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-lg-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-lg-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-lg-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-lg-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-lg-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-lg-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-lg-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-lg-0 {\n margin-top: 0 !important;\n }\n .mt-lg-1 {\n margin-top: 0.25rem !important;\n }\n .mt-lg-2 {\n margin-top: 0.5rem !important;\n }\n .mt-lg-3 {\n margin-top: 1rem !important;\n }\n .mt-lg-4 {\n margin-top: 1.5rem !important;\n }\n .mt-lg-5 {\n margin-top: 3rem !important;\n }\n .mt-lg-auto {\n margin-top: auto !important;\n }\n .me-lg-0 {\n margin-right: 0 !important;\n }\n .me-lg-1 {\n margin-right: 0.25rem !important;\n }\n .me-lg-2 {\n margin-right: 0.5rem !important;\n }\n .me-lg-3 {\n margin-right: 1rem !important;\n }\n .me-lg-4 {\n margin-right: 1.5rem !important;\n }\n .me-lg-5 {\n margin-right: 3rem !important;\n }\n .me-lg-auto {\n margin-right: auto !important;\n }\n .mb-lg-0 {\n margin-bottom: 0 !important;\n }\n .mb-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-lg-3 {\n margin-bottom: 1rem !important;\n }\n .mb-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-lg-5 {\n margin-bottom: 3rem !important;\n }\n .mb-lg-auto {\n margin-bottom: auto !important;\n }\n .ms-lg-0 {\n margin-left: 0 !important;\n }\n .ms-lg-1 {\n margin-left: 0.25rem !important;\n }\n .ms-lg-2 {\n margin-left: 0.5rem !important;\n }\n .ms-lg-3 {\n margin-left: 1rem !important;\n }\n .ms-lg-4 {\n margin-left: 1.5rem !important;\n }\n .ms-lg-5 {\n margin-left: 3rem !important;\n }\n .ms-lg-auto {\n margin-left: auto !important;\n }\n .p-lg-0 {\n padding: 0 !important;\n }\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n .p-lg-3 {\n padding: 1rem !important;\n }\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n .p-lg-5 {\n padding: 3rem !important;\n }\n .px-lg-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-lg-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-lg-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-lg-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-lg-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-lg-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-lg-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-lg-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-lg-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-lg-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-lg-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-lg-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-lg-0 {\n padding-top: 0 !important;\n }\n .pt-lg-1 {\n padding-top: 0.25rem !important;\n }\n .pt-lg-2 {\n padding-top: 0.5rem !important;\n }\n .pt-lg-3 {\n padding-top: 1rem !important;\n }\n .pt-lg-4 {\n padding-top: 1.5rem !important;\n }\n .pt-lg-5 {\n padding-top: 3rem !important;\n }\n .pe-lg-0 {\n padding-right: 0 !important;\n }\n .pe-lg-1 {\n padding-right: 0.25rem !important;\n }\n .pe-lg-2 {\n padding-right: 0.5rem !important;\n }\n .pe-lg-3 {\n padding-right: 1rem !important;\n }\n .pe-lg-4 {\n padding-right: 1.5rem !important;\n }\n .pe-lg-5 {\n padding-right: 3rem !important;\n }\n .pb-lg-0 {\n padding-bottom: 0 !important;\n }\n .pb-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-lg-3 {\n padding-bottom: 1rem !important;\n }\n .pb-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-lg-5 {\n padding-bottom: 3rem !important;\n }\n .ps-lg-0 {\n padding-left: 0 !important;\n }\n .ps-lg-1 {\n padding-left: 0.25rem !important;\n }\n .ps-lg-2 {\n padding-left: 0.5rem !important;\n }\n .ps-lg-3 {\n padding-left: 1rem !important;\n }\n .ps-lg-4 {\n padding-left: 1.5rem !important;\n }\n .ps-lg-5 {\n padding-left: 3rem !important;\n }\n}\n@media (min-width: 1200px) {\n .d-xl-inline {\n display: inline !important;\n }\n .d-xl-inline-block {\n display: inline-block !important;\n }\n .d-xl-block {\n display: block !important;\n }\n .d-xl-grid {\n display: grid !important;\n }\n .d-xl-inline-grid {\n display: inline-grid !important;\n }\n .d-xl-table {\n display: table !important;\n }\n .d-xl-table-row {\n display: table-row !important;\n }\n .d-xl-table-cell {\n display: table-cell !important;\n }\n .d-xl-flex {\n display: flex !important;\n }\n .d-xl-inline-flex {\n display: inline-flex !important;\n }\n .d-xl-none {\n display: none !important;\n }\n .flex-xl-fill {\n flex: 1 1 auto !important;\n }\n .flex-xl-row {\n flex-direction: row !important;\n }\n .flex-xl-column {\n flex-direction: column !important;\n }\n .flex-xl-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-xl-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-xl-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-xl-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-xl-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-xl-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-xl-wrap {\n flex-wrap: wrap !important;\n }\n .flex-xl-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-xl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-xl-start {\n justify-content: flex-start !important;\n }\n .justify-content-xl-end {\n justify-content: flex-end !important;\n }\n .justify-content-xl-center {\n justify-content: center !important;\n }\n .justify-content-xl-between {\n justify-content: space-between !important;\n }\n .justify-content-xl-around {\n justify-content: space-around !important;\n }\n .justify-content-xl-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-xl-start {\n align-items: flex-start !important;\n }\n .align-items-xl-end {\n align-items: flex-end !important;\n }\n .align-items-xl-center {\n align-items: center !important;\n }\n .align-items-xl-baseline {\n align-items: baseline !important;\n }\n .align-items-xl-stretch {\n align-items: stretch !important;\n }\n .align-content-xl-start {\n align-content: flex-start !important;\n }\n .align-content-xl-end {\n align-content: flex-end !important;\n }\n .align-content-xl-center {\n align-content: center !important;\n }\n .align-content-xl-between {\n align-content: space-between !important;\n }\n .align-content-xl-around {\n align-content: space-around !important;\n }\n .align-content-xl-stretch {\n align-content: stretch !important;\n }\n .align-self-xl-auto {\n align-self: auto !important;\n }\n .align-self-xl-start {\n align-self: flex-start !important;\n }\n .align-self-xl-end {\n align-self: flex-end !important;\n }\n .align-self-xl-center {\n align-self: center !important;\n }\n .align-self-xl-baseline {\n align-self: baseline !important;\n }\n .align-self-xl-stretch {\n align-self: stretch !important;\n }\n .order-xl-first {\n order: -1 !important;\n }\n .order-xl-0 {\n order: 0 !important;\n }\n .order-xl-1 {\n order: 1 !important;\n }\n .order-xl-2 {\n order: 2 !important;\n }\n .order-xl-3 {\n order: 3 !important;\n }\n .order-xl-4 {\n order: 4 !important;\n }\n .order-xl-5 {\n order: 5 !important;\n }\n .order-xl-last {\n order: 6 !important;\n }\n .m-xl-0 {\n margin: 0 !important;\n }\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n .m-xl-3 {\n margin: 1rem !important;\n }\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n .m-xl-5 {\n margin: 3rem !important;\n }\n .m-xl-auto {\n margin: auto !important;\n }\n .mx-xl-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-xl-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-xl-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-xl-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-xl-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-xl-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-xl-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-xl-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-xl-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-xl-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-xl-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-xl-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-xl-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-xl-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-xl-0 {\n margin-top: 0 !important;\n }\n .mt-xl-1 {\n margin-top: 0.25rem !important;\n }\n .mt-xl-2 {\n margin-top: 0.5rem !important;\n }\n .mt-xl-3 {\n margin-top: 1rem !important;\n }\n .mt-xl-4 {\n margin-top: 1.5rem !important;\n }\n .mt-xl-5 {\n margin-top: 3rem !important;\n }\n .mt-xl-auto {\n margin-top: auto !important;\n }\n .me-xl-0 {\n margin-right: 0 !important;\n }\n .me-xl-1 {\n margin-right: 0.25rem !important;\n }\n .me-xl-2 {\n margin-right: 0.5rem !important;\n }\n .me-xl-3 {\n margin-right: 1rem !important;\n }\n .me-xl-4 {\n margin-right: 1.5rem !important;\n }\n .me-xl-5 {\n margin-right: 3rem !important;\n }\n .me-xl-auto {\n margin-right: auto !important;\n }\n .mb-xl-0 {\n margin-bottom: 0 !important;\n }\n .mb-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-xl-3 {\n margin-bottom: 1rem !important;\n }\n .mb-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-xl-5 {\n margin-bottom: 3rem !important;\n }\n .mb-xl-auto {\n margin-bottom: auto !important;\n }\n .ms-xl-0 {\n margin-left: 0 !important;\n }\n .ms-xl-1 {\n margin-left: 0.25rem !important;\n }\n .ms-xl-2 {\n margin-left: 0.5rem !important;\n }\n .ms-xl-3 {\n margin-left: 1rem !important;\n }\n .ms-xl-4 {\n margin-left: 1.5rem !important;\n }\n .ms-xl-5 {\n margin-left: 3rem !important;\n }\n .ms-xl-auto {\n margin-left: auto !important;\n }\n .p-xl-0 {\n padding: 0 !important;\n }\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n .p-xl-3 {\n padding: 1rem !important;\n }\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n .p-xl-5 {\n padding: 3rem !important;\n }\n .px-xl-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-xl-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-xl-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-xl-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-xl-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-xl-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-xl-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-xl-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-xl-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-xl-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-xl-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-xl-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-xl-0 {\n padding-top: 0 !important;\n }\n .pt-xl-1 {\n padding-top: 0.25rem !important;\n }\n .pt-xl-2 {\n padding-top: 0.5rem !important;\n }\n .pt-xl-3 {\n padding-top: 1rem !important;\n }\n .pt-xl-4 {\n padding-top: 1.5rem !important;\n }\n .pt-xl-5 {\n padding-top: 3rem !important;\n }\n .pe-xl-0 {\n padding-right: 0 !important;\n }\n .pe-xl-1 {\n padding-right: 0.25rem !important;\n }\n .pe-xl-2 {\n padding-right: 0.5rem !important;\n }\n .pe-xl-3 {\n padding-right: 1rem !important;\n }\n .pe-xl-4 {\n padding-right: 1.5rem !important;\n }\n .pe-xl-5 {\n padding-right: 3rem !important;\n }\n .pb-xl-0 {\n padding-bottom: 0 !important;\n }\n .pb-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-xl-3 {\n padding-bottom: 1rem !important;\n }\n .pb-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-xl-5 {\n padding-bottom: 3rem !important;\n }\n .ps-xl-0 {\n padding-left: 0 !important;\n }\n .ps-xl-1 {\n padding-left: 0.25rem !important;\n }\n .ps-xl-2 {\n padding-left: 0.5rem !important;\n }\n .ps-xl-3 {\n padding-left: 1rem !important;\n }\n .ps-xl-4 {\n padding-left: 1.5rem !important;\n }\n .ps-xl-5 {\n padding-left: 3rem !important;\n }\n}\n@media (min-width: 1400px) {\n .d-xxl-inline {\n display: inline !important;\n }\n .d-xxl-inline-block {\n display: inline-block !important;\n }\n .d-xxl-block {\n display: block !important;\n }\n .d-xxl-grid {\n display: grid !important;\n }\n .d-xxl-inline-grid {\n display: inline-grid !important;\n }\n .d-xxl-table {\n display: table !important;\n }\n .d-xxl-table-row {\n display: table-row !important;\n }\n .d-xxl-table-cell {\n display: table-cell !important;\n }\n .d-xxl-flex {\n display: flex !important;\n }\n .d-xxl-inline-flex {\n display: inline-flex !important;\n }\n .d-xxl-none {\n display: none !important;\n }\n .flex-xxl-fill {\n flex: 1 1 auto !important;\n }\n .flex-xxl-row {\n flex-direction: row !important;\n }\n .flex-xxl-column {\n flex-direction: column !important;\n }\n .flex-xxl-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-xxl-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-xxl-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-xxl-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-xxl-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-xxl-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-xxl-wrap {\n flex-wrap: wrap !important;\n }\n .flex-xxl-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-xxl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-xxl-start {\n justify-content: flex-start !important;\n }\n .justify-content-xxl-end {\n justify-content: flex-end !important;\n }\n .justify-content-xxl-center {\n justify-content: center !important;\n }\n .justify-content-xxl-between {\n justify-content: space-between !important;\n }\n .justify-content-xxl-around {\n justify-content: space-around !important;\n }\n .justify-content-xxl-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-xxl-start {\n align-items: flex-start !important;\n }\n .align-items-xxl-end {\n align-items: flex-end !important;\n }\n .align-items-xxl-center {\n align-items: center !important;\n }\n .align-items-xxl-baseline {\n align-items: baseline !important;\n }\n .align-items-xxl-stretch {\n align-items: stretch !important;\n }\n .align-content-xxl-start {\n align-content: flex-start !important;\n }\n .align-content-xxl-end {\n align-content: flex-end !important;\n }\n .align-content-xxl-center {\n align-content: center !important;\n }\n .align-content-xxl-between {\n align-content: space-between !important;\n }\n .align-content-xxl-around {\n align-content: space-around !important;\n }\n .align-content-xxl-stretch {\n align-content: stretch !important;\n }\n .align-self-xxl-auto {\n align-self: auto !important;\n }\n .align-self-xxl-start {\n align-self: flex-start !important;\n }\n .align-self-xxl-end {\n align-self: flex-end !important;\n }\n .align-self-xxl-center {\n align-self: center !important;\n }\n .align-self-xxl-baseline {\n align-self: baseline !important;\n }\n .align-self-xxl-stretch {\n align-self: stretch !important;\n }\n .order-xxl-first {\n order: -1 !important;\n }\n .order-xxl-0 {\n order: 0 !important;\n }\n .order-xxl-1 {\n order: 1 !important;\n }\n .order-xxl-2 {\n order: 2 !important;\n }\n .order-xxl-3 {\n order: 3 !important;\n }\n .order-xxl-4 {\n order: 4 !important;\n }\n .order-xxl-5 {\n order: 5 !important;\n }\n .order-xxl-last {\n order: 6 !important;\n }\n .m-xxl-0 {\n margin: 0 !important;\n }\n .m-xxl-1 {\n margin: 0.25rem !important;\n }\n .m-xxl-2 {\n margin: 0.5rem !important;\n }\n .m-xxl-3 {\n margin: 1rem !important;\n }\n .m-xxl-4 {\n margin: 1.5rem !important;\n }\n .m-xxl-5 {\n margin: 3rem !important;\n }\n .m-xxl-auto {\n margin: auto !important;\n }\n .mx-xxl-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-xxl-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-xxl-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-xxl-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-xxl-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-xxl-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-xxl-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-xxl-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-xxl-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-xxl-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-xxl-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-xxl-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-xxl-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-xxl-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-xxl-0 {\n margin-top: 0 !important;\n }\n .mt-xxl-1 {\n margin-top: 0.25rem !important;\n }\n .mt-xxl-2 {\n margin-top: 0.5rem !important;\n }\n .mt-xxl-3 {\n margin-top: 1rem !important;\n }\n .mt-xxl-4 {\n margin-top: 1.5rem !important;\n }\n .mt-xxl-5 {\n margin-top: 3rem !important;\n }\n .mt-xxl-auto {\n margin-top: auto !important;\n }\n .me-xxl-0 {\n margin-right: 0 !important;\n }\n .me-xxl-1 {\n margin-right: 0.25rem !important;\n }\n .me-xxl-2 {\n margin-right: 0.5rem !important;\n }\n .me-xxl-3 {\n margin-right: 1rem !important;\n }\n .me-xxl-4 {\n margin-right: 1.5rem !important;\n }\n .me-xxl-5 {\n margin-right: 3rem !important;\n }\n .me-xxl-auto {\n margin-right: auto !important;\n }\n .mb-xxl-0 {\n margin-bottom: 0 !important;\n }\n .mb-xxl-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-xxl-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-xxl-3 {\n margin-bottom: 1rem !important;\n }\n .mb-xxl-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-xxl-5 {\n margin-bottom: 3rem !important;\n }\n .mb-xxl-auto {\n margin-bottom: auto !important;\n }\n .ms-xxl-0 {\n margin-left: 0 !important;\n }\n .ms-xxl-1 {\n margin-left: 0.25rem !important;\n }\n .ms-xxl-2 {\n margin-left: 0.5rem !important;\n }\n .ms-xxl-3 {\n margin-left: 1rem !important;\n }\n .ms-xxl-4 {\n margin-left: 1.5rem !important;\n }\n .ms-xxl-5 {\n margin-left: 3rem !important;\n }\n .ms-xxl-auto {\n margin-left: auto !important;\n }\n .p-xxl-0 {\n padding: 0 !important;\n }\n .p-xxl-1 {\n padding: 0.25rem !important;\n }\n .p-xxl-2 {\n padding: 0.5rem !important;\n }\n .p-xxl-3 {\n padding: 1rem !important;\n }\n .p-xxl-4 {\n padding: 1.5rem !important;\n }\n .p-xxl-5 {\n padding: 3rem !important;\n }\n .px-xxl-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-xxl-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-xxl-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-xxl-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-xxl-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-xxl-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-xxl-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-xxl-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-xxl-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-xxl-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-xxl-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-xxl-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-xxl-0 {\n padding-top: 0 !important;\n }\n .pt-xxl-1 {\n padding-top: 0.25rem !important;\n }\n .pt-xxl-2 {\n padding-top: 0.5rem !important;\n }\n .pt-xxl-3 {\n padding-top: 1rem !important;\n }\n .pt-xxl-4 {\n padding-top: 1.5rem !important;\n }\n .pt-xxl-5 {\n padding-top: 3rem !important;\n }\n .pe-xxl-0 {\n padding-right: 0 !important;\n }\n .pe-xxl-1 {\n padding-right: 0.25rem !important;\n }\n .pe-xxl-2 {\n padding-right: 0.5rem !important;\n }\n .pe-xxl-3 {\n padding-right: 1rem !important;\n }\n .pe-xxl-4 {\n padding-right: 1.5rem !important;\n }\n .pe-xxl-5 {\n padding-right: 3rem !important;\n }\n .pb-xxl-0 {\n padding-bottom: 0 !important;\n }\n .pb-xxl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-xxl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-xxl-3 {\n padding-bottom: 1rem !important;\n }\n .pb-xxl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-xxl-5 {\n padding-bottom: 3rem !important;\n }\n .ps-xxl-0 {\n padding-left: 0 !important;\n }\n .ps-xxl-1 {\n padding-left: 0.25rem !important;\n }\n .ps-xxl-2 {\n padding-left: 0.5rem !important;\n }\n .ps-xxl-3 {\n padding-left: 1rem !important;\n }\n .ps-xxl-4 {\n padding-left: 1.5rem !important;\n }\n .ps-xxl-5 {\n padding-left: 3rem !important;\n }\n}\n@media print {\n .d-print-inline {\n display: inline !important;\n }\n .d-print-inline-block {\n display: inline-block !important;\n }\n .d-print-block {\n display: block !important;\n }\n .d-print-grid {\n display: grid !important;\n }\n .d-print-inline-grid {\n display: inline-grid !important;\n }\n .d-print-table {\n display: table !important;\n }\n .d-print-table-row {\n display: table-row !important;\n }\n .d-print-table-cell {\n display: table-cell !important;\n }\n .d-print-flex {\n display: flex !important;\n }\n .d-print-inline-flex {\n display: inline-flex !important;\n }\n .d-print-none {\n display: none !important;\n }\n}\n\n/*# sourceMappingURL=bootstrap-grid.css.map */","// Container mixins\n\n@mixin make-container($gutter: $container-padding-x) {\n --#{$prefix}gutter-x: #{$gutter};\n --#{$prefix}gutter-y: 0;\n width: 100%;\n padding-right: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list\n padding-left: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list\n margin-right: auto;\n margin-left: auto;\n}\n","// Breakpoint viewport sizes and media queries.\n//\n// Breakpoints are defined as a map of (name: minimum width), order from small to large:\n//\n// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)\n//\n// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.\n\n// Name of the next breakpoint, or null for the last breakpoint.\n//\n// >> breakpoint-next(sm)\n// md\n// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// md\n// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl xxl))\n// md\n@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {\n $n: index($breakpoint-names, $name);\n @if not $n {\n @error \"breakpoint `#{$name}` not found in `#{$breakpoints}`\";\n }\n @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);\n}\n\n// Minimum breakpoint width. Null for the smallest (first) breakpoint.\n//\n// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// 576px\n@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {\n $min: map-get($breakpoints, $name);\n @return if($min != 0, $min, null);\n}\n\n// Maximum breakpoint width.\n// The maximum value is reduced by 0.02px to work around the limitations of\n// `min-` and `max-` prefixes and viewports with fractional widths.\n// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max\n// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.\n// See https://bugs.webkit.org/show_bug.cgi?id=178261\n//\n// >> breakpoint-max(md, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// 767.98px\n@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {\n $max: map-get($breakpoints, $name);\n @return if($max and $max > 0, $max - .02, null);\n}\n\n// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front.\n// Useful for making responsive utilities.\n//\n// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// \"\" (Returns a blank string)\n// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// \"-sm\"\n@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {\n @return if(breakpoint-min($name, $breakpoints) == null, \"\", \"-#{$name}\");\n}\n\n// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.\n// Makes the @content apply to the given breakpoint and wider.\n@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n @if $min {\n @media (min-width: $min) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media of at most the maximum breakpoint width. No query for the largest breakpoint.\n// Makes the @content apply to the given breakpoint and narrower.\n@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {\n $max: breakpoint-max($name, $breakpoints);\n @if $max {\n @media (max-width: $max) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media that spans multiple breakpoint widths.\n// Makes the @content apply between the min and max breakpoints\n@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($lower, $breakpoints);\n $max: breakpoint-max($upper, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($lower, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($upper, $breakpoints) {\n @content;\n }\n }\n}\n\n// Media between the breakpoint's minimum and maximum widths.\n// No minimum for the smallest breakpoint, and no maximum for the largest one.\n// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.\n@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n $next: breakpoint-next($name, $breakpoints);\n $max: breakpoint-max($next, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($name, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($next, $breakpoints) {\n @content;\n }\n }\n}\n","// Row\n//\n// Rows contain your columns.\n\n:root {\n @each $name, $value in $grid-breakpoints {\n --#{$prefix}breakpoint-#{$name}: #{$value};\n }\n}\n\n@if $enable-grid-classes {\n .row {\n @include make-row();\n\n > * {\n @include make-col-ready();\n }\n }\n}\n\n@if $enable-cssgrid {\n .grid {\n display: grid;\n grid-template-rows: repeat(var(--#{$prefix}rows, 1), 1fr);\n grid-template-columns: repeat(var(--#{$prefix}columns, #{$grid-columns}), 1fr);\n gap: var(--#{$prefix}gap, #{$grid-gutter-width});\n\n @include make-cssgrid();\n }\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n@if $enable-grid-classes {\n @include make-grid-columns();\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n@mixin make-row($gutter: $grid-gutter-width) {\n --#{$prefix}gutter-x: #{$gutter};\n --#{$prefix}gutter-y: 0;\n display: flex;\n flex-wrap: wrap;\n // TODO: Revisit calc order after https://github.com/react-bootstrap/react-bootstrap/issues/6039 is fixed\n margin-top: calc(-1 * var(--#{$prefix}gutter-y)); // stylelint-disable-line function-disallowed-list\n margin-right: calc(-.5 * var(--#{$prefix}gutter-x)); // stylelint-disable-line function-disallowed-list\n margin-left: calc(-.5 * var(--#{$prefix}gutter-x)); // stylelint-disable-line function-disallowed-list\n}\n\n@mixin make-col-ready() {\n // Add box sizing if only the grid is loaded\n box-sizing: if(variable-exists(include-column-box-sizing) and $include-column-box-sizing, border-box, null);\n // Prevent columns from becoming too narrow when at smaller grid tiers by\n // always setting `width: 100%;`. This works because we set the width\n // later on to override this initial width.\n flex-shrink: 0;\n width: 100%;\n max-width: 100%; // Prevent `.col-auto`, `.col` (& responsive variants) from breaking out the grid\n padding-right: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list\n padding-left: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list\n margin-top: var(--#{$prefix}gutter-y);\n}\n\n@mixin make-col($size: false, $columns: $grid-columns) {\n @if $size {\n flex: 0 0 auto;\n width: percentage(divide($size, $columns));\n\n } @else {\n flex: 1 1 0;\n max-width: 100%;\n }\n}\n\n@mixin make-col-auto() {\n flex: 0 0 auto;\n width: auto;\n}\n\n@mixin make-col-offset($size, $columns: $grid-columns) {\n $num: divide($size, $columns);\n margin-left: if($num == 0, 0, percentage($num));\n}\n\n// Row columns\n//\n// Specify on a parent element(e.g., .row) to force immediate children into NN\n// number of columns. Supports wrapping to new lines, but does not do a Masonry\n// style grid.\n@mixin row-cols($count) {\n > * {\n flex: 0 0 auto;\n width: percentage(divide(1, $count));\n }\n}\n\n// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `$grid-columns`.\n\n@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {\n @each $breakpoint in map-keys($breakpoints) {\n $infix: breakpoint-infix($breakpoint, $breakpoints);\n\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n // Provide basic `.col-{bp}` classes for equal-width flexbox columns\n .col#{$infix} {\n flex: 1 0 0%; // Flexbugs #4: https://github.com/philipwalton/flexbugs#flexbug-4\n }\n\n .row-cols#{$infix}-auto > * {\n @include make-col-auto();\n }\n\n @if $grid-row-columns > 0 {\n @for $i from 1 through $grid-row-columns {\n .row-cols#{$infix}-#{$i} {\n @include row-cols($i);\n }\n }\n }\n\n .col#{$infix}-auto {\n @include make-col-auto();\n }\n\n @if $columns > 0 {\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @include make-col($i, $columns);\n }\n }\n\n // `$columns - 1` because offsetting by the width of an entire row isn't possible\n @for $i from 0 through ($columns - 1) {\n @if not ($infix == \"\" and $i == 0) { // Avoid emitting useless .offset-0\n .offset#{$infix}-#{$i} {\n @include make-col-offset($i, $columns);\n }\n }\n }\n }\n\n // Gutters\n //\n // Make use of `.g-*`, `.gx-*` or `.gy-*` utilities to change spacing between the columns.\n @each $key, $value in $gutters {\n .g#{$infix}-#{$key},\n .gx#{$infix}-#{$key} {\n --#{$prefix}gutter-x: #{$value};\n }\n\n .g#{$infix}-#{$key},\n .gy#{$infix}-#{$key} {\n --#{$prefix}gutter-y: #{$value};\n }\n }\n }\n }\n}\n\n@mixin make-cssgrid($columns: $grid-columns, $breakpoints: $grid-breakpoints) {\n @each $breakpoint in map-keys($breakpoints) {\n $infix: breakpoint-infix($breakpoint, $breakpoints);\n\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n @if $columns > 0 {\n @for $i from 1 through $columns {\n .g-col#{$infix}-#{$i} {\n grid-column: auto / span $i;\n }\n }\n\n // Start with `1` because `0` is an invalid value.\n // Ends with `$columns - 1` because offsetting by the width of an entire row isn't possible.\n @for $i from 1 through ($columns - 1) {\n .g-start#{$infix}-#{$i} {\n grid-column-start: $i;\n }\n }\n }\n }\n }\n}\n","// Utility generator\n// Used to generate utilities & print utilities\n@mixin generate-utility($utility, $infix: \"\", $is-rfs-media-query: false) {\n $values: map-get($utility, values);\n\n // If the values are a list or string, convert it into a map\n @if type-of($values) == \"string\" or type-of(nth($values, 1)) != \"list\" {\n $values: zip($values, $values);\n }\n\n @each $key, $value in $values {\n $properties: map-get($utility, property);\n\n // Multiple properties are possible, for example with vertical or horizontal margins or paddings\n @if type-of($properties) == \"string\" {\n $properties: append((), $properties);\n }\n\n // Use custom class if present\n $property-class: if(map-has-key($utility, class), map-get($utility, class), nth($properties, 1));\n $property-class: if($property-class == null, \"\", $property-class);\n\n // Use custom CSS variable name if present, otherwise default to `class`\n $css-variable-name: if(map-has-key($utility, css-variable-name), map-get($utility, css-variable-name), map-get($utility, class));\n\n // State params to generate pseudo-classes\n $state: if(map-has-key($utility, state), map-get($utility, state), ());\n\n $infix: if($property-class == \"\" and str-slice($infix, 1, 1) == \"-\", str-slice($infix, 2), $infix);\n\n // Don't prefix if value key is null (e.g. with shadow class)\n $property-class-modifier: if($key, if($property-class == \"\" and $infix == \"\", \"\", \"-\") + $key, \"\");\n\n @if map-get($utility, rfs) {\n // Inside the media query\n @if $is-rfs-media-query {\n $val: rfs-value($value);\n\n // Do not render anything if fluid and non fluid values are the same\n $value: if($val == rfs-fluid-value($value), null, $val);\n }\n @else {\n $value: rfs-fluid-value($value);\n }\n }\n\n $is-css-var: map-get($utility, css-var);\n $is-local-vars: map-get($utility, local-vars);\n $is-rtl: map-get($utility, rtl);\n\n @if $value != null {\n @if $is-rtl == false {\n /* rtl:begin:remove */\n }\n\n @if $is-css-var {\n .#{$property-class + $infix + $property-class-modifier} {\n --#{$prefix}#{$css-variable-name}: #{$value};\n }\n\n @each $pseudo in $state {\n .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} {\n --#{$prefix}#{$css-variable-name}: #{$value};\n }\n }\n } @else {\n .#{$property-class + $infix + $property-class-modifier} {\n @each $property in $properties {\n @if $is-local-vars {\n @each $local-var, $variable in $is-local-vars {\n --#{$prefix}#{$local-var}: #{$variable};\n }\n }\n #{$property}: $value if($enable-important-utilities, !important, null);\n }\n }\n\n @each $pseudo in $state {\n .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} {\n @each $property in $properties {\n @if $is-local-vars {\n @each $local-var, $variable in $is-local-vars {\n --#{$prefix}#{$local-var}: #{$variable};\n }\n }\n #{$property}: $value if($enable-important-utilities, !important, null);\n }\n }\n }\n }\n\n @if $is-rtl == false {\n /* rtl:end:remove */\n }\n }\n }\n}\n","// Loop over each breakpoint\n@each $breakpoint in map-keys($grid-breakpoints) {\n\n // Generate media query if needed\n @include media-breakpoint-up($breakpoint) {\n $infix: breakpoint-infix($breakpoint, $grid-breakpoints);\n\n // Loop over each utility property\n @each $key, $utility in $utilities {\n // The utility can be disabled with `false`, thus check if the utility is a map first\n // Only proceed if responsive media queries are enabled or if it's the base media query\n @if type-of($utility) == \"map\" and (map-get($utility, responsive) or $infix == \"\") {\n @include generate-utility($utility, $infix);\n }\n }\n }\n}\n\n// RFS rescaling\n@media (min-width: $rfs-mq-value) {\n @each $breakpoint in map-keys($grid-breakpoints) {\n $infix: breakpoint-infix($breakpoint, $grid-breakpoints);\n\n @if (map-get($grid-breakpoints, $breakpoint) < $rfs-breakpoint) {\n // Loop over each utility property\n @each $key, $utility in $utilities {\n // The utility can be disabled with `false`, thus check if the utility is a map first\n // Only proceed if responsive media queries are enabled or if it's the base media query\n @if type-of($utility) == \"map\" and map-get($utility, rfs) and (map-get($utility, responsive) or $infix == \"\") {\n @include generate-utility($utility, $infix, true);\n }\n }\n }\n }\n}\n\n\n// Print utilities\n@media print {\n @each $key, $utility in $utilities {\n // The utility can be disabled with `false`, thus check if the utility is a map first\n // Then check if the utility needs print styles\n @if type-of($utility) == \"map\" and map-get($utility, print) == true {\n @include generate-utility($utility, \"-print\");\n }\n }\n}\n"]} \ No newline at end of file diff --git a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css b/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css deleted file mode 100644 index 1a5d656..0000000 --- a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css +++ /dev/null @@ -1,4084 +0,0 @@ -/*! - * Bootstrap Grid v5.3.3 (https://getbootstrap.com/) - * Copyright 2011-2024 The Bootstrap Authors - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ -.container, -.container-fluid, -.container-xxl, -.container-xl, -.container-lg, -.container-md, -.container-sm { - --bs-gutter-x: 1.5rem; - --bs-gutter-y: 0; - width: 100%; - padding-left: calc(var(--bs-gutter-x) * 0.5); - padding-right: calc(var(--bs-gutter-x) * 0.5); - margin-left: auto; - margin-right: auto; -} - -@media (min-width: 576px) { - .container-sm, .container { - max-width: 540px; - } -} -@media (min-width: 768px) { - .container-md, .container-sm, .container { - max-width: 720px; - } -} -@media (min-width: 992px) { - .container-lg, .container-md, .container-sm, .container { - max-width: 960px; - } -} -@media (min-width: 1200px) { - .container-xl, .container-lg, .container-md, .container-sm, .container { - max-width: 1140px; - } -} -@media (min-width: 1400px) { - .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container { - max-width: 1320px; - } -} -:root { - --bs-breakpoint-xs: 0; - --bs-breakpoint-sm: 576px; - --bs-breakpoint-md: 768px; - --bs-breakpoint-lg: 992px; - --bs-breakpoint-xl: 1200px; - --bs-breakpoint-xxl: 1400px; -} - -.row { - --bs-gutter-x: 1.5rem; - --bs-gutter-y: 0; - display: flex; - flex-wrap: wrap; - margin-top: calc(-1 * var(--bs-gutter-y)); - margin-left: calc(-0.5 * var(--bs-gutter-x)); - margin-right: calc(-0.5 * var(--bs-gutter-x)); -} -.row > * { - box-sizing: border-box; - flex-shrink: 0; - width: 100%; - max-width: 100%; - padding-left: calc(var(--bs-gutter-x) * 0.5); - padding-right: calc(var(--bs-gutter-x) * 0.5); - margin-top: var(--bs-gutter-y); -} - -.col { - flex: 1 0 0%; -} - -.row-cols-auto > * { - flex: 0 0 auto; - width: auto; -} - -.row-cols-1 > * { - flex: 0 0 auto; - width: 100%; -} - -.row-cols-2 > * { - flex: 0 0 auto; - width: 50%; -} - -.row-cols-3 > * { - flex: 0 0 auto; - width: 33.33333333%; -} - -.row-cols-4 > * { - flex: 0 0 auto; - width: 25%; -} - -.row-cols-5 > * { - flex: 0 0 auto; - width: 20%; -} - -.row-cols-6 > * { - flex: 0 0 auto; - width: 16.66666667%; -} - -.col-auto { - flex: 0 0 auto; - width: auto; -} - -.col-1 { - flex: 0 0 auto; - width: 8.33333333%; -} - -.col-2 { - flex: 0 0 auto; - width: 16.66666667%; -} - -.col-3 { - flex: 0 0 auto; - width: 25%; -} - -.col-4 { - flex: 0 0 auto; - width: 33.33333333%; -} - -.col-5 { - flex: 0 0 auto; - width: 41.66666667%; -} - -.col-6 { - flex: 0 0 auto; - width: 50%; -} - -.col-7 { - flex: 0 0 auto; - width: 58.33333333%; -} - -.col-8 { - flex: 0 0 auto; - width: 66.66666667%; -} - -.col-9 { - flex: 0 0 auto; - width: 75%; -} - -.col-10 { - flex: 0 0 auto; - width: 83.33333333%; -} - -.col-11 { - flex: 0 0 auto; - width: 91.66666667%; -} - -.col-12 { - flex: 0 0 auto; - width: 100%; -} - -.offset-1 { - margin-right: 8.33333333%; -} - -.offset-2 { - margin-right: 16.66666667%; -} - -.offset-3 { - margin-right: 25%; -} - -.offset-4 { - margin-right: 33.33333333%; -} - -.offset-5 { - margin-right: 41.66666667%; -} - -.offset-6 { - margin-right: 50%; -} - -.offset-7 { - margin-right: 58.33333333%; -} - -.offset-8 { - margin-right: 66.66666667%; -} - -.offset-9 { - margin-right: 75%; -} - -.offset-10 { - margin-right: 83.33333333%; -} - -.offset-11 { - margin-right: 91.66666667%; -} - -.g-0, -.gx-0 { - --bs-gutter-x: 0; -} - -.g-0, -.gy-0 { - --bs-gutter-y: 0; -} - -.g-1, -.gx-1 { - --bs-gutter-x: 0.25rem; -} - -.g-1, -.gy-1 { - --bs-gutter-y: 0.25rem; -} - -.g-2, -.gx-2 { - --bs-gutter-x: 0.5rem; -} - -.g-2, -.gy-2 { - --bs-gutter-y: 0.5rem; -} - -.g-3, -.gx-3 { - --bs-gutter-x: 1rem; -} - -.g-3, -.gy-3 { - --bs-gutter-y: 1rem; -} - -.g-4, -.gx-4 { - --bs-gutter-x: 1.5rem; -} - -.g-4, -.gy-4 { - --bs-gutter-y: 1.5rem; -} - -.g-5, -.gx-5 { - --bs-gutter-x: 3rem; -} - -.g-5, -.gy-5 { - --bs-gutter-y: 3rem; -} - -@media (min-width: 576px) { - .col-sm { - flex: 1 0 0%; - } - .row-cols-sm-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-sm-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-sm-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-sm-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-sm-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-sm-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-sm-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-sm-auto { - flex: 0 0 auto; - width: auto; - } - .col-sm-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-sm-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-sm-3 { - flex: 0 0 auto; - width: 25%; - } - .col-sm-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-sm-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-sm-6 { - flex: 0 0 auto; - width: 50%; - } - .col-sm-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-sm-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-sm-9 { - flex: 0 0 auto; - width: 75%; - } - .col-sm-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-sm-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-sm-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-sm-0 { - margin-right: 0; - } - .offset-sm-1 { - margin-right: 8.33333333%; - } - .offset-sm-2 { - margin-right: 16.66666667%; - } - .offset-sm-3 { - margin-right: 25%; - } - .offset-sm-4 { - margin-right: 33.33333333%; - } - .offset-sm-5 { - margin-right: 41.66666667%; - } - .offset-sm-6 { - margin-right: 50%; - } - .offset-sm-7 { - margin-right: 58.33333333%; - } - .offset-sm-8 { - margin-right: 66.66666667%; - } - .offset-sm-9 { - margin-right: 75%; - } - .offset-sm-10 { - margin-right: 83.33333333%; - } - .offset-sm-11 { - margin-right: 91.66666667%; - } - .g-sm-0, - .gx-sm-0 { - --bs-gutter-x: 0; - } - .g-sm-0, - .gy-sm-0 { - --bs-gutter-y: 0; - } - .g-sm-1, - .gx-sm-1 { - --bs-gutter-x: 0.25rem; - } - .g-sm-1, - .gy-sm-1 { - --bs-gutter-y: 0.25rem; - } - .g-sm-2, - .gx-sm-2 { - --bs-gutter-x: 0.5rem; - } - .g-sm-2, - .gy-sm-2 { - --bs-gutter-y: 0.5rem; - } - .g-sm-3, - .gx-sm-3 { - --bs-gutter-x: 1rem; - } - .g-sm-3, - .gy-sm-3 { - --bs-gutter-y: 1rem; - } - .g-sm-4, - .gx-sm-4 { - --bs-gutter-x: 1.5rem; - } - .g-sm-4, - .gy-sm-4 { - --bs-gutter-y: 1.5rem; - } - .g-sm-5, - .gx-sm-5 { - --bs-gutter-x: 3rem; - } - .g-sm-5, - .gy-sm-5 { - --bs-gutter-y: 3rem; - } -} -@media (min-width: 768px) { - .col-md { - flex: 1 0 0%; - } - .row-cols-md-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-md-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-md-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-md-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-md-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-md-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-md-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-md-auto { - flex: 0 0 auto; - width: auto; - } - .col-md-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-md-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-md-3 { - flex: 0 0 auto; - width: 25%; - } - .col-md-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-md-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-md-6 { - flex: 0 0 auto; - width: 50%; - } - .col-md-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-md-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-md-9 { - flex: 0 0 auto; - width: 75%; - } - .col-md-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-md-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-md-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-md-0 { - margin-right: 0; - } - .offset-md-1 { - margin-right: 8.33333333%; - } - .offset-md-2 { - margin-right: 16.66666667%; - } - .offset-md-3 { - margin-right: 25%; - } - .offset-md-4 { - margin-right: 33.33333333%; - } - .offset-md-5 { - margin-right: 41.66666667%; - } - .offset-md-6 { - margin-right: 50%; - } - .offset-md-7 { - margin-right: 58.33333333%; - } - .offset-md-8 { - margin-right: 66.66666667%; - } - .offset-md-9 { - margin-right: 75%; - } - .offset-md-10 { - margin-right: 83.33333333%; - } - .offset-md-11 { - margin-right: 91.66666667%; - } - .g-md-0, - .gx-md-0 { - --bs-gutter-x: 0; - } - .g-md-0, - .gy-md-0 { - --bs-gutter-y: 0; - } - .g-md-1, - .gx-md-1 { - --bs-gutter-x: 0.25rem; - } - .g-md-1, - .gy-md-1 { - --bs-gutter-y: 0.25rem; - } - .g-md-2, - .gx-md-2 { - --bs-gutter-x: 0.5rem; - } - .g-md-2, - .gy-md-2 { - --bs-gutter-y: 0.5rem; - } - .g-md-3, - .gx-md-3 { - --bs-gutter-x: 1rem; - } - .g-md-3, - .gy-md-3 { - --bs-gutter-y: 1rem; - } - .g-md-4, - .gx-md-4 { - --bs-gutter-x: 1.5rem; - } - .g-md-4, - .gy-md-4 { - --bs-gutter-y: 1.5rem; - } - .g-md-5, - .gx-md-5 { - --bs-gutter-x: 3rem; - } - .g-md-5, - .gy-md-5 { - --bs-gutter-y: 3rem; - } -} -@media (min-width: 992px) { - .col-lg { - flex: 1 0 0%; - } - .row-cols-lg-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-lg-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-lg-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-lg-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-lg-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-lg-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-lg-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-lg-auto { - flex: 0 0 auto; - width: auto; - } - .col-lg-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-lg-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-lg-3 { - flex: 0 0 auto; - width: 25%; - } - .col-lg-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-lg-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-lg-6 { - flex: 0 0 auto; - width: 50%; - } - .col-lg-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-lg-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-lg-9 { - flex: 0 0 auto; - width: 75%; - } - .col-lg-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-lg-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-lg-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-lg-0 { - margin-right: 0; - } - .offset-lg-1 { - margin-right: 8.33333333%; - } - .offset-lg-2 { - margin-right: 16.66666667%; - } - .offset-lg-3 { - margin-right: 25%; - } - .offset-lg-4 { - margin-right: 33.33333333%; - } - .offset-lg-5 { - margin-right: 41.66666667%; - } - .offset-lg-6 { - margin-right: 50%; - } - .offset-lg-7 { - margin-right: 58.33333333%; - } - .offset-lg-8 { - margin-right: 66.66666667%; - } - .offset-lg-9 { - margin-right: 75%; - } - .offset-lg-10 { - margin-right: 83.33333333%; - } - .offset-lg-11 { - margin-right: 91.66666667%; - } - .g-lg-0, - .gx-lg-0 { - --bs-gutter-x: 0; - } - .g-lg-0, - .gy-lg-0 { - --bs-gutter-y: 0; - } - .g-lg-1, - .gx-lg-1 { - --bs-gutter-x: 0.25rem; - } - .g-lg-1, - .gy-lg-1 { - --bs-gutter-y: 0.25rem; - } - .g-lg-2, - .gx-lg-2 { - --bs-gutter-x: 0.5rem; - } - .g-lg-2, - .gy-lg-2 { - --bs-gutter-y: 0.5rem; - } - .g-lg-3, - .gx-lg-3 { - --bs-gutter-x: 1rem; - } - .g-lg-3, - .gy-lg-3 { - --bs-gutter-y: 1rem; - } - .g-lg-4, - .gx-lg-4 { - --bs-gutter-x: 1.5rem; - } - .g-lg-4, - .gy-lg-4 { - --bs-gutter-y: 1.5rem; - } - .g-lg-5, - .gx-lg-5 { - --bs-gutter-x: 3rem; - } - .g-lg-5, - .gy-lg-5 { - --bs-gutter-y: 3rem; - } -} -@media (min-width: 1200px) { - .col-xl { - flex: 1 0 0%; - } - .row-cols-xl-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-xl-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-xl-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-xl-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-xl-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-xl-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-xl-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xl-auto { - flex: 0 0 auto; - width: auto; - } - .col-xl-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-xl-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xl-3 { - flex: 0 0 auto; - width: 25%; - } - .col-xl-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-xl-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-xl-6 { - flex: 0 0 auto; - width: 50%; - } - .col-xl-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-xl-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-xl-9 { - flex: 0 0 auto; - width: 75%; - } - .col-xl-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-xl-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-xl-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-xl-0 { - margin-right: 0; - } - .offset-xl-1 { - margin-right: 8.33333333%; - } - .offset-xl-2 { - margin-right: 16.66666667%; - } - .offset-xl-3 { - margin-right: 25%; - } - .offset-xl-4 { - margin-right: 33.33333333%; - } - .offset-xl-5 { - margin-right: 41.66666667%; - } - .offset-xl-6 { - margin-right: 50%; - } - .offset-xl-7 { - margin-right: 58.33333333%; - } - .offset-xl-8 { - margin-right: 66.66666667%; - } - .offset-xl-9 { - margin-right: 75%; - } - .offset-xl-10 { - margin-right: 83.33333333%; - } - .offset-xl-11 { - margin-right: 91.66666667%; - } - .g-xl-0, - .gx-xl-0 { - --bs-gutter-x: 0; - } - .g-xl-0, - .gy-xl-0 { - --bs-gutter-y: 0; - } - .g-xl-1, - .gx-xl-1 { - --bs-gutter-x: 0.25rem; - } - .g-xl-1, - .gy-xl-1 { - --bs-gutter-y: 0.25rem; - } - .g-xl-2, - .gx-xl-2 { - --bs-gutter-x: 0.5rem; - } - .g-xl-2, - .gy-xl-2 { - --bs-gutter-y: 0.5rem; - } - .g-xl-3, - .gx-xl-3 { - --bs-gutter-x: 1rem; - } - .g-xl-3, - .gy-xl-3 { - --bs-gutter-y: 1rem; - } - .g-xl-4, - .gx-xl-4 { - --bs-gutter-x: 1.5rem; - } - .g-xl-4, - .gy-xl-4 { - --bs-gutter-y: 1.5rem; - } - .g-xl-5, - .gx-xl-5 { - --bs-gutter-x: 3rem; - } - .g-xl-5, - .gy-xl-5 { - --bs-gutter-y: 3rem; - } -} -@media (min-width: 1400px) { - .col-xxl { - flex: 1 0 0%; - } - .row-cols-xxl-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-xxl-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-xxl-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-xxl-3 > * { - flex: 0 0 auto; - width: 33.33333333%; - } - .row-cols-xxl-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-xxl-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-xxl-6 > * { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xxl-auto { - flex: 0 0 auto; - width: auto; - } - .col-xxl-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-xxl-2 { - flex: 0 0 auto; - width: 16.66666667%; - } - .col-xxl-3 { - flex: 0 0 auto; - width: 25%; - } - .col-xxl-4 { - flex: 0 0 auto; - width: 33.33333333%; - } - .col-xxl-5 { - flex: 0 0 auto; - width: 41.66666667%; - } - .col-xxl-6 { - flex: 0 0 auto; - width: 50%; - } - .col-xxl-7 { - flex: 0 0 auto; - width: 58.33333333%; - } - .col-xxl-8 { - flex: 0 0 auto; - width: 66.66666667%; - } - .col-xxl-9 { - flex: 0 0 auto; - width: 75%; - } - .col-xxl-10 { - flex: 0 0 auto; - width: 83.33333333%; - } - .col-xxl-11 { - flex: 0 0 auto; - width: 91.66666667%; - } - .col-xxl-12 { - flex: 0 0 auto; - width: 100%; - } - .offset-xxl-0 { - margin-right: 0; - } - .offset-xxl-1 { - margin-right: 8.33333333%; - } - .offset-xxl-2 { - margin-right: 16.66666667%; - } - .offset-xxl-3 { - margin-right: 25%; - } - .offset-xxl-4 { - margin-right: 33.33333333%; - } - .offset-xxl-5 { - margin-right: 41.66666667%; - } - .offset-xxl-6 { - margin-right: 50%; - } - .offset-xxl-7 { - margin-right: 58.33333333%; - } - .offset-xxl-8 { - margin-right: 66.66666667%; - } - .offset-xxl-9 { - margin-right: 75%; - } - .offset-xxl-10 { - margin-right: 83.33333333%; - } - .offset-xxl-11 { - margin-right: 91.66666667%; - } - .g-xxl-0, - .gx-xxl-0 { - --bs-gutter-x: 0; - } - .g-xxl-0, - .gy-xxl-0 { - --bs-gutter-y: 0; - } - .g-xxl-1, - .gx-xxl-1 { - --bs-gutter-x: 0.25rem; - } - .g-xxl-1, - .gy-xxl-1 { - --bs-gutter-y: 0.25rem; - } - .g-xxl-2, - .gx-xxl-2 { - --bs-gutter-x: 0.5rem; - } - .g-xxl-2, - .gy-xxl-2 { - --bs-gutter-y: 0.5rem; - } - .g-xxl-3, - .gx-xxl-3 { - --bs-gutter-x: 1rem; - } - .g-xxl-3, - .gy-xxl-3 { - --bs-gutter-y: 1rem; - } - .g-xxl-4, - .gx-xxl-4 { - --bs-gutter-x: 1.5rem; - } - .g-xxl-4, - .gy-xxl-4 { - --bs-gutter-y: 1.5rem; - } - .g-xxl-5, - .gx-xxl-5 { - --bs-gutter-x: 3rem; - } - .g-xxl-5, - .gy-xxl-5 { - --bs-gutter-y: 3rem; - } -} -.d-inline { - display: inline !important; -} - -.d-inline-block { - display: inline-block !important; -} - -.d-block { - display: block !important; -} - -.d-grid { - display: grid !important; -} - -.d-inline-grid { - display: inline-grid !important; -} - -.d-table { - display: table !important; -} - -.d-table-row { - display: table-row !important; -} - -.d-table-cell { - display: table-cell !important; -} - -.d-flex { - display: flex !important; -} - -.d-inline-flex { - display: inline-flex !important; -} - -.d-none { - display: none !important; -} - -.flex-fill { - flex: 1 1 auto !important; -} - -.flex-row { - flex-direction: row !important; -} - -.flex-column { - flex-direction: column !important; -} - -.flex-row-reverse { - flex-direction: row-reverse !important; -} - -.flex-column-reverse { - flex-direction: column-reverse !important; -} - -.flex-grow-0 { - flex-grow: 0 !important; -} - -.flex-grow-1 { - flex-grow: 1 !important; -} - -.flex-shrink-0 { - flex-shrink: 0 !important; -} - -.flex-shrink-1 { - flex-shrink: 1 !important; -} - -.flex-wrap { - flex-wrap: wrap !important; -} - -.flex-nowrap { - flex-wrap: nowrap !important; -} - -.flex-wrap-reverse { - flex-wrap: wrap-reverse !important; -} - -.justify-content-start { - justify-content: flex-start !important; -} - -.justify-content-end { - justify-content: flex-end !important; -} - -.justify-content-center { - justify-content: center !important; -} - -.justify-content-between { - justify-content: space-between !important; -} - -.justify-content-around { - justify-content: space-around !important; -} - -.justify-content-evenly { - justify-content: space-evenly !important; -} - -.align-items-start { - align-items: flex-start !important; -} - -.align-items-end { - align-items: flex-end !important; -} - -.align-items-center { - align-items: center !important; -} - -.align-items-baseline { - align-items: baseline !important; -} - -.align-items-stretch { - align-items: stretch !important; -} - -.align-content-start { - align-content: flex-start !important; -} - -.align-content-end { - align-content: flex-end !important; -} - -.align-content-center { - align-content: center !important; -} - -.align-content-between { - align-content: space-between !important; -} - -.align-content-around { - align-content: space-around !important; -} - -.align-content-stretch { - align-content: stretch !important; -} - -.align-self-auto { - align-self: auto !important; -} - -.align-self-start { - align-self: flex-start !important; -} - -.align-self-end { - align-self: flex-end !important; -} - -.align-self-center { - align-self: center !important; -} - -.align-self-baseline { - align-self: baseline !important; -} - -.align-self-stretch { - align-self: stretch !important; -} - -.order-first { - order: -1 !important; -} - -.order-0 { - order: 0 !important; -} - -.order-1 { - order: 1 !important; -} - -.order-2 { - order: 2 !important; -} - -.order-3 { - order: 3 !important; -} - -.order-4 { - order: 4 !important; -} - -.order-5 { - order: 5 !important; -} - -.order-last { - order: 6 !important; -} - -.m-0 { - margin: 0 !important; -} - -.m-1 { - margin: 0.25rem !important; -} - -.m-2 { - margin: 0.5rem !important; -} - -.m-3 { - margin: 1rem !important; -} - -.m-4 { - margin: 1.5rem !important; -} - -.m-5 { - margin: 3rem !important; -} - -.m-auto { - margin: auto !important; -} - -.mx-0 { - margin-left: 0 !important; - margin-right: 0 !important; -} - -.mx-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; -} - -.mx-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; -} - -.mx-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; -} - -.mx-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; -} - -.mx-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; -} - -.mx-auto { - margin-left: auto !important; - margin-right: auto !important; -} - -.my-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; -} - -.my-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; -} - -.my-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; -} - -.my-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; -} - -.my-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; -} - -.my-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; -} - -.my-auto { - margin-top: auto !important; - margin-bottom: auto !important; -} - -.mt-0 { - margin-top: 0 !important; -} - -.mt-1 { - margin-top: 0.25rem !important; -} - -.mt-2 { - margin-top: 0.5rem !important; -} - -.mt-3 { - margin-top: 1rem !important; -} - -.mt-4 { - margin-top: 1.5rem !important; -} - -.mt-5 { - margin-top: 3rem !important; -} - -.mt-auto { - margin-top: auto !important; -} - -.me-0 { - margin-left: 0 !important; -} - -.me-1 { - margin-left: 0.25rem !important; -} - -.me-2 { - margin-left: 0.5rem !important; -} - -.me-3 { - margin-left: 1rem !important; -} - -.me-4 { - margin-left: 1.5rem !important; -} - -.me-5 { - margin-left: 3rem !important; -} - -.me-auto { - margin-left: auto !important; -} - -.mb-0 { - margin-bottom: 0 !important; -} - -.mb-1 { - margin-bottom: 0.25rem !important; -} - -.mb-2 { - margin-bottom: 0.5rem !important; -} - -.mb-3 { - margin-bottom: 1rem !important; -} - -.mb-4 { - margin-bottom: 1.5rem !important; -} - -.mb-5 { - margin-bottom: 3rem !important; -} - -.mb-auto { - margin-bottom: auto !important; -} - -.ms-0 { - margin-right: 0 !important; -} - -.ms-1 { - margin-right: 0.25rem !important; -} - -.ms-2 { - margin-right: 0.5rem !important; -} - -.ms-3 { - margin-right: 1rem !important; -} - -.ms-4 { - margin-right: 1.5rem !important; -} - -.ms-5 { - margin-right: 3rem !important; -} - -.ms-auto { - margin-right: auto !important; -} - -.p-0 { - padding: 0 !important; -} - -.p-1 { - padding: 0.25rem !important; -} - -.p-2 { - padding: 0.5rem !important; -} - -.p-3 { - padding: 1rem !important; -} - -.p-4 { - padding: 1.5rem !important; -} - -.p-5 { - padding: 3rem !important; -} - -.px-0 { - padding-left: 0 !important; - padding-right: 0 !important; -} - -.px-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; -} - -.px-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; -} - -.px-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; -} - -.px-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; -} - -.px-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; -} - -.py-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; -} - -.py-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; -} - -.py-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; -} - -.py-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; -} - -.py-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; -} - -.py-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; -} - -.pt-0 { - padding-top: 0 !important; -} - -.pt-1 { - padding-top: 0.25rem !important; -} - -.pt-2 { - padding-top: 0.5rem !important; -} - -.pt-3 { - padding-top: 1rem !important; -} - -.pt-4 { - padding-top: 1.5rem !important; -} - -.pt-5 { - padding-top: 3rem !important; -} - -.pe-0 { - padding-left: 0 !important; -} - -.pe-1 { - padding-left: 0.25rem !important; -} - -.pe-2 { - padding-left: 0.5rem !important; -} - -.pe-3 { - padding-left: 1rem !important; -} - -.pe-4 { - padding-left: 1.5rem !important; -} - -.pe-5 { - padding-left: 3rem !important; -} - -.pb-0 { - padding-bottom: 0 !important; -} - -.pb-1 { - padding-bottom: 0.25rem !important; -} - -.pb-2 { - padding-bottom: 0.5rem !important; -} - -.pb-3 { - padding-bottom: 1rem !important; -} - -.pb-4 { - padding-bottom: 1.5rem !important; -} - -.pb-5 { - padding-bottom: 3rem !important; -} - -.ps-0 { - padding-right: 0 !important; -} - -.ps-1 { - padding-right: 0.25rem !important; -} - -.ps-2 { - padding-right: 0.5rem !important; -} - -.ps-3 { - padding-right: 1rem !important; -} - -.ps-4 { - padding-right: 1.5rem !important; -} - -.ps-5 { - padding-right: 3rem !important; -} - -@media (min-width: 576px) { - .d-sm-inline { - display: inline !important; - } - .d-sm-inline-block { - display: inline-block !important; - } - .d-sm-block { - display: block !important; - } - .d-sm-grid { - display: grid !important; - } - .d-sm-inline-grid { - display: inline-grid !important; - } - .d-sm-table { - display: table !important; - } - .d-sm-table-row { - display: table-row !important; - } - .d-sm-table-cell { - display: table-cell !important; - } - .d-sm-flex { - display: flex !important; - } - .d-sm-inline-flex { - display: inline-flex !important; - } - .d-sm-none { - display: none !important; - } - .flex-sm-fill { - flex: 1 1 auto !important; - } - .flex-sm-row { - flex-direction: row !important; - } - .flex-sm-column { - flex-direction: column !important; - } - .flex-sm-row-reverse { - flex-direction: row-reverse !important; - } - .flex-sm-column-reverse { - flex-direction: column-reverse !important; - } - .flex-sm-grow-0 { - flex-grow: 0 !important; - } - .flex-sm-grow-1 { - flex-grow: 1 !important; - } - .flex-sm-shrink-0 { - flex-shrink: 0 !important; - } - .flex-sm-shrink-1 { - flex-shrink: 1 !important; - } - .flex-sm-wrap { - flex-wrap: wrap !important; - } - .flex-sm-nowrap { - flex-wrap: nowrap !important; - } - .flex-sm-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-sm-start { - justify-content: flex-start !important; - } - .justify-content-sm-end { - justify-content: flex-end !important; - } - .justify-content-sm-center { - justify-content: center !important; - } - .justify-content-sm-between { - justify-content: space-between !important; - } - .justify-content-sm-around { - justify-content: space-around !important; - } - .justify-content-sm-evenly { - justify-content: space-evenly !important; - } - .align-items-sm-start { - align-items: flex-start !important; - } - .align-items-sm-end { - align-items: flex-end !important; - } - .align-items-sm-center { - align-items: center !important; - } - .align-items-sm-baseline { - align-items: baseline !important; - } - .align-items-sm-stretch { - align-items: stretch !important; - } - .align-content-sm-start { - align-content: flex-start !important; - } - .align-content-sm-end { - align-content: flex-end !important; - } - .align-content-sm-center { - align-content: center !important; - } - .align-content-sm-between { - align-content: space-between !important; - } - .align-content-sm-around { - align-content: space-around !important; - } - .align-content-sm-stretch { - align-content: stretch !important; - } - .align-self-sm-auto { - align-self: auto !important; - } - .align-self-sm-start { - align-self: flex-start !important; - } - .align-self-sm-end { - align-self: flex-end !important; - } - .align-self-sm-center { - align-self: center !important; - } - .align-self-sm-baseline { - align-self: baseline !important; - } - .align-self-sm-stretch { - align-self: stretch !important; - } - .order-sm-first { - order: -1 !important; - } - .order-sm-0 { - order: 0 !important; - } - .order-sm-1 { - order: 1 !important; - } - .order-sm-2 { - order: 2 !important; - } - .order-sm-3 { - order: 3 !important; - } - .order-sm-4 { - order: 4 !important; - } - .order-sm-5 { - order: 5 !important; - } - .order-sm-last { - order: 6 !important; - } - .m-sm-0 { - margin: 0 !important; - } - .m-sm-1 { - margin: 0.25rem !important; - } - .m-sm-2 { - margin: 0.5rem !important; - } - .m-sm-3 { - margin: 1rem !important; - } - .m-sm-4 { - margin: 1.5rem !important; - } - .m-sm-5 { - margin: 3rem !important; - } - .m-sm-auto { - margin: auto !important; - } - .mx-sm-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-sm-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-sm-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-sm-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-sm-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-sm-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-sm-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-sm-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-sm-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-sm-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-sm-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-sm-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-sm-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-sm-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-sm-0 { - margin-top: 0 !important; - } - .mt-sm-1 { - margin-top: 0.25rem !important; - } - .mt-sm-2 { - margin-top: 0.5rem !important; - } - .mt-sm-3 { - margin-top: 1rem !important; - } - .mt-sm-4 { - margin-top: 1.5rem !important; - } - .mt-sm-5 { - margin-top: 3rem !important; - } - .mt-sm-auto { - margin-top: auto !important; - } - .me-sm-0 { - margin-left: 0 !important; - } - .me-sm-1 { - margin-left: 0.25rem !important; - } - .me-sm-2 { - margin-left: 0.5rem !important; - } - .me-sm-3 { - margin-left: 1rem !important; - } - .me-sm-4 { - margin-left: 1.5rem !important; - } - .me-sm-5 { - margin-left: 3rem !important; - } - .me-sm-auto { - margin-left: auto !important; - } - .mb-sm-0 { - margin-bottom: 0 !important; - } - .mb-sm-1 { - margin-bottom: 0.25rem !important; - } - .mb-sm-2 { - margin-bottom: 0.5rem !important; - } - .mb-sm-3 { - margin-bottom: 1rem !important; - } - .mb-sm-4 { - margin-bottom: 1.5rem !important; - } - .mb-sm-5 { - margin-bottom: 3rem !important; - } - .mb-sm-auto { - margin-bottom: auto !important; - } - .ms-sm-0 { - margin-right: 0 !important; - } - .ms-sm-1 { - margin-right: 0.25rem !important; - } - .ms-sm-2 { - margin-right: 0.5rem !important; - } - .ms-sm-3 { - margin-right: 1rem !important; - } - .ms-sm-4 { - margin-right: 1.5rem !important; - } - .ms-sm-5 { - margin-right: 3rem !important; - } - .ms-sm-auto { - margin-right: auto !important; - } - .p-sm-0 { - padding: 0 !important; - } - .p-sm-1 { - padding: 0.25rem !important; - } - .p-sm-2 { - padding: 0.5rem !important; - } - .p-sm-3 { - padding: 1rem !important; - } - .p-sm-4 { - padding: 1.5rem !important; - } - .p-sm-5 { - padding: 3rem !important; - } - .px-sm-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-sm-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-sm-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-sm-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-sm-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-sm-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-sm-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-sm-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-sm-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-sm-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-sm-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-sm-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-sm-0 { - padding-top: 0 !important; - } - .pt-sm-1 { - padding-top: 0.25rem !important; - } - .pt-sm-2 { - padding-top: 0.5rem !important; - } - .pt-sm-3 { - padding-top: 1rem !important; - } - .pt-sm-4 { - padding-top: 1.5rem !important; - } - .pt-sm-5 { - padding-top: 3rem !important; - } - .pe-sm-0 { - padding-left: 0 !important; - } - .pe-sm-1 { - padding-left: 0.25rem !important; - } - .pe-sm-2 { - padding-left: 0.5rem !important; - } - .pe-sm-3 { - padding-left: 1rem !important; - } - .pe-sm-4 { - padding-left: 1.5rem !important; - } - .pe-sm-5 { - padding-left: 3rem !important; - } - .pb-sm-0 { - padding-bottom: 0 !important; - } - .pb-sm-1 { - padding-bottom: 0.25rem !important; - } - .pb-sm-2 { - padding-bottom: 0.5rem !important; - } - .pb-sm-3 { - padding-bottom: 1rem !important; - } - .pb-sm-4 { - padding-bottom: 1.5rem !important; - } - .pb-sm-5 { - padding-bottom: 3rem !important; - } - .ps-sm-0 { - padding-right: 0 !important; - } - .ps-sm-1 { - padding-right: 0.25rem !important; - } - .ps-sm-2 { - padding-right: 0.5rem !important; - } - .ps-sm-3 { - padding-right: 1rem !important; - } - .ps-sm-4 { - padding-right: 1.5rem !important; - } - .ps-sm-5 { - padding-right: 3rem !important; - } -} -@media (min-width: 768px) { - .d-md-inline { - display: inline !important; - } - .d-md-inline-block { - display: inline-block !important; - } - .d-md-block { - display: block !important; - } - .d-md-grid { - display: grid !important; - } - .d-md-inline-grid { - display: inline-grid !important; - } - .d-md-table { - display: table !important; - } - .d-md-table-row { - display: table-row !important; - } - .d-md-table-cell { - display: table-cell !important; - } - .d-md-flex { - display: flex !important; - } - .d-md-inline-flex { - display: inline-flex !important; - } - .d-md-none { - display: none !important; - } - .flex-md-fill { - flex: 1 1 auto !important; - } - .flex-md-row { - flex-direction: row !important; - } - .flex-md-column { - flex-direction: column !important; - } - .flex-md-row-reverse { - flex-direction: row-reverse !important; - } - .flex-md-column-reverse { - flex-direction: column-reverse !important; - } - .flex-md-grow-0 { - flex-grow: 0 !important; - } - .flex-md-grow-1 { - flex-grow: 1 !important; - } - .flex-md-shrink-0 { - flex-shrink: 0 !important; - } - .flex-md-shrink-1 { - flex-shrink: 1 !important; - } - .flex-md-wrap { - flex-wrap: wrap !important; - } - .flex-md-nowrap { - flex-wrap: nowrap !important; - } - .flex-md-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-md-start { - justify-content: flex-start !important; - } - .justify-content-md-end { - justify-content: flex-end !important; - } - .justify-content-md-center { - justify-content: center !important; - } - .justify-content-md-between { - justify-content: space-between !important; - } - .justify-content-md-around { - justify-content: space-around !important; - } - .justify-content-md-evenly { - justify-content: space-evenly !important; - } - .align-items-md-start { - align-items: flex-start !important; - } - .align-items-md-end { - align-items: flex-end !important; - } - .align-items-md-center { - align-items: center !important; - } - .align-items-md-baseline { - align-items: baseline !important; - } - .align-items-md-stretch { - align-items: stretch !important; - } - .align-content-md-start { - align-content: flex-start !important; - } - .align-content-md-end { - align-content: flex-end !important; - } - .align-content-md-center { - align-content: center !important; - } - .align-content-md-between { - align-content: space-between !important; - } - .align-content-md-around { - align-content: space-around !important; - } - .align-content-md-stretch { - align-content: stretch !important; - } - .align-self-md-auto { - align-self: auto !important; - } - .align-self-md-start { - align-self: flex-start !important; - } - .align-self-md-end { - align-self: flex-end !important; - } - .align-self-md-center { - align-self: center !important; - } - .align-self-md-baseline { - align-self: baseline !important; - } - .align-self-md-stretch { - align-self: stretch !important; - } - .order-md-first { - order: -1 !important; - } - .order-md-0 { - order: 0 !important; - } - .order-md-1 { - order: 1 !important; - } - .order-md-2 { - order: 2 !important; - } - .order-md-3 { - order: 3 !important; - } - .order-md-4 { - order: 4 !important; - } - .order-md-5 { - order: 5 !important; - } - .order-md-last { - order: 6 !important; - } - .m-md-0 { - margin: 0 !important; - } - .m-md-1 { - margin: 0.25rem !important; - } - .m-md-2 { - margin: 0.5rem !important; - } - .m-md-3 { - margin: 1rem !important; - } - .m-md-4 { - margin: 1.5rem !important; - } - .m-md-5 { - margin: 3rem !important; - } - .m-md-auto { - margin: auto !important; - } - .mx-md-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-md-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-md-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-md-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-md-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-md-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-md-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-md-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-md-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-md-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-md-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-md-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-md-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-md-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-md-0 { - margin-top: 0 !important; - } - .mt-md-1 { - margin-top: 0.25rem !important; - } - .mt-md-2 { - margin-top: 0.5rem !important; - } - .mt-md-3 { - margin-top: 1rem !important; - } - .mt-md-4 { - margin-top: 1.5rem !important; - } - .mt-md-5 { - margin-top: 3rem !important; - } - .mt-md-auto { - margin-top: auto !important; - } - .me-md-0 { - margin-left: 0 !important; - } - .me-md-1 { - margin-left: 0.25rem !important; - } - .me-md-2 { - margin-left: 0.5rem !important; - } - .me-md-3 { - margin-left: 1rem !important; - } - .me-md-4 { - margin-left: 1.5rem !important; - } - .me-md-5 { - margin-left: 3rem !important; - } - .me-md-auto { - margin-left: auto !important; - } - .mb-md-0 { - margin-bottom: 0 !important; - } - .mb-md-1 { - margin-bottom: 0.25rem !important; - } - .mb-md-2 { - margin-bottom: 0.5rem !important; - } - .mb-md-3 { - margin-bottom: 1rem !important; - } - .mb-md-4 { - margin-bottom: 1.5rem !important; - } - .mb-md-5 { - margin-bottom: 3rem !important; - } - .mb-md-auto { - margin-bottom: auto !important; - } - .ms-md-0 { - margin-right: 0 !important; - } - .ms-md-1 { - margin-right: 0.25rem !important; - } - .ms-md-2 { - margin-right: 0.5rem !important; - } - .ms-md-3 { - margin-right: 1rem !important; - } - .ms-md-4 { - margin-right: 1.5rem !important; - } - .ms-md-5 { - margin-right: 3rem !important; - } - .ms-md-auto { - margin-right: auto !important; - } - .p-md-0 { - padding: 0 !important; - } - .p-md-1 { - padding: 0.25rem !important; - } - .p-md-2 { - padding: 0.5rem !important; - } - .p-md-3 { - padding: 1rem !important; - } - .p-md-4 { - padding: 1.5rem !important; - } - .p-md-5 { - padding: 3rem !important; - } - .px-md-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-md-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-md-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-md-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-md-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-md-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-md-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-md-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-md-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-md-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-md-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-md-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-md-0 { - padding-top: 0 !important; - } - .pt-md-1 { - padding-top: 0.25rem !important; - } - .pt-md-2 { - padding-top: 0.5rem !important; - } - .pt-md-3 { - padding-top: 1rem !important; - } - .pt-md-4 { - padding-top: 1.5rem !important; - } - .pt-md-5 { - padding-top: 3rem !important; - } - .pe-md-0 { - padding-left: 0 !important; - } - .pe-md-1 { - padding-left: 0.25rem !important; - } - .pe-md-2 { - padding-left: 0.5rem !important; - } - .pe-md-3 { - padding-left: 1rem !important; - } - .pe-md-4 { - padding-left: 1.5rem !important; - } - .pe-md-5 { - padding-left: 3rem !important; - } - .pb-md-0 { - padding-bottom: 0 !important; - } - .pb-md-1 { - padding-bottom: 0.25rem !important; - } - .pb-md-2 { - padding-bottom: 0.5rem !important; - } - .pb-md-3 { - padding-bottom: 1rem !important; - } - .pb-md-4 { - padding-bottom: 1.5rem !important; - } - .pb-md-5 { - padding-bottom: 3rem !important; - } - .ps-md-0 { - padding-right: 0 !important; - } - .ps-md-1 { - padding-right: 0.25rem !important; - } - .ps-md-2 { - padding-right: 0.5rem !important; - } - .ps-md-3 { - padding-right: 1rem !important; - } - .ps-md-4 { - padding-right: 1.5rem !important; - } - .ps-md-5 { - padding-right: 3rem !important; - } -} -@media (min-width: 992px) { - .d-lg-inline { - display: inline !important; - } - .d-lg-inline-block { - display: inline-block !important; - } - .d-lg-block { - display: block !important; - } - .d-lg-grid { - display: grid !important; - } - .d-lg-inline-grid { - display: inline-grid !important; - } - .d-lg-table { - display: table !important; - } - .d-lg-table-row { - display: table-row !important; - } - .d-lg-table-cell { - display: table-cell !important; - } - .d-lg-flex { - display: flex !important; - } - .d-lg-inline-flex { - display: inline-flex !important; - } - .d-lg-none { - display: none !important; - } - .flex-lg-fill { - flex: 1 1 auto !important; - } - .flex-lg-row { - flex-direction: row !important; - } - .flex-lg-column { - flex-direction: column !important; - } - .flex-lg-row-reverse { - flex-direction: row-reverse !important; - } - .flex-lg-column-reverse { - flex-direction: column-reverse !important; - } - .flex-lg-grow-0 { - flex-grow: 0 !important; - } - .flex-lg-grow-1 { - flex-grow: 1 !important; - } - .flex-lg-shrink-0 { - flex-shrink: 0 !important; - } - .flex-lg-shrink-1 { - flex-shrink: 1 !important; - } - .flex-lg-wrap { - flex-wrap: wrap !important; - } - .flex-lg-nowrap { - flex-wrap: nowrap !important; - } - .flex-lg-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-lg-start { - justify-content: flex-start !important; - } - .justify-content-lg-end { - justify-content: flex-end !important; - } - .justify-content-lg-center { - justify-content: center !important; - } - .justify-content-lg-between { - justify-content: space-between !important; - } - .justify-content-lg-around { - justify-content: space-around !important; - } - .justify-content-lg-evenly { - justify-content: space-evenly !important; - } - .align-items-lg-start { - align-items: flex-start !important; - } - .align-items-lg-end { - align-items: flex-end !important; - } - .align-items-lg-center { - align-items: center !important; - } - .align-items-lg-baseline { - align-items: baseline !important; - } - .align-items-lg-stretch { - align-items: stretch !important; - } - .align-content-lg-start { - align-content: flex-start !important; - } - .align-content-lg-end { - align-content: flex-end !important; - } - .align-content-lg-center { - align-content: center !important; - } - .align-content-lg-between { - align-content: space-between !important; - } - .align-content-lg-around { - align-content: space-around !important; - } - .align-content-lg-stretch { - align-content: stretch !important; - } - .align-self-lg-auto { - align-self: auto !important; - } - .align-self-lg-start { - align-self: flex-start !important; - } - .align-self-lg-end { - align-self: flex-end !important; - } - .align-self-lg-center { - align-self: center !important; - } - .align-self-lg-baseline { - align-self: baseline !important; - } - .align-self-lg-stretch { - align-self: stretch !important; - } - .order-lg-first { - order: -1 !important; - } - .order-lg-0 { - order: 0 !important; - } - .order-lg-1 { - order: 1 !important; - } - .order-lg-2 { - order: 2 !important; - } - .order-lg-3 { - order: 3 !important; - } - .order-lg-4 { - order: 4 !important; - } - .order-lg-5 { - order: 5 !important; - } - .order-lg-last { - order: 6 !important; - } - .m-lg-0 { - margin: 0 !important; - } - .m-lg-1 { - margin: 0.25rem !important; - } - .m-lg-2 { - margin: 0.5rem !important; - } - .m-lg-3 { - margin: 1rem !important; - } - .m-lg-4 { - margin: 1.5rem !important; - } - .m-lg-5 { - margin: 3rem !important; - } - .m-lg-auto { - margin: auto !important; - } - .mx-lg-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-lg-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-lg-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-lg-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-lg-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-lg-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-lg-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-lg-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-lg-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-lg-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-lg-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-lg-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-lg-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-lg-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-lg-0 { - margin-top: 0 !important; - } - .mt-lg-1 { - margin-top: 0.25rem !important; - } - .mt-lg-2 { - margin-top: 0.5rem !important; - } - .mt-lg-3 { - margin-top: 1rem !important; - } - .mt-lg-4 { - margin-top: 1.5rem !important; - } - .mt-lg-5 { - margin-top: 3rem !important; - } - .mt-lg-auto { - margin-top: auto !important; - } - .me-lg-0 { - margin-left: 0 !important; - } - .me-lg-1 { - margin-left: 0.25rem !important; - } - .me-lg-2 { - margin-left: 0.5rem !important; - } - .me-lg-3 { - margin-left: 1rem !important; - } - .me-lg-4 { - margin-left: 1.5rem !important; - } - .me-lg-5 { - margin-left: 3rem !important; - } - .me-lg-auto { - margin-left: auto !important; - } - .mb-lg-0 { - margin-bottom: 0 !important; - } - .mb-lg-1 { - margin-bottom: 0.25rem !important; - } - .mb-lg-2 { - margin-bottom: 0.5rem !important; - } - .mb-lg-3 { - margin-bottom: 1rem !important; - } - .mb-lg-4 { - margin-bottom: 1.5rem !important; - } - .mb-lg-5 { - margin-bottom: 3rem !important; - } - .mb-lg-auto { - margin-bottom: auto !important; - } - .ms-lg-0 { - margin-right: 0 !important; - } - .ms-lg-1 { - margin-right: 0.25rem !important; - } - .ms-lg-2 { - margin-right: 0.5rem !important; - } - .ms-lg-3 { - margin-right: 1rem !important; - } - .ms-lg-4 { - margin-right: 1.5rem !important; - } - .ms-lg-5 { - margin-right: 3rem !important; - } - .ms-lg-auto { - margin-right: auto !important; - } - .p-lg-0 { - padding: 0 !important; - } - .p-lg-1 { - padding: 0.25rem !important; - } - .p-lg-2 { - padding: 0.5rem !important; - } - .p-lg-3 { - padding: 1rem !important; - } - .p-lg-4 { - padding: 1.5rem !important; - } - .p-lg-5 { - padding: 3rem !important; - } - .px-lg-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-lg-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-lg-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-lg-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-lg-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-lg-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-lg-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-lg-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-lg-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-lg-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-lg-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-lg-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-lg-0 { - padding-top: 0 !important; - } - .pt-lg-1 { - padding-top: 0.25rem !important; - } - .pt-lg-2 { - padding-top: 0.5rem !important; - } - .pt-lg-3 { - padding-top: 1rem !important; - } - .pt-lg-4 { - padding-top: 1.5rem !important; - } - .pt-lg-5 { - padding-top: 3rem !important; - } - .pe-lg-0 { - padding-left: 0 !important; - } - .pe-lg-1 { - padding-left: 0.25rem !important; - } - .pe-lg-2 { - padding-left: 0.5rem !important; - } - .pe-lg-3 { - padding-left: 1rem !important; - } - .pe-lg-4 { - padding-left: 1.5rem !important; - } - .pe-lg-5 { - padding-left: 3rem !important; - } - .pb-lg-0 { - padding-bottom: 0 !important; - } - .pb-lg-1 { - padding-bottom: 0.25rem !important; - } - .pb-lg-2 { - padding-bottom: 0.5rem !important; - } - .pb-lg-3 { - padding-bottom: 1rem !important; - } - .pb-lg-4 { - padding-bottom: 1.5rem !important; - } - .pb-lg-5 { - padding-bottom: 3rem !important; - } - .ps-lg-0 { - padding-right: 0 !important; - } - .ps-lg-1 { - padding-right: 0.25rem !important; - } - .ps-lg-2 { - padding-right: 0.5rem !important; - } - .ps-lg-3 { - padding-right: 1rem !important; - } - .ps-lg-4 { - padding-right: 1.5rem !important; - } - .ps-lg-5 { - padding-right: 3rem !important; - } -} -@media (min-width: 1200px) { - .d-xl-inline { - display: inline !important; - } - .d-xl-inline-block { - display: inline-block !important; - } - .d-xl-block { - display: block !important; - } - .d-xl-grid { - display: grid !important; - } - .d-xl-inline-grid { - display: inline-grid !important; - } - .d-xl-table { - display: table !important; - } - .d-xl-table-row { - display: table-row !important; - } - .d-xl-table-cell { - display: table-cell !important; - } - .d-xl-flex { - display: flex !important; - } - .d-xl-inline-flex { - display: inline-flex !important; - } - .d-xl-none { - display: none !important; - } - .flex-xl-fill { - flex: 1 1 auto !important; - } - .flex-xl-row { - flex-direction: row !important; - } - .flex-xl-column { - flex-direction: column !important; - } - .flex-xl-row-reverse { - flex-direction: row-reverse !important; - } - .flex-xl-column-reverse { - flex-direction: column-reverse !important; - } - .flex-xl-grow-0 { - flex-grow: 0 !important; - } - .flex-xl-grow-1 { - flex-grow: 1 !important; - } - .flex-xl-shrink-0 { - flex-shrink: 0 !important; - } - .flex-xl-shrink-1 { - flex-shrink: 1 !important; - } - .flex-xl-wrap { - flex-wrap: wrap !important; - } - .flex-xl-nowrap { - flex-wrap: nowrap !important; - } - .flex-xl-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-xl-start { - justify-content: flex-start !important; - } - .justify-content-xl-end { - justify-content: flex-end !important; - } - .justify-content-xl-center { - justify-content: center !important; - } - .justify-content-xl-between { - justify-content: space-between !important; - } - .justify-content-xl-around { - justify-content: space-around !important; - } - .justify-content-xl-evenly { - justify-content: space-evenly !important; - } - .align-items-xl-start { - align-items: flex-start !important; - } - .align-items-xl-end { - align-items: flex-end !important; - } - .align-items-xl-center { - align-items: center !important; - } - .align-items-xl-baseline { - align-items: baseline !important; - } - .align-items-xl-stretch { - align-items: stretch !important; - } - .align-content-xl-start { - align-content: flex-start !important; - } - .align-content-xl-end { - align-content: flex-end !important; - } - .align-content-xl-center { - align-content: center !important; - } - .align-content-xl-between { - align-content: space-between !important; - } - .align-content-xl-around { - align-content: space-around !important; - } - .align-content-xl-stretch { - align-content: stretch !important; - } - .align-self-xl-auto { - align-self: auto !important; - } - .align-self-xl-start { - align-self: flex-start !important; - } - .align-self-xl-end { - align-self: flex-end !important; - } - .align-self-xl-center { - align-self: center !important; - } - .align-self-xl-baseline { - align-self: baseline !important; - } - .align-self-xl-stretch { - align-self: stretch !important; - } - .order-xl-first { - order: -1 !important; - } - .order-xl-0 { - order: 0 !important; - } - .order-xl-1 { - order: 1 !important; - } - .order-xl-2 { - order: 2 !important; - } - .order-xl-3 { - order: 3 !important; - } - .order-xl-4 { - order: 4 !important; - } - .order-xl-5 { - order: 5 !important; - } - .order-xl-last { - order: 6 !important; - } - .m-xl-0 { - margin: 0 !important; - } - .m-xl-1 { - margin: 0.25rem !important; - } - .m-xl-2 { - margin: 0.5rem !important; - } - .m-xl-3 { - margin: 1rem !important; - } - .m-xl-4 { - margin: 1.5rem !important; - } - .m-xl-5 { - margin: 3rem !important; - } - .m-xl-auto { - margin: auto !important; - } - .mx-xl-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-xl-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-xl-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-xl-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-xl-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-xl-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-xl-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-xl-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-xl-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-xl-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-xl-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-xl-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-xl-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-xl-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-xl-0 { - margin-top: 0 !important; - } - .mt-xl-1 { - margin-top: 0.25rem !important; - } - .mt-xl-2 { - margin-top: 0.5rem !important; - } - .mt-xl-3 { - margin-top: 1rem !important; - } - .mt-xl-4 { - margin-top: 1.5rem !important; - } - .mt-xl-5 { - margin-top: 3rem !important; - } - .mt-xl-auto { - margin-top: auto !important; - } - .me-xl-0 { - margin-left: 0 !important; - } - .me-xl-1 { - margin-left: 0.25rem !important; - } - .me-xl-2 { - margin-left: 0.5rem !important; - } - .me-xl-3 { - margin-left: 1rem !important; - } - .me-xl-4 { - margin-left: 1.5rem !important; - } - .me-xl-5 { - margin-left: 3rem !important; - } - .me-xl-auto { - margin-left: auto !important; - } - .mb-xl-0 { - margin-bottom: 0 !important; - } - .mb-xl-1 { - margin-bottom: 0.25rem !important; - } - .mb-xl-2 { - margin-bottom: 0.5rem !important; - } - .mb-xl-3 { - margin-bottom: 1rem !important; - } - .mb-xl-4 { - margin-bottom: 1.5rem !important; - } - .mb-xl-5 { - margin-bottom: 3rem !important; - } - .mb-xl-auto { - margin-bottom: auto !important; - } - .ms-xl-0 { - margin-right: 0 !important; - } - .ms-xl-1 { - margin-right: 0.25rem !important; - } - .ms-xl-2 { - margin-right: 0.5rem !important; - } - .ms-xl-3 { - margin-right: 1rem !important; - } - .ms-xl-4 { - margin-right: 1.5rem !important; - } - .ms-xl-5 { - margin-right: 3rem !important; - } - .ms-xl-auto { - margin-right: auto !important; - } - .p-xl-0 { - padding: 0 !important; - } - .p-xl-1 { - padding: 0.25rem !important; - } - .p-xl-2 { - padding: 0.5rem !important; - } - .p-xl-3 { - padding: 1rem !important; - } - .p-xl-4 { - padding: 1.5rem !important; - } - .p-xl-5 { - padding: 3rem !important; - } - .px-xl-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-xl-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-xl-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-xl-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-xl-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-xl-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-xl-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-xl-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-xl-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-xl-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-xl-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-xl-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-xl-0 { - padding-top: 0 !important; - } - .pt-xl-1 { - padding-top: 0.25rem !important; - } - .pt-xl-2 { - padding-top: 0.5rem !important; - } - .pt-xl-3 { - padding-top: 1rem !important; - } - .pt-xl-4 { - padding-top: 1.5rem !important; - } - .pt-xl-5 { - padding-top: 3rem !important; - } - .pe-xl-0 { - padding-left: 0 !important; - } - .pe-xl-1 { - padding-left: 0.25rem !important; - } - .pe-xl-2 { - padding-left: 0.5rem !important; - } - .pe-xl-3 { - padding-left: 1rem !important; - } - .pe-xl-4 { - padding-left: 1.5rem !important; - } - .pe-xl-5 { - padding-left: 3rem !important; - } - .pb-xl-0 { - padding-bottom: 0 !important; - } - .pb-xl-1 { - padding-bottom: 0.25rem !important; - } - .pb-xl-2 { - padding-bottom: 0.5rem !important; - } - .pb-xl-3 { - padding-bottom: 1rem !important; - } - .pb-xl-4 { - padding-bottom: 1.5rem !important; - } - .pb-xl-5 { - padding-bottom: 3rem !important; - } - .ps-xl-0 { - padding-right: 0 !important; - } - .ps-xl-1 { - padding-right: 0.25rem !important; - } - .ps-xl-2 { - padding-right: 0.5rem !important; - } - .ps-xl-3 { - padding-right: 1rem !important; - } - .ps-xl-4 { - padding-right: 1.5rem !important; - } - .ps-xl-5 { - padding-right: 3rem !important; - } -} -@media (min-width: 1400px) { - .d-xxl-inline { - display: inline !important; - } - .d-xxl-inline-block { - display: inline-block !important; - } - .d-xxl-block { - display: block !important; - } - .d-xxl-grid { - display: grid !important; - } - .d-xxl-inline-grid { - display: inline-grid !important; - } - .d-xxl-table { - display: table !important; - } - .d-xxl-table-row { - display: table-row !important; - } - .d-xxl-table-cell { - display: table-cell !important; - } - .d-xxl-flex { - display: flex !important; - } - .d-xxl-inline-flex { - display: inline-flex !important; - } - .d-xxl-none { - display: none !important; - } - .flex-xxl-fill { - flex: 1 1 auto !important; - } - .flex-xxl-row { - flex-direction: row !important; - } - .flex-xxl-column { - flex-direction: column !important; - } - .flex-xxl-row-reverse { - flex-direction: row-reverse !important; - } - .flex-xxl-column-reverse { - flex-direction: column-reverse !important; - } - .flex-xxl-grow-0 { - flex-grow: 0 !important; - } - .flex-xxl-grow-1 { - flex-grow: 1 !important; - } - .flex-xxl-shrink-0 { - flex-shrink: 0 !important; - } - .flex-xxl-shrink-1 { - flex-shrink: 1 !important; - } - .flex-xxl-wrap { - flex-wrap: wrap !important; - } - .flex-xxl-nowrap { - flex-wrap: nowrap !important; - } - .flex-xxl-wrap-reverse { - flex-wrap: wrap-reverse !important; - } - .justify-content-xxl-start { - justify-content: flex-start !important; - } - .justify-content-xxl-end { - justify-content: flex-end !important; - } - .justify-content-xxl-center { - justify-content: center !important; - } - .justify-content-xxl-between { - justify-content: space-between !important; - } - .justify-content-xxl-around { - justify-content: space-around !important; - } - .justify-content-xxl-evenly { - justify-content: space-evenly !important; - } - .align-items-xxl-start { - align-items: flex-start !important; - } - .align-items-xxl-end { - align-items: flex-end !important; - } - .align-items-xxl-center { - align-items: center !important; - } - .align-items-xxl-baseline { - align-items: baseline !important; - } - .align-items-xxl-stretch { - align-items: stretch !important; - } - .align-content-xxl-start { - align-content: flex-start !important; - } - .align-content-xxl-end { - align-content: flex-end !important; - } - .align-content-xxl-center { - align-content: center !important; - } - .align-content-xxl-between { - align-content: space-between !important; - } - .align-content-xxl-around { - align-content: space-around !important; - } - .align-content-xxl-stretch { - align-content: stretch !important; - } - .align-self-xxl-auto { - align-self: auto !important; - } - .align-self-xxl-start { - align-self: flex-start !important; - } - .align-self-xxl-end { - align-self: flex-end !important; - } - .align-self-xxl-center { - align-self: center !important; - } - .align-self-xxl-baseline { - align-self: baseline !important; - } - .align-self-xxl-stretch { - align-self: stretch !important; - } - .order-xxl-first { - order: -1 !important; - } - .order-xxl-0 { - order: 0 !important; - } - .order-xxl-1 { - order: 1 !important; - } - .order-xxl-2 { - order: 2 !important; - } - .order-xxl-3 { - order: 3 !important; - } - .order-xxl-4 { - order: 4 !important; - } - .order-xxl-5 { - order: 5 !important; - } - .order-xxl-last { - order: 6 !important; - } - .m-xxl-0 { - margin: 0 !important; - } - .m-xxl-1 { - margin: 0.25rem !important; - } - .m-xxl-2 { - margin: 0.5rem !important; - } - .m-xxl-3 { - margin: 1rem !important; - } - .m-xxl-4 { - margin: 1.5rem !important; - } - .m-xxl-5 { - margin: 3rem !important; - } - .m-xxl-auto { - margin: auto !important; - } - .mx-xxl-0 { - margin-left: 0 !important; - margin-right: 0 !important; - } - .mx-xxl-1 { - margin-left: 0.25rem !important; - margin-right: 0.25rem !important; - } - .mx-xxl-2 { - margin-left: 0.5rem !important; - margin-right: 0.5rem !important; - } - .mx-xxl-3 { - margin-left: 1rem !important; - margin-right: 1rem !important; - } - .mx-xxl-4 { - margin-left: 1.5rem !important; - margin-right: 1.5rem !important; - } - .mx-xxl-5 { - margin-left: 3rem !important; - margin-right: 3rem !important; - } - .mx-xxl-auto { - margin-left: auto !important; - margin-right: auto !important; - } - .my-xxl-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; - } - .my-xxl-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; - } - .my-xxl-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; - } - .my-xxl-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; - } - .my-xxl-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; - } - .my-xxl-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; - } - .my-xxl-auto { - margin-top: auto !important; - margin-bottom: auto !important; - } - .mt-xxl-0 { - margin-top: 0 !important; - } - .mt-xxl-1 { - margin-top: 0.25rem !important; - } - .mt-xxl-2 { - margin-top: 0.5rem !important; - } - .mt-xxl-3 { - margin-top: 1rem !important; - } - .mt-xxl-4 { - margin-top: 1.5rem !important; - } - .mt-xxl-5 { - margin-top: 3rem !important; - } - .mt-xxl-auto { - margin-top: auto !important; - } - .me-xxl-0 { - margin-left: 0 !important; - } - .me-xxl-1 { - margin-left: 0.25rem !important; - } - .me-xxl-2 { - margin-left: 0.5rem !important; - } - .me-xxl-3 { - margin-left: 1rem !important; - } - .me-xxl-4 { - margin-left: 1.5rem !important; - } - .me-xxl-5 { - margin-left: 3rem !important; - } - .me-xxl-auto { - margin-left: auto !important; - } - .mb-xxl-0 { - margin-bottom: 0 !important; - } - .mb-xxl-1 { - margin-bottom: 0.25rem !important; - } - .mb-xxl-2 { - margin-bottom: 0.5rem !important; - } - .mb-xxl-3 { - margin-bottom: 1rem !important; - } - .mb-xxl-4 { - margin-bottom: 1.5rem !important; - } - .mb-xxl-5 { - margin-bottom: 3rem !important; - } - .mb-xxl-auto { - margin-bottom: auto !important; - } - .ms-xxl-0 { - margin-right: 0 !important; - } - .ms-xxl-1 { - margin-right: 0.25rem !important; - } - .ms-xxl-2 { - margin-right: 0.5rem !important; - } - .ms-xxl-3 { - margin-right: 1rem !important; - } - .ms-xxl-4 { - margin-right: 1.5rem !important; - } - .ms-xxl-5 { - margin-right: 3rem !important; - } - .ms-xxl-auto { - margin-right: auto !important; - } - .p-xxl-0 { - padding: 0 !important; - } - .p-xxl-1 { - padding: 0.25rem !important; - } - .p-xxl-2 { - padding: 0.5rem !important; - } - .p-xxl-3 { - padding: 1rem !important; - } - .p-xxl-4 { - padding: 1.5rem !important; - } - .p-xxl-5 { - padding: 3rem !important; - } - .px-xxl-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - .px-xxl-1 { - padding-left: 0.25rem !important; - padding-right: 0.25rem !important; - } - .px-xxl-2 { - padding-left: 0.5rem !important; - padding-right: 0.5rem !important; - } - .px-xxl-3 { - padding-left: 1rem !important; - padding-right: 1rem !important; - } - .px-xxl-4 { - padding-left: 1.5rem !important; - padding-right: 1.5rem !important; - } - .px-xxl-5 { - padding-left: 3rem !important; - padding-right: 3rem !important; - } - .py-xxl-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .py-xxl-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; - } - .py-xxl-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } - .py-xxl-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; - } - .py-xxl-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; - } - .py-xxl-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; - } - .pt-xxl-0 { - padding-top: 0 !important; - } - .pt-xxl-1 { - padding-top: 0.25rem !important; - } - .pt-xxl-2 { - padding-top: 0.5rem !important; - } - .pt-xxl-3 { - padding-top: 1rem !important; - } - .pt-xxl-4 { - padding-top: 1.5rem !important; - } - .pt-xxl-5 { - padding-top: 3rem !important; - } - .pe-xxl-0 { - padding-left: 0 !important; - } - .pe-xxl-1 { - padding-left: 0.25rem !important; - } - .pe-xxl-2 { - padding-left: 0.5rem !important; - } - .pe-xxl-3 { - padding-left: 1rem !important; - } - .pe-xxl-4 { - padding-left: 1.5rem !important; - } - .pe-xxl-5 { - padding-left: 3rem !important; - } - .pb-xxl-0 { - padding-bottom: 0 !important; - } - .pb-xxl-1 { - padding-bottom: 0.25rem !important; - } - .pb-xxl-2 { - padding-bottom: 0.5rem !important; - } - .pb-xxl-3 { - padding-bottom: 1rem !important; - } - .pb-xxl-4 { - padding-bottom: 1.5rem !important; - } - .pb-xxl-5 { - padding-bottom: 3rem !important; - } - .ps-xxl-0 { - padding-right: 0 !important; - } - .ps-xxl-1 { - padding-right: 0.25rem !important; - } - .ps-xxl-2 { - padding-right: 0.5rem !important; - } - .ps-xxl-3 { - padding-right: 1rem !important; - } - .ps-xxl-4 { - padding-right: 1.5rem !important; - } - .ps-xxl-5 { - padding-right: 3rem !important; - } -} -@media print { - .d-print-inline { - display: inline !important; - } - .d-print-inline-block { - display: inline-block !important; - } - .d-print-block { - display: block !important; - } - .d-print-grid { - display: grid !important; - } - .d-print-inline-grid { - display: inline-grid !important; - } - .d-print-table { - display: table !important; - } - .d-print-table-row { - display: table-row !important; - } - .d-print-table-cell { - display: table-cell !important; - } - .d-print-flex { - display: flex !important; - } - .d-print-inline-flex { - display: inline-flex !important; - } - .d-print-none { - display: none !important; - } -} -/*# sourceMappingURL=bootstrap-grid.rtl.css.map */ \ No newline at end of file diff --git a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map b/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map deleted file mode 100644 index 8df43cf..0000000 --- a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../scss/mixins/_banner.scss","../../scss/_containers.scss","../../scss/mixins/_container.scss","bootstrap-grid.css","../../scss/mixins/_breakpoints.scss","../../scss/_variables.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_utilities.scss","../../scss/utilities/_api.scss"],"names":[],"mappings":"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EE,mBAAA;EJ6+BR;EI1+BM;;IAEE,mBAAA;EJ4+BR;AACF;AKpiCQ;EAOI,0BAAA;ALgiCZ;;AKviCQ;EAOI,gCAAA;ALoiCZ;;AK3iCQ;EAOI,yBAAA;ALwiCZ;;AK/iCQ;EAOI,wBAAA;AL4iCZ;;AKnjCQ;EAOI,+BAAA;ALgjCZ;;AKvjCQ;EAOI,yBAAA;ALojCZ;;AK3jCQ;EAOI,6BAAA;ALwjCZ;;AK/jCQ;EAOI,8BAAA;AL4jCZ;;AKnkCQ;EAOI,wBAAA;ALgkCZ;;AKvkCQ;EAOI,+BAAA;ALokCZ;;AK3kCQ;EAOI,wBAAA;ALwkCZ;;AK/kCQ;EAOI,yBAAA;AL4kCZ;;AKnlCQ;EAOI,8BAAA;ALglCZ;;AKvlCQ;EAOI,iCAAA;ALolCZ;;AK3lCQ;EAOI,sCAAA;ALwlCZ;;AK/lCQ;EAOI,yCAAA;AL4lCZ;;AKnmCQ;EAOI,uBAAA;ALgmCZ;;AKvmCQ;EAOI,uBAAA;ALomCZ;;AK3mCQ;EAOI,yBAAA;ALwmCZ;;AK/mCQ;EAOI,yBAAA;AL4mCZ;;AKnnCQ;EAOI,0BAAA;ALgnCZ;;AKvnCQ;EAOI,4BAAA;ALonCZ;;AK3nCQ;EAOI,kCAAA;ALwnCZ;;AK/nCQ;EAOI,sCAAA;AL4nCZ;;AKnoCQ;EAOI,oCAAA;ALgoCZ;;AKvoCQ;EAOI,kCAAA;ALooCZ;;AK3oCQ;EAOI,yCAAA;ALwoCZ;;AK/oCQ;EAOI,wCAAA;AL4oCZ;;AKnpCQ;EAOI,wCAAA;ALgpCZ;;AKvpCQ;EAOI,kCAAA;ALopCZ;;AK3pCQ;EAOI,gCAAA;ALwpCZ;;AK/pCQ;EAOI,8BAAA;AL4pCZ;;AKnqCQ;EAOI,gCAAA;ALgqCZ;;AKvqCQ;EAOI,+BAAA;ALoqCZ;;AK3qCQ;EAOI,oCAAA;ALwqCZ;;AK/qCQ;EAOI,kCAAA;AL4qCZ;;AKnrCQ;EAOI,gCAAA;ALgrCZ;;AKvrCQ;EAOI,uCAAA;ALorCZ;;AK3rCQ;EAOI,sCAAA;ALwrCZ;;AK/rCQ;EAOI,iCAAA;AL4rCZ;;AKnsCQ;EAOI,2BAAA;ALgsCZ;;AKvsCQ;EAOI,iCAAA;ALosCZ;;AK3sCQ;EAOI,+BAAA;ALwsCZ;;AK/sCQ;EAOI,6BAAA;AL4sCZ;;AKntCQ;EA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;AL8iDZ;;AKrjDQ;EAOI,4BAAA;ALkjDZ;;AKzjDQ;EAOI,0BAAA;ALsjDZ;;AK7jDQ;EAOI,gCAAA;AL0jDZ;;AKjkDQ;EAOI,+BAAA;AL8jDZ;;AKrkDQ;EAOI,6BAAA;ALkkDZ;;AKzkDQ;EAOI,+BAAA;ALskDZ;;AK7kDQ;EAOI,6BAAA;AL0kDZ;;AKjlDQ;EAOI,4BAAA;AL8kDZ;;AKrlDQ;EAOI,kCAAA;ALklDZ;;AKzlDQ;EAOI,iCAAA;ALslDZ;;AK7lDQ;EAOI,+BAAA;AL0lDZ;;AKjmDQ;EAOI,iCAAA;AL8lDZ;;AKrmDQ;EAOI,+BAAA;ALkmDZ;;AKzmDQ;EAOI,2BAAA;ALsmDZ;;AK7mDQ;EAOI,iCAAA;AL0mDZ;;AKjnDQ;EAOI,gCAAA;AL8mDZ;;AKrnDQ;EAOI,8BAAA;ALknDZ;;AKznDQ;EAOI,gCAAA;ALsnDZ;;AK7nDQ;EAOI,8BAAA;AL0nDZ;;ACpoDI;EIGI;IAOI,0BAAA;EL+nDV;EKtoDM;IAOI,gCAAA;ELkoDV;EKzoDM;IAOI,yBAAA;ELqoDV;EK5oDM;IAOI,wBAAA;ELwoDV;EK/oDM;IAOI,+BAAA;EL2oDV;EKlpDM;IAOI,yBAAA;EL8oDV;EKrpDM;IAOI,6BAAA;ELipDV;EKxpDM;IAOI,8BAAA;ELopDV;EK3pDM;IAOI,wBAAA;ELupDV;EK9pDM;IAOI,+BAAA;EL0pDV;EKjqDM;IAOI,wBAAA;EL6pDV;EKpqDM;IAOI,yBAAA;ELgqDV;EKvqDM;IAOI,8BAAA;ELmqDV;EK1qDM;IAOI,iCAAA;ELsqDV;EK7qDM;IAOI,sCAAA;ELyqDV;EKhrDM;IAOI,yCAAA;EL4qDV;EKnrDM;IAOI,uBAAA;EL+qDV;EKtrDM;IAOI,uBAAA;ELkrDV;EKzrDM;IAOI,yBAAA;ELqrDV;EK5rDM;IAOI,yBAAA;ELwrDV;EK/rDM;IAOI,0BAAA;EL2rDV;EKlsDM;IAOI,4BAAA;EL8rDV;EKrsDM;IAOI,kCAAA;ELisDV;EKxsDM;IAOI,sCAAA;ELosDV;EK3sDM;IAOI,oCAAA;ELusDV;EK9sDM;IAOI,kCAAA;EL0sDV;EKjtDM;IAOI,yCAAA;EL6sDV;EKptDM;IAOI,wCAAA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nEV;EK/nEM;IAOI,yCAAA;EL2nEV;EKloEM;IAOI,uBAAA;EL8nEV;EKroEM;IAOI,uBAAA;ELioEV;EKxoEM;IAOI,yBAAA;ELooEV;EK3oEM;IAOI,yBAAA;ELuoEV;EK9oEM;IAOI,0BAAA;EL0oEV;EKjpEM;IAOI,4BAAA;EL6oEV;EKppEM;IAOI,kCAAA;ELgpEV;EKvpEM;IAOI,sCAAA;ELmpEV;EK1pEM;IAOI,oCAAA;ELspEV;EK7pEM;IAOI,kCAAA;ELypEV;EKhqEM;IAOI,yCAAA;EL4pEV;EKnqEM;IAOI,wCAAA;EL+pEV;EKtqEM;IAOI,wCAAA;ELkqEV;EKzqEM;IAOI,kCAAA;ELqqEV;EK5qEM;IAOI,gCAAA;ELwqEV;EK/qEM;IAOI,8BAAA;EL2qEV;EKlrEM;IAOI,gCAAA;EL8qEV;EKrrEM;IAOI,+BAAA;ELirEV;EKxrEM;IAOI,oCAAA;ELorEV;EK3rEM;IAOI,kCAAA;ELurEV;EK9rEM;IAOI,gCAAA;EL0rEV;EKjsEM;IAOI,uCAAA;EL6rEV;EKpsEM;IAOI,sCAAA;ELgsEV;EKvsEM;IAOI,iCAAA;ELmsEV;EK1sEM;IAOI,2BAAA;ELssEV;EK7sEM;IAOI,iCAAA;ELysEV;EKhtEM;IAOI,+BAAA;EL4sEV;EKntEM;IAOI,6BAAA;EL+sEV;EKttEM;IAOI,+BAAA;ELktEV;EKztEM;IAOI,8BAAA;ELqtEV;EK5tEM;IAOI,oBAAA;ELwtEV;EK/tEM;IAOI,mBAAA;EL2tEV;EKluEM;IAOI,mBAAA;EL8tEV;EKruEM;IAOI,mBAAA;ELiuEV;EKxuEM;IAOI,mBAAA;ELouEV;EK3uEM;IAOI,mBAAA;ELuuEV;EK9uEM;IAOI,mBAAA;EL0uEV;EKjvEM;IAOI,mBAAA;EL6uEV;EKpvEM;IAOI,oBAAA;ELgvEV;EKvvEM;IAOI,0BAAA;ELmvEV;EK1vEM;IAOI,yBAAA;ELsvEV;EK7vEM;IAOI,uBAAA;ELyvEV;EKhwEM;IAOI,yBAAA;EL4vEV;EKnwEM;IAOI,uBAAA;EL+vEV;EKtwEM;IAOI,uBAAA;ELkwEV;EKzwEM;IAOI,yBAAA;IAAA,0BAAA;ELswEV;EK7wEM;IAO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EKviFM;IAOI,yBAAA;ELmiFV;EK1iFM;IAOI,wBAAA;ELsiFV;EK7iFM;IAOI,+BAAA;ELyiFV;EKhjFM;IAOI,yBAAA;EL4iFV;EKnjFM;IAOI,6BAAA;EL+iFV;EKtjFM;IAOI,8BAAA;ELkjFV;EKzjFM;IAOI,wBAAA;ELqjFV;EK5jFM;IAOI,+BAAA;ELwjFV;EK/jFM;IAOI,wBAAA;EL2jFV;EKlkFM;IAOI,yBAAA;EL8jFV;EKrkFM;IAOI,8BAAA;ELikFV;EKxkFM;IAOI,iCAAA;ELokFV;EK3kFM;IAOI,sCAAA;ELukFV;EK9kFM;IAOI,yCAAA;EL0kFV;EKjlFM;IAOI,uBAAA;EL6kFV;EKplFM;IAOI,uBAAA;ELglFV;EKvlFM;IAOI,yBAAA;ELmlFV;EK1lFM;IAOI,yBAAA;ELslFV;EK7lFM;IAOI,0BAAA;ELylFV;EKhmFM;IAOI,4BAAA;EL4lFV;EKnmFM;IAOI,kCAAA;EL+lFV;EKtmFM;IAOI,sCAAA;ELkmFV;EKzmFM;IAOI,oCAAA;ELqmFV;EK5mFM;IAOI,kCAAA;ELwmFV;EK/mFM;IAOI,yCAAA;EL2mFV;EKlnFM;IAOI,wCAAA;EL8mFV;EKrnFM;IAOI,wCAAA;ELinFV;EKxnFM;IAOI,kCAAA;ELonFV;EK3nFM;IAOI,gCAAA;ELunFV;EK9nFM;IAOI,8BAAA;EL0nFV;EKjoFM;IAOI,gCAAA;EL6nFV;EKpoFM;IAOI,+BAAA;ELgoFV;EKvoFM;IAOI,oCAAA;ELmoFV;EK1oFM;IAOI,kCAAA;ELsoFV;EK7oFM;IAOI,gCAAA;ELyoFV;EKhpFM;IAOI,uCAAA;EL4oFV;EKnpFM;IAOI,sCAAA;EL+oFV;EKtpFM;IAOI,iCAAA;ELkpFV;EKzpFM;IAOI,2BAAA;ELqpFV;EK5pFM;IAOI,iCAAA;ELwpFV;EK/pFM;IAOI,+BAAA;EL2pFV;EKlqFM;IAOI,6BAAA;EL8pFV;EKrqFM;IAOI,+BAAA;ELiqFV;EKxqFM;IAOI,8BAAA;ELoqFV;EK3qFM;IAOI,oBAAA;ELuqFV;EK9qFM;IAOI,mBAAA;EL0qFV;EKjrFM;IAOI,mBAAA;EL6qFV;EKprFM;IAOI,mBAAA;ELgrFV;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AAA;ELy8FV;EKh9FM;IAOI,iCAAA;EL48FV;EKn9FM;IAOI,+BAAA;EL+8FV;EKt9FM;IAOI,iCAAA;ELk9FV;EKz9FM;IAOI,+BAAA;ELq9FV;EK59FM;IAOI,2BAAA;ELw9FV;EK/9FM;IAOI,iCAAA;EL29FV;EKl+FM;IAOI,gCAAA;EL89FV;EKr+FM;IAOI,8BAAA;ELi+FV;EKx+FM;IAOI,gCAAA;ELo+FV;EK3+FM;IAOI,8BAAA;ELu+FV;AACF;ACl/FI;EIGI;IAOI,0BAAA;EL4+FV;EKn/FM;IAOI,gCAAA;EL++FV;EKt/FM;IAOI,yBAAA;ELk/FV;EKz/FM;IAOI,wBAAA;ELq/FV;EK5/FM;IAOI,+BAAA;ELw/FV;EK//FM;IAOI,yBAAA;EL2/FV;EKlgGM;IAOI,6BAAA;EL8/FV;EKrgGM;IAOI,8BAAA;ELigGV;EKxgGM;IAOI,wBAAA;ELogGV;EK3gGM;IAOI,+BAAA;ELugGV;EK9gGM;IAOI,wBAAA;EL0gGV;EKjhGM;IAOI,yBAAA;EL6gGV;EKphGM;IAOI,8BAAA;ELghGV;EKvhGM;IAOI,iCAAA;ELmhGV;EK1hGM;IAOI,sCAAA;ELshGV;EK7hGM;IAOI,yCAAA;ELyhGV;EKhiGM;IAOI,uBAAA;EL4hGV;EKniGM;IAOI,uBAAA;EL+hGV;EKtiGM;IAOI,yBAAA;ELkiGV;EKziGM;IAOI,yBAAA;ELqiGV;EK5iGM;IAOI,0BAAA;ELwiGV;EK/iGM;IAOI,4BAAA;EL2iGV;EKljGM;IAOI,kCAAA;EL8iGV;EKrjGM;IAOI,sCAAA;ELijGV;EKxjGM;IAOI,oCAAA;ELojGV;EK3jGM;IAOI,kCAAA;ELujGV;EK9jGM;IAOI,yCAAA;EL0jGV;EKjkGM;IAOI,wCAAA;EL6jGV;EKpkGM;IAOI,wCAAA;ELgkGV;EKvkGM;IAOI,kCAAA;ELmkGV;EK1kGM;IAOI,gCAAA;ELskGV;EK7kGM;IAOI,8BAAA;ELykGV;EKhlGM;IAOI,gCAAA;EL4kGV;EKnlGM;IAOI,+BAAA;EL+kGV;EKtlGM;IAOI,oCAAA;ELklGV;EKzlGM;IAOI,kCAAA;ELqlGV;EK5lGM;IAOI,gCAAA;ELwlGV;EK/l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ELi3GV;EKx3GM;IAOI,+BAAA;ELo3GV;EK33GM;IAOI,8BAAA;ELu3GV;EK93GM;IAOI,4BAAA;EL03GV;EKj4GM;IAOI,8BAAA;EL63GV;EKp4GM;IAOI,4BAAA;ELg4GV;EKv4GM;IAOI,0BAAA;ELm4GV;EK14GM;IAOI,gCAAA;ELs4GV;EK74GM;IAOI,+BAAA;ELy4GV;EKh5GM;IAOI,6BAAA;EL44GV;EKn5GM;IAOI,+BAAA;EL+4GV;EKt5GM;IAOI,6BAAA;ELk5GV;EKz5GM;IAOI,4BAAA;ELq5GV;EK55GM;IAOI,kCAAA;ELw5GV;EK/5GM;IAOI,iCAAA;EL25GV;EKl6GM;IAOI,+BAAA;EL85GV;EKr6GM;IAOI,iCAAA;ELi6GV;EKx6GM;IAOI,+BAAA;ELo6GV;EK36GM;IAOI,2BAAA;ELu6GV;EK96GM;IAOI,iCAAA;EL06GV;EKj7GM;IAOI,gCAAA;EL66GV;EKp7GM;IAOI,8BAAA;ELg7GV;EKv7GM;IAOI,gCAAA;ELm7GV;EK17GM;IAOI,8BAAA;ELs7GV;AACF;ACj8GI;EIGI;IAOI,0BAAA;EL27GV;EKl8GM;IAOI,gCAAA;EL87GV;EKr8GM;IAOI,yBAAA;ELi8GV;EKx8GM;IAOI,wBAAA;ELo8GV;EK38GM;IAOI,+BAAA;ELu8GV;EK98GM;IAOI,yBAAA;EL08GV;EKj9GM;IAOI,6BAAA;EL68GV;EKp9GM;IAOI,8BAAA;ELg9GV;EKv9GM;IAOI,wBAAA;ELm9GV;EK19GM;IAOI,+BAAA;ELs9GV;EK79GM;IAOI,wBAAA;ELy9GV;EKh+GM;IAOI,yBAAA;EL49GV;EKn+GM;IAOI,8BAAA;EL+9GV;EKt+GM;IAOI,iCAAA;ELk+GV;EKz+GM;IAOI,sCAAA;ELq+GV;EK5+GM;IAOI,yCAAA;ELw+GV;EK/+GM;IAOI,uBAAA;EL2+GV;EKl/GM;IAOI,uBAAA;EL8+GV;EKr/GM;IAOI,yBAAA;ELi/GV;EKx/GM;IAOI,yBAAA;ELo/GV;EK3/GM;IAOI,0BAAA;ELu/GV;EK9/GM;IAOI,4BAAA;EL0/GV;EKjgHM;IAOI,kCAAA;EL6/GV;EKpgHM;IAOI,sCAAA;ELggHV;EKvgHM;IAOI,oCAAA;ELmgHV;EK1gHM;IAOI,kCAAA;ELsgHV;EK7gHM;IAOI,yCAAA;ELygHV;EKhhHM;IAOI,wCAAA;EL4gHV;EKnhHM;IAOI,wCAAA;EL+gHV;EKthHM;IAOI,kCAAA;ELkhHV;EKzhHM;IAOI,gCAAA;ELqhHV;EK5hHM;IAOI,8BAAA;ELwhHV;EK/hHM;IAOI,gCAAA;EL2hHV;EKliHM;IAOI,+BAAA;EL8hHV;EKriHM;IAOI,oCAAA;ELiiHV;EKxiHM;IAOI,kCAAA;ELoiHV;EK3iHM;IAOI,gCAAA;ELuiHV;EK9iHM;IAOI,uCAAA;EL0iHV;EKjjHM;IAOI,sCAAA;EL6iHV;EKpjHM;IAOI,iCAAA;ELgjHV;EKvjHM;IAOI,2BAAA;ELmjHV;EK1jHM;IAOI,iCAAA;ELsjHV;EK7jHM;IAOI,+BAAA;ELyjHV;EKhkHM;IAOI,6BAAA;EL4jHV;EKnkHM;IAOI,+BAAA;EL+jHV;EKtkHM;IAOI,8BAAA;ELkkHV;EKzkHM;IAOI,oBAAA;ELqkHV;EK5kHM;IAOI,mBAAA;ELwkHV;EK/kHM;IAOI,mBAAA;EL2kHV;EKllHM;IAOI,mBAAA;EL8kHV;EKrlHM;IAOI,mBAAA;ELilHV;EKxlHM;IAOI,mBAAA;ELolHV;EK3lHM;IAOI,mBAAA;ELulHV;EK9lHM;IAOI,mBAAA;EL0lHV;EKjmHM;IAOI,oBAAA;EL6lHV;EKpmHM;IAOI,0BAAA;ELgmHV;EKvmHM;IAOI,yBAAA;ELmmHV;EK1mHM;IAOI,uBAAA;ELsmHV;EK7mHM;IAOI,yBAAA;ELymHV;EKhnHM;IAOI,uBAAA;EL4mHV;EKnnHM;IAOI,uBAAA;EL+mHV;EKtnHM;IAOI,yBAAA;IAAA,0BAAA;ELmnHV;EK1nHM;IAOI,+BAAA;IAAA,gCAAA;ELunHV;EK9nHM;IAOI,8BAAA;IAAA,+BAAA;EL2nHV;EKloHM;IAOI,4BAAA;IAAA,6BAAA;EL+nHV;EKtoHM;IAOI,8BAAA;IAAA,+BAAA;ELmoHV;EK1oHM;IAOI,4BAAA;IAAA,6BAAA;ELuoHV;EK9oHM;IAOI,4BAAA;IAAA,6BAAA;EL2oHV;EKlpHM;IAOI,wBAAA;IAAA,2BAAA;EL+oHV;EKtpHM;IAOI,8BAAA;IAAA,iCAAA;ELmpHV;EK1pHM;IAOI,6BAAA;IAAA,gCAAA;ELupHV;EK9pHM;IAOI,2BAAA;IAAA,8BAAA;EL2pHV;EKlqHM;IAOI,6BAAA;IAAA,gCAAA;EL+pHV;EKtqHM;IAOI,2BAAA;IAAA,8BAAA;ELmqHV;EK1qHM;IAOI,2BAAA;IAAA,8BAAA;ELuqHV;EK9qHM;IAOI,wBAAA;EL0qHV;EKjrHM;IAOI,8BAAA;EL6qHV;EKprHM;IAOI,6BAAA;ELgrHV;EKvrHM;IAOI,2BAAA;ELmrHV;EK1rHM;IAOI,6BAAA;ELsrHV;EK7rHM;IAOI,2BAAA;ELyrHV;EKhsHM;IAOI,2BAAA;EL4rHV;EKnsHM;IAOI,yBAAA;EL+rHV;EKtsHM;IAOI,+BAAA;ELksHV;EKzsHM;IAOI,8BAAA;ELqsHV;EK5sHM;IAOI,4BAAA;ELwsHV;EK/sHM;IAOI,8BAAA;EL2sHV;EKltHM;IAOI,4BAAA;EL8sHV;EKrtHM;IAOI,4BAAA;ELitHV;EKxtHM;IAOI,2BAAA;ELotHV;EK3tHM;IAOI,iCAAA;ELutHV;EK9tHM;IAOI,gCAAA;EL0tHV;EKjuHM;IAOI,8BAAA;EL6tHV;EKpuHM;IAOI,gCAAA;ELguHV;EKvuHM;IAOI,8BAAA;ELmuHV;EK1uHM;IAOI,8BAAA;ELsuHV;EK7uHM;IAOI,0BAAA;ELyuHV;EKhvHM;IAOI,gCAAA;EL4uHV;EKnvHM;IAOI,+BAAA;EL+uHV;EKtvHM;IAOI,6BAAA;ELkvHV;EKzvHM;IAOI,+BAAA;ELqvHV;EK5vHM;IAOI,6BAAA;ELwvHV;EK/vHM;IAOI,6BAAA;EL2vHV;EKlwHM;IAOI,qBAAA;EL8vHV;EKrwHM;IAOI,2BAAA;ELiwHV;EKxwHM;IAOI,0BAAA;ELowHV;EK3wHM;IAOI,wBAAA;ELuwHV;EK9wHM;IAOI,0BAAA;EL0wHV;EKjxHM;IAOI,wBAAA;EL6wHV;EKpxHM;IAOI,0BAAA;IAAA,2BAAA;ELixHV;EKxxHM;IAOI,gCAAA;IAAA,iCAAA;ELqxHV;EK5xHM;IAOI,+BAAA;IAAA,gCAAA;ELyxHV;EKhyHM;IAOI,6BAAA;IAAA,8BAAA;EL6xHV;EKpyHM;IAOI,+BAAA;IAAA,gCAAA;ELiyHV;EKxyHM;IAOI,6BAAA;IAAA,8BAAA;ELqyHV;EK5yHM;IAOI,yBAAA;IAAA,4BAAA;ELyyHV;EKhzHM;IAOI,+BAAA;IAAA,kCAAA;EL6yHV;EKpzHM;IAOI,8BAAA;IAAA,iCAAA;ELizHV;EKxzHM;IAOI,4BAAA;IAAA,+BAAA;ELqzHV;EK5zHM;IAOI,8BAAA;IAAA,iCAAA;ELyzHV;EKh0HM;IAOI,4BAAA;IAAA,+BAAA;EL6zHV;EKp0HM;IAOI,yBAAA;ELg0HV;EKv0HM;IAOI,+BAAA;ELm0HV;EK10HM;IAOI,8BAAA;ELs0HV;EK70HM;IAOI,4BAAA;ELy0HV;EKh1HM;IAOI,8BAAA;EL40HV;EKn1HM;IAOI,4BAAA;EL+0HV;EKt1HM;IAOI,0BAAA;ELk1HV;EKz1HM;IAOI,gCAAA;ELq1HV;EK51HM;IAOI,+BAAA;ELw1HV;EK/1HM;IAOI,6BAAA;EL21HV;EKl2HM;IAOI,+BAAA;EL81HV;EKr2HM;IAOI,6BAAA;ELi2HV;EKx2HM;IAOI,4BAAA;ELo2HV;EK32HM;IAOI,kCAAA;ELu2HV;EK92HM;IAOI,iCAAA;EL02HV;EKj3HM;IAOI,+BAAA;EL62HV;EKp3HM;IAOI,iCAAA;ELg3HV;EKv3HM;IAOI,+BAAA;ELm3HV;EK13HM;IAOI,2BAAA;ELs3HV;EK73HM;IAOI,iCAAA;ELy3HV;EKh4HM;IAOI,gCAAA;EL43HV;EKn4HM;IAOI,8BAAA;EL+3HV;EKt4HM;IAOI,gCAAA;ELk4HV;EKz4HM;IAOI,8BAAA;ELq4HV;AACF;AMz6HA;ED4BQ;IAOI,0BAAA;EL04HV;EKj5HM;IAOI,gCAAA;EL64HV;EKp5HM;IAOI,yBAAA;ELg5HV;EKv5HM;IAOI,wBAAA;ELm5HV;EK15HM;IAOI,+BAAA;ELs5HV;EK75HM;IAOI,yBAAA;ELy5HV;EKh6HM;IAOI,6BAAA;EL45HV;EKn6HM;IAOI,8BAAA;EL+5HV;EKt6HM;IAOI,wBAAA;ELk6HV;EKz6HM;IAOI,+BAAA;ELq6HV;EK56HM;IAOI,wBAAA;ELw6HV;AACF","file":"bootstrap-grid.rtl.css","sourcesContent":["@mixin bsBanner($file) {\n /*!\n * Bootstrap #{$file} v5.3.3 (https://getbootstrap.com/)\n * Copyright 2011-2024 The Bootstrap Authors\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n}\n","// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n@if $enable-container-classes {\n // Single container class with breakpoint max-widths\n .container,\n // 100% wide container at all breakpoints\n .container-fluid {\n @include make-container();\n }\n\n // Responsive containers that are 100% wide until a breakpoint\n @each $breakpoint, $container-max-width in $container-max-widths {\n .container-#{$breakpoint} {\n @extend .container-fluid;\n }\n\n @include media-breakpoint-up($breakpoint, $grid-breakpoints) {\n %responsive-container-#{$breakpoint} {\n max-width: $container-max-width;\n }\n\n // Extend each breakpoint which is smaller or equal to the current breakpoint\n $extend-breakpoint: true;\n\n @each $name, $width in $grid-breakpoints {\n @if ($extend-breakpoint) {\n .container#{breakpoint-infix($name, $grid-breakpoints)} {\n @extend %responsive-container-#{$breakpoint};\n }\n\n // Once the current breakpoint is reached, stop extending\n @if ($breakpoint == $name) {\n $extend-breakpoint: false;\n }\n }\n }\n }\n }\n}\n","// Container mixins\n\n@mixin make-container($gutter: $container-padding-x) {\n --#{$prefix}gutter-x: #{$gutter};\n --#{$prefix}gutter-y: 0;\n width: 100%;\n padding-right: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list\n padding-left: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list\n margin-right: auto;\n margin-left: auto;\n}\n","/*!\n * Bootstrap Grid v5.3.3 (https://getbootstrap.com/)\n * Copyright 2011-2024 The Bootstrap Authors\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n.container,\n.container-fluid,\n.container-xxl,\n.container-xl,\n.container-lg,\n.container-md,\n.container-sm {\n --bs-gutter-x: 1.5rem;\n --bs-gutter-y: 0;\n width: 100%;\n padding-right: calc(var(--bs-gutter-x) * 0.5);\n padding-left: calc(var(--bs-gutter-x) * 0.5);\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container-sm, .container {\n max-width: 540px;\n }\n}\n@media (min-width: 768px) {\n .container-md, .container-sm, .container {\n max-width: 720px;\n }\n}\n@media (min-width: 992px) {\n .container-lg, .container-md, .container-sm, .container {\n max-width: 960px;\n }\n}\n@media (min-width: 1200px) {\n .container-xl, .container-lg, .container-md, .container-sm, .container {\n max-width: 1140px;\n }\n}\n@media (min-width: 1400px) {\n .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container {\n max-width: 1320px;\n }\n}\n:root {\n --bs-breakpoint-xs: 0;\n --bs-breakpoint-sm: 576px;\n --bs-breakpoint-md: 768px;\n --bs-breakpoint-lg: 992px;\n --bs-breakpoint-xl: 1200px;\n --bs-breakpoint-xxl: 1400px;\n}\n\n.row {\n --bs-gutter-x: 1.5rem;\n --bs-gutter-y: 0;\n display: flex;\n flex-wrap: wrap;\n margin-top: calc(-1 * var(--bs-gutter-y));\n margin-right: calc(-0.5 * var(--bs-gutter-x));\n margin-left: calc(-0.5 * var(--bs-gutter-x));\n}\n.row > * {\n box-sizing: border-box;\n flex-shrink: 0;\n width: 100%;\n max-width: 100%;\n padding-right: calc(var(--bs-gutter-x) * 0.5);\n padding-left: calc(var(--bs-gutter-x) * 0.5);\n margin-top: var(--bs-gutter-y);\n}\n\n.col {\n flex: 1 0 0%;\n}\n\n.row-cols-auto > * {\n flex: 0 0 auto;\n width: auto;\n}\n\n.row-cols-1 > * {\n flex: 0 0 auto;\n width: 100%;\n}\n\n.row-cols-2 > * {\n flex: 0 0 auto;\n width: 50%;\n}\n\n.row-cols-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n}\n\n.row-cols-4 > * {\n flex: 0 0 auto;\n width: 25%;\n}\n\n.row-cols-5 > * {\n flex: 0 0 auto;\n width: 20%;\n}\n\n.row-cols-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n}\n\n.col-auto {\n flex: 0 0 auto;\n width: auto;\n}\n\n.col-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n}\n\n.col-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n}\n\n.col-3 {\n flex: 0 0 auto;\n width: 25%;\n}\n\n.col-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n}\n\n.col-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n}\n\n.col-6 {\n flex: 0 0 auto;\n width: 50%;\n}\n\n.col-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n}\n\n.col-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n}\n\n.col-9 {\n flex: 0 0 auto;\n width: 75%;\n}\n\n.col-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n}\n\n.col-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n}\n\n.col-12 {\n flex: 0 0 auto;\n width: 100%;\n}\n\n.offset-1 {\n margin-left: 8.33333333%;\n}\n\n.offset-2 {\n margin-left: 16.66666667%;\n}\n\n.offset-3 {\n margin-left: 25%;\n}\n\n.offset-4 {\n margin-left: 33.33333333%;\n}\n\n.offset-5 {\n margin-left: 41.66666667%;\n}\n\n.offset-6 {\n margin-left: 50%;\n}\n\n.offset-7 {\n margin-left: 58.33333333%;\n}\n\n.offset-8 {\n margin-left: 66.66666667%;\n}\n\n.offset-9 {\n margin-left: 75%;\n}\n\n.offset-10 {\n margin-left: 83.33333333%;\n}\n\n.offset-11 {\n margin-left: 91.66666667%;\n}\n\n.g-0,\n.gx-0 {\n --bs-gutter-x: 0;\n}\n\n.g-0,\n.gy-0 {\n --bs-gutter-y: 0;\n}\n\n.g-1,\n.gx-1 {\n --bs-gutter-x: 0.25rem;\n}\n\n.g-1,\n.gy-1 {\n --bs-gutter-y: 0.25rem;\n}\n\n.g-2,\n.gx-2 {\n --bs-gutter-x: 0.5rem;\n}\n\n.g-2,\n.gy-2 {\n --bs-gutter-y: 0.5rem;\n}\n\n.g-3,\n.gx-3 {\n --bs-gutter-x: 1rem;\n}\n\n.g-3,\n.gy-3 {\n --bs-gutter-y: 1rem;\n}\n\n.g-4,\n.gx-4 {\n --bs-gutter-x: 1.5rem;\n}\n\n.g-4,\n.gy-4 {\n --bs-gutter-y: 1.5rem;\n}\n\n.g-5,\n.gx-5 {\n --bs-gutter-x: 3rem;\n}\n\n.g-5,\n.gy-5 {\n --bs-gutter-y: 3rem;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n flex: 1 0 0%;\n }\n .row-cols-sm-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-sm-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-sm-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-sm-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-sm-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-sm-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-sm-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-sm-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-sm-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-sm-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-sm-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-sm-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-sm-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-sm-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-sm-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-sm-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-sm-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-sm-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-sm-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-sm-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-sm-0 {\n margin-left: 0;\n }\n .offset-sm-1 {\n margin-left: 8.33333333%;\n }\n .offset-sm-2 {\n margin-left: 16.66666667%;\n }\n .offset-sm-3 {\n margin-left: 25%;\n }\n .offset-sm-4 {\n margin-left: 33.33333333%;\n }\n .offset-sm-5 {\n margin-left: 41.66666667%;\n }\n .offset-sm-6 {\n margin-left: 50%;\n }\n .offset-sm-7 {\n margin-left: 58.33333333%;\n }\n .offset-sm-8 {\n margin-left: 66.66666667%;\n }\n .offset-sm-9 {\n margin-left: 75%;\n }\n .offset-sm-10 {\n margin-left: 83.33333333%;\n }\n .offset-sm-11 {\n margin-left: 91.66666667%;\n }\n .g-sm-0,\n .gx-sm-0 {\n --bs-gutter-x: 0;\n }\n .g-sm-0,\n .gy-sm-0 {\n --bs-gutter-y: 0;\n }\n .g-sm-1,\n .gx-sm-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-sm-1,\n .gy-sm-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-sm-2,\n .gx-sm-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-sm-2,\n .gy-sm-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-sm-3,\n .gx-sm-3 {\n --bs-gutter-x: 1rem;\n }\n .g-sm-3,\n .gy-sm-3 {\n --bs-gutter-y: 1rem;\n }\n .g-sm-4,\n .gx-sm-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-sm-4,\n .gy-sm-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-sm-5,\n .gx-sm-5 {\n --bs-gutter-x: 3rem;\n }\n .g-sm-5,\n .gy-sm-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 768px) {\n .col-md {\n flex: 1 0 0%;\n }\n .row-cols-md-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-md-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-md-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-md-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-md-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-md-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-md-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-md-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-md-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-md-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-md-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-md-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-md-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-md-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-md-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-md-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-md-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-md-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-md-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-md-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-md-0 {\n margin-left: 0;\n }\n .offset-md-1 {\n margin-left: 8.33333333%;\n }\n .offset-md-2 {\n margin-left: 16.66666667%;\n }\n .offset-md-3 {\n margin-left: 25%;\n }\n .offset-md-4 {\n margin-left: 33.33333333%;\n }\n .offset-md-5 {\n margin-left: 41.66666667%;\n }\n .offset-md-6 {\n margin-left: 50%;\n }\n .offset-md-7 {\n margin-left: 58.33333333%;\n }\n .offset-md-8 {\n margin-left: 66.66666667%;\n }\n .offset-md-9 {\n margin-left: 75%;\n }\n .offset-md-10 {\n margin-left: 83.33333333%;\n }\n .offset-md-11 {\n margin-left: 91.66666667%;\n }\n .g-md-0,\n .gx-md-0 {\n --bs-gutter-x: 0;\n }\n .g-md-0,\n .gy-md-0 {\n --bs-gutter-y: 0;\n }\n .g-md-1,\n .gx-md-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-md-1,\n .gy-md-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-md-2,\n .gx-md-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-md-2,\n .gy-md-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-md-3,\n .gx-md-3 {\n --bs-gutter-x: 1rem;\n }\n .g-md-3,\n .gy-md-3 {\n --bs-gutter-y: 1rem;\n }\n .g-md-4,\n .gx-md-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-md-4,\n .gy-md-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-md-5,\n .gx-md-5 {\n --bs-gutter-x: 3rem;\n }\n .g-md-5,\n .gy-md-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 992px) {\n .col-lg {\n flex: 1 0 0%;\n }\n .row-cols-lg-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-lg-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-lg-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-lg-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-lg-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-lg-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-lg-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-lg-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-lg-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-lg-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-lg-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-lg-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-lg-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-lg-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-lg-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-lg-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-lg-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-lg-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-lg-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-lg-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-lg-0 {\n margin-left: 0;\n }\n .offset-lg-1 {\n margin-left: 8.33333333%;\n }\n .offset-lg-2 {\n margin-left: 16.66666667%;\n }\n .offset-lg-3 {\n margin-left: 25%;\n }\n .offset-lg-4 {\n margin-left: 33.33333333%;\n }\n .offset-lg-5 {\n margin-left: 41.66666667%;\n }\n .offset-lg-6 {\n margin-left: 50%;\n }\n .offset-lg-7 {\n margin-left: 58.33333333%;\n }\n .offset-lg-8 {\n margin-left: 66.66666667%;\n }\n .offset-lg-9 {\n margin-left: 75%;\n }\n .offset-lg-10 {\n margin-left: 83.33333333%;\n }\n .offset-lg-11 {\n margin-left: 91.66666667%;\n }\n .g-lg-0,\n .gx-lg-0 {\n --bs-gutter-x: 0;\n }\n .g-lg-0,\n .gy-lg-0 {\n --bs-gutter-y: 0;\n }\n .g-lg-1,\n .gx-lg-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-lg-1,\n .gy-lg-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-lg-2,\n .gx-lg-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-lg-2,\n .gy-lg-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-lg-3,\n .gx-lg-3 {\n --bs-gutter-x: 1rem;\n }\n .g-lg-3,\n .gy-lg-3 {\n --bs-gutter-y: 1rem;\n }\n .g-lg-4,\n .gx-lg-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-lg-4,\n .gy-lg-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-lg-5,\n .gx-lg-5 {\n --bs-gutter-x: 3rem;\n }\n .g-lg-5,\n .gy-lg-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 1200px) {\n .col-xl {\n flex: 1 0 0%;\n }\n .row-cols-xl-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-xl-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-xl-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-xl-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-xl-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-xl-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-xl-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xl-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-xl-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-xl-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xl-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-xl-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-xl-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-xl-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-xl-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-xl-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-xl-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-xl-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-xl-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-xl-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-xl-0 {\n margin-left: 0;\n }\n .offset-xl-1 {\n margin-left: 8.33333333%;\n }\n .offset-xl-2 {\n margin-left: 16.66666667%;\n }\n .offset-xl-3 {\n margin-left: 25%;\n }\n .offset-xl-4 {\n margin-left: 33.33333333%;\n }\n .offset-xl-5 {\n margin-left: 41.66666667%;\n }\n .offset-xl-6 {\n margin-left: 50%;\n }\n .offset-xl-7 {\n margin-left: 58.33333333%;\n }\n .offset-xl-8 {\n margin-left: 66.66666667%;\n }\n .offset-xl-9 {\n margin-left: 75%;\n }\n .offset-xl-10 {\n margin-left: 83.33333333%;\n }\n .offset-xl-11 {\n margin-left: 91.66666667%;\n }\n .g-xl-0,\n .gx-xl-0 {\n --bs-gutter-x: 0;\n }\n .g-xl-0,\n .gy-xl-0 {\n --bs-gutter-y: 0;\n }\n .g-xl-1,\n .gx-xl-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-xl-1,\n .gy-xl-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-xl-2,\n .gx-xl-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-xl-2,\n .gy-xl-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-xl-3,\n .gx-xl-3 {\n --bs-gutter-x: 1rem;\n }\n .g-xl-3,\n .gy-xl-3 {\n --bs-gutter-y: 1rem;\n }\n .g-xl-4,\n .gx-xl-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-xl-4,\n .gy-xl-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-xl-5,\n .gx-xl-5 {\n --bs-gutter-x: 3rem;\n }\n .g-xl-5,\n .gy-xl-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 1400px) {\n .col-xxl {\n flex: 1 0 0%;\n }\n .row-cols-xxl-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-xxl-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-xxl-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-xxl-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-xxl-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-xxl-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-xxl-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xxl-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-xxl-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-xxl-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xxl-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-xxl-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-xxl-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-xxl-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-xxl-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-xxl-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-xxl-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-xxl-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-xxl-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-xxl-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-xxl-0 {\n margin-left: 0;\n }\n .offset-xxl-1 {\n margin-left: 8.33333333%;\n }\n .offset-xxl-2 {\n margin-left: 16.66666667%;\n }\n .offset-xxl-3 {\n margin-left: 25%;\n }\n .offset-xxl-4 {\n margin-left: 33.33333333%;\n }\n .offset-xxl-5 {\n margin-left: 41.66666667%;\n }\n .offset-xxl-6 {\n margin-left: 50%;\n }\n .offset-xxl-7 {\n margin-left: 58.33333333%;\n }\n .offset-xxl-8 {\n margin-left: 66.66666667%;\n }\n .offset-xxl-9 {\n margin-left: 75%;\n }\n .offset-xxl-10 {\n margin-left: 83.33333333%;\n }\n .offset-xxl-11 {\n margin-left: 91.66666667%;\n }\n .g-xxl-0,\n .gx-xxl-0 {\n --bs-gutter-x: 0;\n }\n .g-xxl-0,\n .gy-xxl-0 {\n --bs-gutter-y: 0;\n }\n .g-xxl-1,\n .gx-xxl-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-xxl-1,\n .gy-xxl-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-xxl-2,\n .gx-xxl-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-xxl-2,\n .gy-xxl-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-xxl-3,\n .gx-xxl-3 {\n --bs-gutter-x: 1rem;\n }\n .g-xxl-3,\n .gy-xxl-3 {\n --bs-gutter-y: 1rem;\n }\n .g-xxl-4,\n .gx-xxl-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-xxl-4,\n .gy-xxl-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-xxl-5,\n .gx-xxl-5 {\n --bs-gutter-x: 3rem;\n }\n .g-xxl-5,\n .gy-xxl-5 {\n --bs-gutter-y: 3rem;\n }\n}\n.d-inline {\n display: inline !important;\n}\n\n.d-inline-block {\n display: inline-block !important;\n}\n\n.d-block {\n display: block !important;\n}\n\n.d-grid {\n display: grid !important;\n}\n\n.d-inline-grid {\n display: inline-grid !important;\n}\n\n.d-table {\n display: table !important;\n}\n\n.d-table-row {\n display: table-row !important;\n}\n\n.d-table-cell {\n display: table-cell !important;\n}\n\n.d-flex {\n display: flex !important;\n}\n\n.d-inline-flex {\n display: inline-flex !important;\n}\n\n.d-none {\n display: none !important;\n}\n\n.flex-fill {\n flex: 1 1 auto !important;\n}\n\n.flex-row {\n flex-direction: row !important;\n}\n\n.flex-column {\n flex-direction: column !important;\n}\n\n.flex-row-reverse {\n flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n flex-direction: column-reverse !important;\n}\n\n.flex-grow-0 {\n flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n flex-shrink: 1 !important;\n}\n\n.flex-wrap {\n flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n}\n\n.justify-content-start {\n justify-content: flex-start !important;\n}\n\n.justify-content-end {\n justify-content: flex-end !important;\n}\n\n.justify-content-center {\n justify-content: center !important;\n}\n\n.justify-content-between {\n justify-content: space-between !important;\n}\n\n.justify-content-around {\n justify-content: space-around !important;\n}\n\n.justify-content-evenly {\n justify-content: space-evenly !important;\n}\n\n.align-items-start {\n align-items: flex-start !important;\n}\n\n.align-items-end {\n align-items: flex-end !important;\n}\n\n.align-items-center {\n align-items: center !important;\n}\n\n.align-items-baseline {\n align-items: baseline !important;\n}\n\n.align-items-stretch {\n align-items: stretch !important;\n}\n\n.align-content-start {\n align-content: flex-start !important;\n}\n\n.align-content-end {\n align-content: flex-end !important;\n}\n\n.align-content-center {\n align-content: center !important;\n}\n\n.align-content-between {\n align-content: space-between !important;\n}\n\n.align-content-around {\n align-content: space-around !important;\n}\n\n.align-content-stretch {\n align-content: stretch !important;\n}\n\n.align-self-auto {\n align-self: auto !important;\n}\n\n.align-self-start {\n align-self: flex-start !important;\n}\n\n.align-self-end {\n align-self: flex-end !important;\n}\n\n.align-self-center {\n align-self: center !important;\n}\n\n.align-self-baseline {\n align-self: baseline !important;\n}\n\n.align-self-stretch {\n align-self: stretch !important;\n}\n\n.order-first {\n order: -1 !important;\n}\n\n.order-0 {\n order: 0 !important;\n}\n\n.order-1 {\n order: 1 !important;\n}\n\n.order-2 {\n order: 2 !important;\n}\n\n.order-3 {\n order: 3 !important;\n}\n\n.order-4 {\n order: 4 !important;\n}\n\n.order-5 {\n order: 5 !important;\n}\n\n.order-last {\n order: 6 !important;\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mx-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n}\n\n.mx-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n}\n\n.mx-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n}\n\n.mx-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n}\n\n.mx-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n}\n\n.mx-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n}\n\n.mx-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n}\n\n.my-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n.my-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n}\n\n.my-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n}\n\n.my-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n}\n\n.my-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n}\n\n.my-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n}\n\n.my-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n}\n\n.mt-0 {\n margin-top: 0 !important;\n}\n\n.mt-1 {\n margin-top: 0.25rem !important;\n}\n\n.mt-2 {\n margin-top: 0.5rem !important;\n}\n\n.mt-3 {\n margin-top: 1rem !important;\n}\n\n.mt-4 {\n margin-top: 1.5rem !important;\n}\n\n.mt-5 {\n margin-top: 3rem !important;\n}\n\n.mt-auto {\n margin-top: auto !important;\n}\n\n.me-0 {\n margin-right: 0 !important;\n}\n\n.me-1 {\n margin-right: 0.25rem !important;\n}\n\n.me-2 {\n margin-right: 0.5rem !important;\n}\n\n.me-3 {\n margin-right: 1rem !important;\n}\n\n.me-4 {\n margin-right: 1.5rem !important;\n}\n\n.me-5 {\n margin-right: 3rem !important;\n}\n\n.me-auto {\n margin-right: auto !important;\n}\n\n.mb-0 {\n margin-bottom: 0 !important;\n}\n\n.mb-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.mb-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.mb-3 {\n margin-bottom: 1rem !important;\n}\n\n.mb-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.mb-5 {\n margin-bottom: 3rem !important;\n}\n\n.mb-auto {\n margin-bottom: auto !important;\n}\n\n.ms-0 {\n margin-left: 0 !important;\n}\n\n.ms-1 {\n margin-left: 0.25rem !important;\n}\n\n.ms-2 {\n margin-left: 0.5rem !important;\n}\n\n.ms-3 {\n margin-left: 1rem !important;\n}\n\n.ms-4 {\n margin-left: 1.5rem !important;\n}\n\n.ms-5 {\n margin-left: 3rem !important;\n}\n\n.ms-auto {\n margin-left: auto !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.px-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n}\n\n.px-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n}\n\n.px-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n}\n\n.px-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n}\n\n.px-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n}\n\n.px-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n}\n\n.py-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n}\n\n.py-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n}\n\n.py-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n}\n\n.py-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n}\n\n.py-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n}\n\n.py-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n}\n\n.pt-0 {\n padding-top: 0 !important;\n}\n\n.pt-1 {\n padding-top: 0.25rem !important;\n}\n\n.pt-2 {\n padding-top: 0.5rem !important;\n}\n\n.pt-3 {\n padding-top: 1rem !important;\n}\n\n.pt-4 {\n padding-top: 1.5rem !important;\n}\n\n.pt-5 {\n padding-top: 3rem !important;\n}\n\n.pe-0 {\n padding-right: 0 !important;\n}\n\n.pe-1 {\n padding-right: 0.25rem !important;\n}\n\n.pe-2 {\n padding-right: 0.5rem !important;\n}\n\n.pe-3 {\n padding-right: 1rem !important;\n}\n\n.pe-4 {\n padding-right: 1.5rem !important;\n}\n\n.pe-5 {\n padding-right: 3rem !important;\n}\n\n.pb-0 {\n padding-bottom: 0 !important;\n}\n\n.pb-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pb-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pb-3 {\n padding-bottom: 1rem !important;\n}\n\n.pb-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pb-5 {\n padding-bottom: 3rem !important;\n}\n\n.ps-0 {\n padding-left: 0 !important;\n}\n\n.ps-1 {\n padding-left: 0.25rem !important;\n}\n\n.ps-2 {\n padding-left: 0.5rem !important;\n}\n\n.ps-3 {\n padding-left: 1rem !important;\n}\n\n.ps-4 {\n padding-left: 1.5rem !important;\n}\n\n.ps-5 {\n padding-left: 3rem !important;\n}\n\n@media (min-width: 576px) {\n .d-sm-inline {\n display: inline !important;\n }\n .d-sm-inline-block {\n display: inline-block !important;\n }\n .d-sm-block {\n display: block !important;\n }\n .d-sm-grid {\n display: grid !important;\n }\n .d-sm-inline-grid {\n display: inline-grid !important;\n }\n .d-sm-table {\n display: table !important;\n }\n .d-sm-table-row {\n display: table-row !important;\n }\n .d-sm-table-cell {\n display: table-cell !important;\n }\n .d-sm-flex {\n display: flex !important;\n }\n .d-sm-inline-flex {\n display: inline-flex !important;\n }\n .d-sm-none {\n display: none !important;\n }\n .flex-sm-fill {\n flex: 1 1 auto !important;\n }\n .flex-sm-row {\n flex-direction: row !important;\n }\n .flex-sm-column {\n flex-direction: column !important;\n }\n .flex-sm-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-sm-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-sm-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-sm-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-sm-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-sm-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-sm-wrap {\n flex-wrap: wrap !important;\n }\n .flex-sm-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-sm-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-sm-start {\n justify-content: flex-start !important;\n }\n .justify-content-sm-end {\n justify-content: flex-end !important;\n }\n .justify-content-sm-center {\n justify-content: center !important;\n }\n .justify-content-sm-between {\n justify-content: space-between !important;\n }\n .justify-content-sm-around {\n justify-content: space-around !important;\n }\n .justify-content-sm-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-sm-start {\n align-items: flex-start !important;\n }\n .align-items-sm-end {\n align-items: flex-end !important;\n }\n .align-items-sm-center {\n align-items: center !important;\n }\n .align-items-sm-baseline {\n align-items: baseline !important;\n }\n .align-items-sm-stretch {\n align-items: stretch !important;\n }\n .align-content-sm-start {\n align-content: flex-start !important;\n }\n .align-content-sm-end {\n align-content: flex-end !important;\n }\n .align-content-sm-center {\n align-content: center !important;\n }\n .align-content-sm-between {\n align-content: space-between !important;\n }\n .align-content-sm-around {\n align-content: space-around !important;\n }\n .align-content-sm-stretch {\n align-content: stretch !important;\n }\n .align-self-sm-auto {\n align-self: auto !important;\n }\n .align-self-sm-start {\n align-self: flex-start !important;\n }\n .align-self-sm-end {\n align-self: flex-end !important;\n }\n .align-self-sm-center {\n align-self: center !important;\n }\n .align-self-sm-baseline {\n align-self: baseline !important;\n }\n .align-self-sm-stretch {\n align-self: stretch !important;\n }\n .order-sm-first {\n order: -1 !important;\n }\n .order-sm-0 {\n order: 0 !important;\n }\n .order-sm-1 {\n order: 1 !important;\n }\n .order-sm-2 {\n order: 2 !important;\n }\n .order-sm-3 {\n order: 3 !important;\n }\n .order-sm-4 {\n order: 4 !important;\n }\n .order-sm-5 {\n order: 5 !important;\n }\n .order-sm-last {\n order: 6 !important;\n }\n .m-sm-0 {\n margin: 0 !important;\n }\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n .m-sm-3 {\n margin: 1rem !important;\n }\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n .m-sm-5 {\n margin: 3rem !important;\n }\n .m-sm-auto {\n margin: auto !important;\n }\n .mx-sm-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-sm-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-sm-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-sm-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-sm-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-sm-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-sm-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-sm-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-sm-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-sm-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-sm-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-sm-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-sm-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-sm-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-sm-0 {\n margin-top: 0 !important;\n }\n .mt-sm-1 {\n margin-top: 0.25rem !important;\n }\n .mt-sm-2 {\n margin-top: 0.5rem !important;\n }\n .mt-sm-3 {\n margin-top: 1rem !important;\n }\n .mt-sm-4 {\n margin-top: 1.5rem !important;\n }\n .mt-sm-5 {\n margin-top: 3rem !important;\n }\n .mt-sm-auto {\n margin-top: auto !important;\n }\n .me-sm-0 {\n margin-right: 0 !important;\n }\n .me-sm-1 {\n margin-right: 0.25rem !important;\n }\n .me-sm-2 {\n margin-right: 0.5rem !important;\n }\n .me-sm-3 {\n margin-right: 1rem !important;\n }\n .me-sm-4 {\n margin-right: 1.5rem !important;\n }\n .me-sm-5 {\n margin-right: 3rem !important;\n }\n .me-sm-auto {\n margin-right: auto !important;\n }\n .mb-sm-0 {\n margin-bottom: 0 !important;\n }\n .mb-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-sm-3 {\n margin-bottom: 1rem !important;\n }\n .mb-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-sm-5 {\n margin-bottom: 3rem !important;\n }\n .mb-sm-auto {\n margin-bottom: auto !important;\n }\n .ms-sm-0 {\n margin-left: 0 !important;\n }\n .ms-sm-1 {\n margin-left: 0.25rem !important;\n }\n .ms-sm-2 {\n margin-left: 0.5rem !important;\n }\n .ms-sm-3 {\n margin-left: 1rem !important;\n }\n .ms-sm-4 {\n margin-left: 1.5rem !important;\n }\n .ms-sm-5 {\n margin-left: 3rem !important;\n }\n .ms-sm-auto {\n margin-left: auto !important;\n }\n .p-sm-0 {\n padding: 0 !important;\n }\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n .p-sm-3 {\n padding: 1rem !important;\n }\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n .p-sm-5 {\n padding: 3rem !important;\n }\n .px-sm-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-sm-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-sm-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-sm-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-sm-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-sm-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-sm-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-sm-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-sm-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-sm-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-sm-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-sm-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-sm-0 {\n padding-top: 0 !important;\n }\n .pt-sm-1 {\n padding-top: 0.25rem !important;\n }\n .pt-sm-2 {\n padding-top: 0.5rem !important;\n }\n .pt-sm-3 {\n padding-top: 1rem !important;\n }\n .pt-sm-4 {\n padding-top: 1.5rem !important;\n }\n .pt-sm-5 {\n padding-top: 3rem !important;\n }\n .pe-sm-0 {\n padding-right: 0 !important;\n }\n .pe-sm-1 {\n padding-right: 0.25rem !important;\n }\n .pe-sm-2 {\n padding-right: 0.5rem !important;\n }\n .pe-sm-3 {\n padding-right: 1rem !important;\n }\n .pe-sm-4 {\n padding-right: 1.5rem !important;\n }\n .pe-sm-5 {\n padding-right: 3rem !important;\n }\n .pb-sm-0 {\n padding-bottom: 0 !important;\n }\n .pb-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-sm-3 {\n padding-bottom: 1rem !important;\n }\n .pb-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-sm-5 {\n padding-bottom: 3rem !important;\n }\n .ps-sm-0 {\n padding-left: 0 !important;\n }\n .ps-sm-1 {\n padding-left: 0.25rem !important;\n }\n .ps-sm-2 {\n padding-left: 0.5rem !important;\n }\n .ps-sm-3 {\n padding-left: 1rem !important;\n }\n .ps-sm-4 {\n padding-left: 1.5rem !important;\n }\n .ps-sm-5 {\n padding-left: 3rem !important;\n }\n}\n@media (min-width: 768px) {\n .d-md-inline {\n display: inline !important;\n }\n .d-md-inline-block {\n display: inline-block !important;\n }\n .d-md-block {\n display: block !important;\n }\n .d-md-grid {\n display: grid !important;\n }\n .d-md-inline-grid {\n display: inline-grid !important;\n }\n .d-md-table {\n display: table !important;\n }\n .d-md-table-row {\n display: table-row !important;\n }\n .d-md-table-cell {\n display: table-cell !important;\n }\n .d-md-flex {\n display: flex !important;\n }\n .d-md-inline-flex {\n display: inline-flex !important;\n }\n .d-md-none {\n display: none !important;\n }\n .flex-md-fill {\n flex: 1 1 auto !important;\n }\n .flex-md-row {\n flex-direction: row !important;\n }\n .flex-md-column {\n flex-direction: column !important;\n }\n .flex-md-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-md-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-md-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-md-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-md-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-md-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-md-wrap {\n flex-wrap: wrap !important;\n }\n .flex-md-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-md-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-md-start {\n justify-content: flex-start !important;\n }\n .justify-content-md-end {\n justify-content: flex-end !important;\n }\n .justify-content-md-center {\n justify-content: center !important;\n }\n .justify-content-md-between {\n justify-content: space-between !important;\n }\n .justify-content-md-around {\n justify-content: space-around !important;\n }\n .justify-content-md-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-md-start {\n align-items: flex-start !important;\n }\n .align-items-md-end {\n align-items: flex-end !important;\n }\n .align-items-md-center {\n align-items: center !important;\n }\n .align-items-md-baseline {\n align-items: baseline !important;\n }\n .align-items-md-stretch {\n align-items: stretch !important;\n }\n .align-content-md-start {\n align-content: flex-start !important;\n }\n .align-content-md-end {\n align-content: flex-end !important;\n }\n .align-content-md-center {\n align-content: center !important;\n }\n .align-content-md-between {\n align-content: space-between !important;\n }\n .align-content-md-around {\n align-content: space-around !important;\n }\n .align-content-md-stretch {\n align-content: stretch !important;\n }\n .align-self-md-auto {\n align-self: auto !important;\n }\n .align-self-md-start {\n align-self: flex-start !important;\n }\n .align-self-md-end {\n align-self: flex-end !important;\n }\n .align-self-md-center {\n align-self: center !important;\n }\n .align-self-md-baseline {\n align-self: baseline !important;\n }\n .align-self-md-stretch {\n align-self: stretch !important;\n }\n .order-md-first {\n order: -1 !important;\n }\n .order-md-0 {\n order: 0 !important;\n }\n .order-md-1 {\n order: 1 !important;\n }\n .order-md-2 {\n order: 2 !important;\n }\n .order-md-3 {\n order: 3 !important;\n }\n .order-md-4 {\n order: 4 !important;\n }\n .order-md-5 {\n order: 5 !important;\n }\n .order-md-last {\n order: 6 !important;\n }\n .m-md-0 {\n margin: 0 !important;\n }\n .m-md-1 {\n margin: 0.25rem !important;\n }\n .m-md-2 {\n margin: 0.5rem !important;\n }\n .m-md-3 {\n margin: 1rem !important;\n }\n .m-md-4 {\n margin: 1.5rem !important;\n }\n .m-md-5 {\n margin: 3rem !important;\n }\n .m-md-auto {\n margin: auto !important;\n }\n .mx-md-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-md-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-md-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-md-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-md-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-md-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-md-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-md-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-md-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-md-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-md-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-md-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-md-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-md-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-md-0 {\n margin-top: 0 !important;\n }\n .mt-md-1 {\n margin-top: 0.25rem !important;\n }\n .mt-md-2 {\n margin-top: 0.5rem !important;\n }\n .mt-md-3 {\n margin-top: 1rem !important;\n }\n .mt-md-4 {\n margin-top: 1.5rem !important;\n }\n .mt-md-5 {\n margin-top: 3rem !important;\n }\n .mt-md-auto {\n margin-top: auto !important;\n }\n .me-md-0 {\n margin-right: 0 !important;\n }\n .me-md-1 {\n margin-right: 0.25rem !important;\n }\n .me-md-2 {\n margin-right: 0.5rem !important;\n }\n .me-md-3 {\n margin-right: 1rem !important;\n }\n .me-md-4 {\n margin-right: 1.5rem !important;\n }\n .me-md-5 {\n margin-right: 3rem !important;\n }\n .me-md-auto {\n margin-right: auto !important;\n }\n .mb-md-0 {\n margin-bottom: 0 !important;\n }\n .mb-md-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-md-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-md-3 {\n margin-bottom: 1rem !important;\n }\n .mb-md-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-md-5 {\n margin-bottom: 3rem !important;\n }\n .mb-md-auto {\n margin-bottom: auto !important;\n }\n .ms-md-0 {\n margin-left: 0 !important;\n }\n .ms-md-1 {\n margin-left: 0.25rem !important;\n }\n .ms-md-2 {\n margin-left: 0.5rem !important;\n }\n .ms-md-3 {\n margin-left: 1rem !important;\n }\n .ms-md-4 {\n margin-left: 1.5rem !important;\n }\n .ms-md-5 {\n margin-left: 3rem !important;\n }\n .ms-md-auto {\n margin-left: auto !important;\n }\n .p-md-0 {\n padding: 0 !important;\n }\n .p-md-1 {\n padding: 0.25rem !important;\n }\n .p-md-2 {\n padding: 0.5rem !important;\n }\n .p-md-3 {\n padding: 1rem !important;\n }\n .p-md-4 {\n padding: 1.5rem !important;\n }\n .p-md-5 {\n padding: 3rem !important;\n }\n .px-md-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-md-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-md-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-md-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-md-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-md-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-md-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-md-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-md-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-md-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-md-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-md-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-md-0 {\n padding-top: 0 !important;\n }\n .pt-md-1 {\n padding-top: 0.25rem !important;\n }\n .pt-md-2 {\n padding-top: 0.5rem !important;\n }\n .pt-md-3 {\n padding-top: 1rem !important;\n }\n .pt-md-4 {\n padding-top: 1.5rem !important;\n }\n .pt-md-5 {\n padding-top: 3rem !important;\n }\n .pe-md-0 {\n padding-right: 0 !important;\n }\n .pe-md-1 {\n padding-right: 0.25rem !important;\n }\n .pe-md-2 {\n padding-right: 0.5rem !important;\n }\n .pe-md-3 {\n padding-right: 1rem !important;\n }\n .pe-md-4 {\n padding-right: 1.5rem !important;\n }\n .pe-md-5 {\n padding-right: 3rem !important;\n }\n .pb-md-0 {\n padding-bottom: 0 !important;\n }\n .pb-md-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-md-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-md-3 {\n padding-bottom: 1rem !important;\n }\n .pb-md-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-md-5 {\n padding-bottom: 3rem !important;\n }\n .ps-md-0 {\n padding-left: 0 !important;\n }\n .ps-md-1 {\n padding-left: 0.25rem !important;\n }\n .ps-md-2 {\n padding-left: 0.5rem !important;\n }\n .ps-md-3 {\n padding-left: 1rem !important;\n }\n .ps-md-4 {\n padding-left: 1.5rem !important;\n }\n .ps-md-5 {\n padding-left: 3rem !important;\n }\n}\n@media (min-width: 992px) {\n .d-lg-inline {\n display: inline !important;\n }\n .d-lg-inline-block {\n display: inline-block !important;\n }\n .d-lg-block {\n display: block !important;\n }\n .d-lg-grid {\n display: grid !important;\n }\n .d-lg-inline-grid {\n display: inline-grid !important;\n }\n .d-lg-table {\n display: table !important;\n }\n .d-lg-table-row {\n display: table-row !important;\n }\n .d-lg-table-cell {\n display: table-cell !important;\n }\n .d-lg-flex {\n display: flex !important;\n }\n .d-lg-inline-flex {\n display: inline-flex !important;\n }\n .d-lg-none {\n display: none !important;\n }\n .flex-lg-fill {\n flex: 1 1 auto !important;\n }\n .flex-lg-row {\n flex-direction: row !important;\n }\n .flex-lg-column {\n flex-direction: column !important;\n }\n .flex-lg-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-lg-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-lg-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-lg-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-lg-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-lg-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-lg-wrap {\n flex-wrap: wrap !important;\n }\n .flex-lg-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-lg-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-lg-start {\n justify-content: flex-start !important;\n }\n .justify-content-lg-end {\n justify-content: flex-end !important;\n }\n .justify-content-lg-center {\n justify-content: center !important;\n }\n .justify-content-lg-between {\n justify-content: space-between !important;\n }\n .justify-content-lg-around {\n justify-content: space-around !important;\n }\n .justify-content-lg-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-lg-start {\n align-items: flex-start !important;\n }\n .align-items-lg-end {\n align-items: flex-end !important;\n }\n .align-items-lg-center {\n align-items: center !important;\n }\n .align-items-lg-baseline {\n align-items: baseline !important;\n }\n .align-items-lg-stretch {\n align-items: stretch !important;\n }\n .align-content-lg-start {\n align-content: flex-start !important;\n }\n .align-content-lg-end {\n align-content: flex-end !important;\n }\n .align-content-lg-center {\n align-content: center !important;\n }\n .align-content-lg-between {\n align-content: space-between !important;\n }\n .align-content-lg-around {\n align-content: space-around !important;\n }\n .align-content-lg-stretch {\n align-content: stretch !important;\n }\n .align-self-lg-auto {\n align-self: auto !important;\n }\n .align-self-lg-start {\n align-self: flex-start !important;\n }\n .align-self-lg-end {\n align-self: flex-end !important;\n }\n .align-self-lg-center {\n align-self: center !important;\n }\n .align-self-lg-baseline {\n align-self: baseline !important;\n }\n .align-self-lg-stretch {\n align-self: stretch !important;\n }\n .order-lg-first {\n order: -1 !important;\n }\n .order-lg-0 {\n order: 0 !important;\n }\n .order-lg-1 {\n order: 1 !important;\n }\n .order-lg-2 {\n order: 2 !important;\n }\n .order-lg-3 {\n order: 3 !important;\n }\n .order-lg-4 {\n order: 4 !important;\n }\n .order-lg-5 {\n order: 5 !important;\n }\n .order-lg-last {\n order: 6 !important;\n }\n .m-lg-0 {\n margin: 0 !important;\n }\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n .m-lg-3 {\n margin: 1rem !important;\n }\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n .m-lg-5 {\n margin: 3rem !important;\n }\n .m-lg-auto {\n margin: auto !important;\n }\n .mx-lg-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-lg-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-lg-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-lg-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-lg-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-lg-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-lg-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-lg-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-lg-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-lg-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-lg-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-lg-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-lg-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-lg-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-lg-0 {\n margin-top: 0 !important;\n }\n .mt-lg-1 {\n margin-top: 0.25rem !important;\n }\n .mt-lg-2 {\n margin-top: 0.5rem !important;\n }\n .mt-lg-3 {\n margin-top: 1rem !important;\n }\n .mt-lg-4 {\n margin-top: 1.5rem !important;\n }\n .mt-lg-5 {\n margin-top: 3rem !important;\n }\n .mt-lg-auto {\n margin-top: auto !important;\n }\n .me-lg-0 {\n margin-right: 0 !important;\n }\n .me-lg-1 {\n margin-right: 0.25rem !important;\n }\n .me-lg-2 {\n margin-right: 0.5rem !important;\n }\n .me-lg-3 {\n margin-right: 1rem !important;\n }\n .me-lg-4 {\n margin-right: 1.5rem !important;\n }\n .me-lg-5 {\n margin-right: 3rem !important;\n }\n .me-lg-auto {\n margin-right: auto !important;\n }\n .mb-lg-0 {\n margin-bottom: 0 !important;\n }\n .mb-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-lg-3 {\n margin-bottom: 1rem !important;\n }\n .mb-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-lg-5 {\n margin-bottom: 3rem !important;\n }\n .mb-lg-auto {\n margin-bottom: auto !important;\n }\n .ms-lg-0 {\n margin-left: 0 !important;\n }\n .ms-lg-1 {\n margin-left: 0.25rem !important;\n }\n .ms-lg-2 {\n margin-left: 0.5rem !important;\n }\n .ms-lg-3 {\n margin-left: 1rem !important;\n }\n .ms-lg-4 {\n margin-left: 1.5rem !important;\n }\n .ms-lg-5 {\n margin-left: 3rem !important;\n }\n .ms-lg-auto {\n margin-left: auto !important;\n }\n .p-lg-0 {\n padding: 0 !important;\n }\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n .p-lg-3 {\n padding: 1rem !important;\n }\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n .p-lg-5 {\n padding: 3rem !important;\n }\n .px-lg-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-lg-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-lg-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-lg-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-lg-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-lg-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-lg-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-lg-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-lg-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-lg-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-lg-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-lg-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-lg-0 {\n padding-top: 0 !important;\n }\n .pt-lg-1 {\n padding-top: 0.25rem !important;\n }\n .pt-lg-2 {\n padding-top: 0.5rem !important;\n }\n .pt-lg-3 {\n padding-top: 1rem !important;\n }\n .pt-lg-4 {\n padding-top: 1.5rem !important;\n }\n .pt-lg-5 {\n padding-top: 3rem !important;\n }\n .pe-lg-0 {\n padding-right: 0 !important;\n }\n .pe-lg-1 {\n padding-right: 0.25rem !important;\n }\n .pe-lg-2 {\n padding-right: 0.5rem !important;\n }\n .pe-lg-3 {\n padding-right: 1rem !important;\n }\n .pe-lg-4 {\n padding-right: 1.5rem !important;\n }\n .pe-lg-5 {\n padding-right: 3rem !important;\n }\n .pb-lg-0 {\n padding-bottom: 0 !important;\n }\n .pb-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-lg-3 {\n padding-bottom: 1rem !important;\n }\n .pb-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-lg-5 {\n padding-bottom: 3rem !important;\n }\n .ps-lg-0 {\n padding-left: 0 !important;\n }\n .ps-lg-1 {\n padding-left: 0.25rem !important;\n }\n .ps-lg-2 {\n padding-left: 0.5rem !important;\n }\n .ps-lg-3 {\n padding-left: 1rem !important;\n }\n .ps-lg-4 {\n padding-left: 1.5rem !important;\n }\n .ps-lg-5 {\n padding-left: 3rem !important;\n }\n}\n@media (min-width: 1200px) {\n .d-xl-inline {\n display: inline !important;\n }\n .d-xl-inline-block {\n display: inline-block !important;\n }\n .d-xl-block {\n display: block !important;\n }\n .d-xl-grid {\n display: grid !important;\n }\n .d-xl-inline-grid {\n display: inline-grid !important;\n }\n .d-xl-table {\n display: table !important;\n }\n .d-xl-table-row {\n display: table-row !important;\n }\n .d-xl-table-cell {\n display: table-cell !important;\n }\n .d-xl-flex {\n display: flex !important;\n }\n .d-xl-inline-flex {\n display: inline-flex !important;\n }\n .d-xl-none {\n display: none !important;\n }\n .flex-xl-fill {\n flex: 1 1 auto !important;\n }\n .flex-xl-row {\n flex-direction: row !important;\n }\n .flex-xl-column {\n flex-direction: column !important;\n }\n .flex-xl-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-xl-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-xl-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-xl-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-xl-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-xl-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-xl-wrap {\n flex-wrap: wrap !important;\n }\n .flex-xl-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-xl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-xl-start {\n justify-content: flex-start !important;\n }\n .justify-content-xl-end {\n justify-content: flex-end !important;\n }\n .justify-content-xl-center {\n justify-content: center !important;\n }\n .justify-content-xl-between {\n justify-content: space-between !important;\n }\n .justify-content-xl-around {\n justify-content: space-around !important;\n }\n .justify-content-xl-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-xl-start {\n align-items: flex-start !important;\n }\n .align-items-xl-end {\n align-items: flex-end !important;\n }\n .align-items-xl-center {\n align-items: center !important;\n }\n .align-items-xl-baseline {\n align-items: baseline !important;\n }\n .align-items-xl-stretch {\n align-items: stretch !important;\n }\n .align-content-xl-start {\n align-content: flex-start !important;\n }\n .align-content-xl-end {\n align-content: flex-end !important;\n }\n .align-content-xl-center {\n align-content: center !important;\n }\n .align-content-xl-between {\n align-content: space-between !important;\n }\n .align-content-xl-around {\n align-content: space-around !important;\n }\n .align-content-xl-stretch {\n align-content: stretch !important;\n }\n .align-self-xl-auto {\n align-self: auto !important;\n }\n .align-self-xl-start {\n align-self: flex-start !important;\n }\n .align-self-xl-end {\n align-self: flex-end !important;\n }\n .align-self-xl-center {\n align-self: center !important;\n }\n .align-self-xl-baseline {\n align-self: baseline !important;\n }\n .align-self-xl-stretch {\n align-self: stretch !important;\n }\n .order-xl-first {\n order: -1 !important;\n }\n .order-xl-0 {\n order: 0 !important;\n }\n .order-xl-1 {\n order: 1 !important;\n }\n .order-xl-2 {\n order: 2 !important;\n }\n .order-xl-3 {\n order: 3 !important;\n }\n .order-xl-4 {\n order: 4 !important;\n }\n .order-xl-5 {\n order: 5 !important;\n }\n .order-xl-last {\n order: 6 !important;\n }\n .m-xl-0 {\n margin: 0 !important;\n }\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n .m-xl-3 {\n margin: 1rem !important;\n }\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n .m-xl-5 {\n margin: 3rem !important;\n }\n .m-xl-auto {\n margin: auto !important;\n }\n .mx-xl-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-xl-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-xl-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-xl-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-xl-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-xl-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-xl-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-xl-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-xl-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-xl-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-xl-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-xl-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-xl-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-xl-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-xl-0 {\n margin-top: 0 !important;\n }\n .mt-xl-1 {\n margin-top: 0.25rem !important;\n }\n .mt-xl-2 {\n margin-top: 0.5rem !important;\n }\n .mt-xl-3 {\n margin-top: 1rem !important;\n }\n .mt-xl-4 {\n margin-top: 1.5rem !important;\n }\n .mt-xl-5 {\n margin-top: 3rem !important;\n }\n .mt-xl-auto {\n margin-top: auto !important;\n }\n .me-xl-0 {\n margin-right: 0 !important;\n }\n .me-xl-1 {\n margin-right: 0.25rem !important;\n }\n .me-xl-2 {\n margin-right: 0.5rem !important;\n }\n .me-xl-3 {\n margin-right: 1rem !important;\n }\n .me-xl-4 {\n margin-right: 1.5rem !important;\n }\n .me-xl-5 {\n margin-right: 3rem !important;\n }\n .me-xl-auto {\n margin-right: auto !important;\n }\n .mb-xl-0 {\n margin-bottom: 0 !important;\n }\n .mb-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-xl-3 {\n margin-bottom: 1rem !important;\n }\n .mb-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-xl-5 {\n margin-bottom: 3rem !important;\n }\n .mb-xl-auto {\n margin-bottom: auto !important;\n }\n .ms-xl-0 {\n margin-left: 0 !important;\n }\n .ms-xl-1 {\n margin-left: 0.25rem !important;\n }\n .ms-xl-2 {\n margin-left: 0.5rem !important;\n }\n .ms-xl-3 {\n margin-left: 1rem !important;\n }\n .ms-xl-4 {\n margin-left: 1.5rem !important;\n }\n .ms-xl-5 {\n margin-left: 3rem !important;\n }\n .ms-xl-auto {\n margin-left: auto !important;\n }\n .p-xl-0 {\n padding: 0 !important;\n }\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n .p-xl-3 {\n padding: 1rem !important;\n }\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n .p-xl-5 {\n padding: 3rem !important;\n }\n .px-xl-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-xl-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-xl-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-xl-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-xl-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-xl-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-xl-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-xl-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-xl-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-xl-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-xl-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-xl-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-xl-0 {\n padding-top: 0 !important;\n }\n .pt-xl-1 {\n padding-top: 0.25rem !important;\n }\n .pt-xl-2 {\n padding-top: 0.5rem !important;\n }\n .pt-xl-3 {\n padding-top: 1rem !important;\n }\n .pt-xl-4 {\n padding-top: 1.5rem !important;\n }\n .pt-xl-5 {\n padding-top: 3rem !important;\n }\n .pe-xl-0 {\n padding-right: 0 !important;\n }\n .pe-xl-1 {\n padding-right: 0.25rem !important;\n }\n .pe-xl-2 {\n padding-right: 0.5rem !important;\n }\n .pe-xl-3 {\n padding-right: 1rem !important;\n }\n .pe-xl-4 {\n padding-right: 1.5rem !important;\n }\n .pe-xl-5 {\n padding-right: 3rem !important;\n }\n .pb-xl-0 {\n padding-bottom: 0 !important;\n }\n .pb-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-xl-3 {\n padding-bottom: 1rem !important;\n }\n .pb-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-xl-5 {\n padding-bottom: 3rem !important;\n }\n .ps-xl-0 {\n padding-left: 0 !important;\n }\n .ps-xl-1 {\n padding-left: 0.25rem !important;\n }\n .ps-xl-2 {\n padding-left: 0.5rem !important;\n }\n .ps-xl-3 {\n padding-left: 1rem !important;\n }\n .ps-xl-4 {\n padding-left: 1.5rem !important;\n }\n .ps-xl-5 {\n padding-left: 3rem !important;\n }\n}\n@media (min-width: 1400px) {\n .d-xxl-inline {\n display: inline !important;\n }\n .d-xxl-inline-block {\n display: inline-block !important;\n }\n .d-xxl-block {\n display: block !important;\n }\n .d-xxl-grid {\n display: grid !important;\n }\n .d-xxl-inline-grid {\n display: inline-grid !important;\n }\n .d-xxl-table {\n display: table !important;\n }\n .d-xxl-table-row {\n display: table-row !important;\n }\n .d-xxl-table-cell {\n display: table-cell !important;\n }\n .d-xxl-flex {\n display: flex !important;\n }\n .d-xxl-inline-flex {\n display: inline-flex !important;\n }\n .d-xxl-none {\n display: none !important;\n }\n .flex-xxl-fill {\n flex: 1 1 auto !important;\n }\n .flex-xxl-row {\n flex-direction: row !important;\n }\n .flex-xxl-column {\n flex-direction: column !important;\n }\n .flex-xxl-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-xxl-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-xxl-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-xxl-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-xxl-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-xxl-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-xxl-wrap {\n flex-wrap: wrap !important;\n }\n .flex-xxl-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-xxl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-xxl-start {\n justify-content: flex-start !important;\n }\n .justify-content-xxl-end {\n justify-content: flex-end !important;\n }\n .justify-content-xxl-center {\n justify-content: center !important;\n }\n .justify-content-xxl-between {\n justify-content: space-between !important;\n }\n .justify-content-xxl-around {\n justify-content: space-around !important;\n }\n .justify-content-xxl-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-xxl-start {\n align-items: flex-start !important;\n }\n .align-items-xxl-end {\n align-items: flex-end !important;\n }\n .align-items-xxl-center {\n align-items: center !important;\n }\n .align-items-xxl-baseline {\n align-items: baseline !important;\n }\n .align-items-xxl-stretch {\n align-items: stretch !important;\n }\n .align-content-xxl-start {\n align-content: flex-start !important;\n }\n .align-content-xxl-end {\n align-content: flex-end !important;\n }\n .align-content-xxl-center {\n align-content: center !important;\n }\n .align-content-xxl-between {\n align-content: space-between !important;\n }\n .align-content-xxl-around {\n align-content: space-around !important;\n }\n .align-content-xxl-stretch {\n align-content: stretch !important;\n }\n .align-self-xxl-auto {\n align-self: auto !important;\n }\n .align-self-xxl-start {\n align-self: flex-start !important;\n }\n .align-self-xxl-end {\n align-self: flex-end !important;\n }\n .align-self-xxl-center {\n align-self: center !important;\n }\n .align-self-xxl-baseline {\n align-self: baseline !important;\n }\n .align-self-xxl-stretch {\n align-self: stretch !important;\n }\n .order-xxl-first {\n order: -1 !important;\n }\n .order-xxl-0 {\n order: 0 !important;\n }\n .order-xxl-1 {\n order: 1 !important;\n }\n .order-xxl-2 {\n order: 2 !important;\n }\n .order-xxl-3 {\n order: 3 !important;\n }\n .order-xxl-4 {\n order: 4 !important;\n }\n .order-xxl-5 {\n order: 5 !important;\n }\n .order-xxl-last {\n order: 6 !important;\n }\n .m-xxl-0 {\n margin: 0 !important;\n }\n .m-xxl-1 {\n margin: 0.25rem !important;\n }\n .m-xxl-2 {\n margin: 0.5rem !important;\n }\n .m-xxl-3 {\n margin: 1rem !important;\n }\n .m-xxl-4 {\n margin: 1.5rem !important;\n }\n .m-xxl-5 {\n margin: 3rem !important;\n }\n .m-xxl-auto {\n margin: auto !important;\n }\n .mx-xxl-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-xxl-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-xxl-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-xxl-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-xxl-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-xxl-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-xxl-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-xxl-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-xxl-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-xxl-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-xxl-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-xxl-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-xxl-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-xxl-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-xxl-0 {\n margin-top: 0 !important;\n }\n .mt-xxl-1 {\n margin-top: 0.25rem !important;\n }\n .mt-xxl-2 {\n margin-top: 0.5rem !important;\n }\n .mt-xxl-3 {\n margin-top: 1rem !important;\n }\n .mt-xxl-4 {\n margin-top: 1.5rem !important;\n }\n .mt-xxl-5 {\n margin-top: 3rem !important;\n }\n .mt-xxl-auto {\n margin-top: auto !important;\n }\n .me-xxl-0 {\n margin-right: 0 !important;\n }\n .me-xxl-1 {\n margin-right: 0.25rem !important;\n }\n .me-xxl-2 {\n margin-right: 0.5rem !important;\n }\n .me-xxl-3 {\n margin-right: 1rem !important;\n }\n .me-xxl-4 {\n margin-right: 1.5rem !important;\n }\n .me-xxl-5 {\n margin-right: 3rem !important;\n }\n .me-xxl-auto {\n margin-right: auto !important;\n }\n .mb-xxl-0 {\n margin-bottom: 0 !important;\n }\n .mb-xxl-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-xxl-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-xxl-3 {\n margin-bottom: 1rem !important;\n }\n .mb-xxl-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-xxl-5 {\n margin-bottom: 3rem !important;\n }\n .mb-xxl-auto {\n margin-bottom: auto !important;\n }\n .ms-xxl-0 {\n margin-left: 0 !important;\n }\n .ms-xxl-1 {\n margin-left: 0.25rem !important;\n }\n .ms-xxl-2 {\n margin-left: 0.5rem !important;\n }\n .ms-xxl-3 {\n margin-left: 1rem !important;\n }\n .ms-xxl-4 {\n margin-left: 1.5rem !important;\n }\n .ms-xxl-5 {\n margin-left: 3rem !important;\n }\n .ms-xxl-auto {\n margin-left: auto !important;\n }\n .p-xxl-0 {\n padding: 0 !important;\n }\n .p-xxl-1 {\n padding: 0.25rem !important;\n }\n .p-xxl-2 {\n padding: 0.5rem !important;\n }\n .p-xxl-3 {\n padding: 1rem !important;\n }\n .p-xxl-4 {\n padding: 1.5rem !important;\n }\n .p-xxl-5 {\n padding: 3rem !important;\n }\n .px-xxl-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-xxl-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-xxl-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-xxl-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-xxl-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-xxl-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-xxl-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-xxl-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-xxl-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-xxl-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-xxl-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-xxl-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-xxl-0 {\n padding-top: 0 !important;\n }\n .pt-xxl-1 {\n padding-top: 0.25rem !important;\n }\n .pt-xxl-2 {\n padding-top: 0.5rem !important;\n }\n .pt-xxl-3 {\n padding-top: 1rem !important;\n }\n .pt-xxl-4 {\n padding-top: 1.5rem !important;\n }\n .pt-xxl-5 {\n padding-top: 3rem !important;\n }\n .pe-xxl-0 {\n padding-right: 0 !important;\n }\n .pe-xxl-1 {\n padding-right: 0.25rem !important;\n }\n .pe-xxl-2 {\n padding-right: 0.5rem !important;\n }\n .pe-xxl-3 {\n padding-right: 1rem !important;\n }\n .pe-xxl-4 {\n padding-right: 1.5rem !important;\n }\n .pe-xxl-5 {\n padding-right: 3rem !important;\n }\n .pb-xxl-0 {\n padding-bottom: 0 !important;\n }\n .pb-xxl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-xxl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-xxl-3 {\n padding-bottom: 1rem !important;\n }\n .pb-xxl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-xxl-5 {\n padding-bottom: 3rem !important;\n }\n .ps-xxl-0 {\n padding-left: 0 !important;\n }\n .ps-xxl-1 {\n padding-left: 0.25rem !important;\n }\n .ps-xxl-2 {\n padding-left: 0.5rem !important;\n }\n .ps-xxl-3 {\n padding-left: 1rem !important;\n }\n .ps-xxl-4 {\n padding-left: 1.5rem !important;\n }\n .ps-xxl-5 {\n padding-left: 3rem !important;\n }\n}\n@media print {\n .d-print-inline {\n display: inline !important;\n }\n .d-print-inline-block {\n display: inline-block !important;\n }\n .d-print-block {\n display: block !important;\n }\n .d-print-grid {\n display: grid !important;\n }\n .d-print-inline-grid {\n display: inline-grid !important;\n }\n .d-print-table {\n display: table !important;\n }\n .d-print-table-row {\n display: table-row !important;\n }\n .d-print-table-cell {\n display: table-cell !important;\n }\n .d-print-flex {\n display: flex !important;\n }\n .d-print-inline-flex {\n display: inline-flex !important;\n }\n .d-print-none {\n display: none !important;\n }\n}\n\n/*# sourceMappingURL=bootstrap-grid.css.map */\n","// Breakpoint viewport sizes and media queries.\n//\n// Breakpoints are defined as a map of (name: minimum width), order from small to large:\n//\n// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)\n//\n// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.\n\n// Name of the next breakpoint, or null for the last breakpoint.\n//\n// >> breakpoint-next(sm)\n// md\n// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// md\n// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl xxl))\n// md\n@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {\n $n: index($breakpoint-names, $name);\n @if not $n {\n @error \"breakpoint `#{$name}` not found in `#{$breakpoints}`\";\n }\n @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);\n}\n\n// Minimum breakpoint width. Null for the smallest (first) breakpoint.\n//\n// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// 576px\n@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {\n $min: map-get($breakpoints, $name);\n @return if($min != 0, $min, null);\n}\n\n// Maximum breakpoint width.\n// The maximum value is reduced by 0.02px to work around the limitations of\n// `min-` and `max-` prefixes and viewports with fractional widths.\n// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max\n// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.\n// See https://bugs.webkit.org/show_bug.cgi?id=178261\n//\n// >> breakpoint-max(md, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// 767.98px\n@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {\n $max: map-get($breakpoints, $name);\n @return if($max and $max > 0, $max - .02, null);\n}\n\n// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front.\n// Useful for making responsive utilities.\n//\n// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// \"\" (Returns a blank string)\n// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// \"-sm\"\n@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {\n @return if(breakpoint-min($name, $breakpoints) == null, \"\", \"-#{$name}\");\n}\n\n// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.\n// Makes the @content apply to the given breakpoint and wider.\n@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n @if $min {\n @media (min-width: $min) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media of at most the maximum breakpoint width. No query for the largest breakpoint.\n// Makes the @content apply to the given breakpoint and narrower.\n@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {\n $max: breakpoint-max($name, $breakpoints);\n @if $max {\n @media (max-width: $max) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media that spans multiple breakpoint widths.\n// Makes the @content apply between the min and max breakpoints\n@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($lower, $breakpoints);\n $max: breakpoint-max($upper, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($lower, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($upper, $breakpoints) {\n @content;\n }\n }\n}\n\n// Media between the breakpoint's minimum and maximum widths.\n// No minimum for the smallest breakpoint, and no maximum for the largest one.\n// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.\n@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n $next: breakpoint-next($name, $breakpoints);\n $max: breakpoint-max($next, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($name, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($next, $breakpoints) {\n @content;\n }\n }\n}\n","// Variables\n//\n// Variables should follow the `$component-state-property-size` formula for\n// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.\n\n// Color system\n\n// scss-docs-start gray-color-variables\n$white: #fff !default;\n$gray-100: #f8f9fa !default;\n$gray-200: #e9ecef !default;\n$gray-300: #dee2e6 !default;\n$gray-400: #ced4da !default;\n$gray-500: #adb5bd !default;\n$gray-600: #6c757d !default;\n$gray-700: #495057 !default;\n$gray-800: #343a40 !default;\n$gray-900: #212529 !default;\n$black: #000 !default;\n// scss-docs-end gray-color-variables\n\n// fusv-disable\n// scss-docs-start gray-colors-map\n$grays: (\n \"100\": $gray-100,\n \"200\": $gray-200,\n \"300\": $gray-300,\n \"400\": $gray-400,\n \"500\": $gray-500,\n \"600\": $gray-600,\n \"700\": $gray-700,\n \"800\": $gray-800,\n \"900\": $gray-900\n) !default;\n// scss-docs-end gray-colors-map\n// fusv-enable\n\n// scss-docs-start color-variables\n$blue: #0d6efd !default;\n$indigo: #6610f2 !default;\n$purple: #6f42c1 !default;\n$pink: #d63384 !default;\n$red: #dc3545 !default;\n$orange: #fd7e14 !default;\n$yellow: #ffc107 !default;\n$green: #198754 !default;\n$teal: #20c997 !default;\n$cyan: #0dcaf0 !default;\n// scss-docs-end color-variables\n\n// scss-docs-start colors-map\n$colors: (\n \"blue\": $blue,\n \"indigo\": $indigo,\n \"purple\": $purple,\n \"pink\": $pink,\n \"red\": $red,\n \"orange\": $orange,\n \"yellow\": $yellow,\n \"green\": $green,\n \"teal\": $teal,\n \"cyan\": $cyan,\n \"black\": $black,\n \"white\": $white,\n \"gray\": $gray-600,\n \"gray-dark\": $gray-800\n) !default;\n// scss-docs-end colors-map\n\n// The contrast ratio to reach against white, to determine if color changes from \"light\" to \"dark\". Acceptable values for WCAG 2.0 are 3, 4.5 and 7.\n// See https://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast\n$min-contrast-ratio: 4.5 !default;\n\n// Customize the light and dark text colors for use in our color contrast function.\n$color-contrast-dark: $black !default;\n$color-contrast-light: $white !default;\n\n// fusv-disable\n$blue-100: tint-color($blue, 80%) !default;\n$blue-200: tint-color($blue, 60%) !default;\n$blue-300: tint-color($blue, 40%) !default;\n$blue-400: tint-color($blue, 20%) !default;\n$blue-500: $blue !default;\n$blue-600: shade-color($blue, 20%) !default;\n$blue-700: shade-color($blue, 40%) !default;\n$blue-800: shade-color($blue, 60%) !default;\n$blue-900: shade-color($blue, 80%) !default;\n\n$indigo-100: tint-color($indigo, 80%) !default;\n$indigo-200: tint-color($indigo, 60%) !default;\n$indigo-300: tint-color($indigo, 40%) !default;\n$indigo-400: tint-color($indigo, 20%) !default;\n$indigo-500: $indigo !default;\n$indigo-600: shade-color($indigo, 20%) !default;\n$indigo-700: shade-color($indigo, 40%) !default;\n$indigo-800: shade-color($indigo, 60%) !default;\n$indigo-900: shade-color($indigo, 80%) !default;\n\n$purple-100: tint-color($purple, 80%) !default;\n$purple-200: tint-color($purple, 60%) !default;\n$purple-300: tint-color($purple, 40%) !default;\n$purple-400: tint-color($purple, 20%) !default;\n$purple-500: $purple !default;\n$purple-600: shade-color($purple, 20%) !default;\n$purple-700: shade-color($purple, 40%) !default;\n$purple-800: shade-color($purple, 60%) !default;\n$purple-900: shade-color($purple, 80%) !default;\n\n$pink-100: tint-color($pink, 80%) !default;\n$pink-200: tint-color($pink, 60%) !default;\n$pink-300: tint-color($pink, 40%) !default;\n$pink-400: tint-color($pink, 20%) !default;\n$pink-500: $pink !default;\n$pink-600: shade-color($pink, 20%) !default;\n$pink-700: shade-color($pink, 40%) !default;\n$pink-800: shade-color($pink, 60%) !default;\n$pink-900: shade-color($pink, 80%) !default;\n\n$red-100: tint-color($red, 80%) !default;\n$red-200: tint-color($red, 60%) !default;\n$red-300: tint-color($red, 40%) !default;\n$red-400: tint-color($red, 20%) !default;\n$red-500: $red !default;\n$red-600: shade-color($red, 20%) !default;\n$red-700: shade-color($red, 40%) !default;\n$red-800: shade-color($red, 60%) !default;\n$red-900: shade-color($red, 80%) !default;\n\n$orange-100: tint-color($orange, 80%) !default;\n$orange-200: tint-color($orange, 60%) !default;\n$orange-300: tint-color($orange, 40%) !default;\n$orange-400: tint-color($orange, 20%) !default;\n$orange-500: $orange !default;\n$orange-600: shade-color($orange, 20%) !default;\n$orange-700: shade-color($orange, 40%) !default;\n$orange-800: shade-color($orange, 60%) !default;\n$orange-900: shade-color($orange, 80%) !default;\n\n$yellow-100: tint-color($yellow, 80%) !default;\n$yellow-200: tint-color($yellow, 60%) !default;\n$yellow-300: tint-color($yellow, 40%) !default;\n$yellow-400: tint-color($yellow, 20%) !default;\n$yellow-500: $yellow !default;\n$yellow-600: shade-color($yellow, 20%) !default;\n$yellow-700: shade-color($yellow, 40%) !default;\n$yellow-800: shade-color($yellow, 60%) !default;\n$yellow-900: shade-color($yellow, 80%) !default;\n\n$green-100: tint-color($green, 80%) !default;\n$green-200: tint-color($green, 60%) !default;\n$green-300: tint-color($green, 40%) !default;\n$green-400: tint-color($green, 20%) !default;\n$green-500: $green !default;\n$green-600: shade-color($green, 20%) !default;\n$green-700: shade-color($green, 40%) !default;\n$green-800: shade-color($green, 60%) !default;\n$green-900: shade-color($green, 80%) !default;\n\n$teal-100: tint-color($teal, 80%) !default;\n$teal-200: tint-color($teal, 60%) !default;\n$teal-300: tint-color($teal, 40%) !default;\n$teal-400: tint-color($teal, 20%) !default;\n$teal-500: $teal !default;\n$teal-600: shade-color($teal, 20%) !default;\n$teal-700: shade-color($teal, 40%) !default;\n$teal-800: shade-color($teal, 60%) !default;\n$teal-900: shade-color($teal, 80%) !default;\n\n$cyan-100: tint-color($cyan, 80%) !default;\n$cyan-200: tint-color($cyan, 60%) !default;\n$cyan-300: tint-color($cyan, 40%) !default;\n$cyan-400: tint-color($cyan, 20%) !default;\n$cyan-500: $cyan !default;\n$cyan-600: shade-color($cyan, 20%) !default;\n$cyan-700: shade-color($cyan, 40%) !default;\n$cyan-800: shade-color($cyan, 60%) !default;\n$cyan-900: shade-color($cyan, 80%) !default;\n\n$blues: (\n \"blue-100\": $blue-100,\n \"blue-200\": $blue-200,\n \"blue-300\": $blue-300,\n \"blue-400\": $blue-400,\n \"blue-500\": $blue-500,\n \"blue-600\": $blue-600,\n \"blue-700\": $blue-700,\n \"blue-800\": $blue-800,\n \"blue-900\": $blue-900\n) !default;\n\n$indigos: (\n \"indigo-100\": $indigo-100,\n \"indigo-200\": $indigo-200,\n \"indigo-300\": $indigo-300,\n \"indigo-400\": $indigo-400,\n \"indigo-500\": $indigo-500,\n \"indigo-600\": $indigo-600,\n \"indigo-700\": $indigo-700,\n \"indigo-800\": $indigo-800,\n \"indigo-900\": $indigo-900\n) !default;\n\n$purples: (\n \"purple-100\": $purple-100,\n \"purple-200\": $purple-200,\n \"purple-300\": $purple-300,\n \"purple-400\": $purple-400,\n \"purple-500\": $purple-500,\n \"purple-600\": $purple-600,\n \"purple-700\": $purple-700,\n \"purple-800\": $purple-800,\n \"purple-900\": $purple-900\n) !default;\n\n$pinks: (\n \"pink-100\": $pink-100,\n \"pink-200\": $pink-200,\n \"pink-300\": $pink-300,\n \"pink-400\": $pink-400,\n \"pink-500\": $pink-500,\n \"pink-600\": $pink-600,\n \"pink-700\": $pink-700,\n \"pink-800\": $pink-800,\n \"pink-900\": $pink-900\n) !default;\n\n$reds: (\n \"red-100\": $red-100,\n \"red-200\": $red-200,\n \"red-300\": $red-300,\n \"red-400\": $red-400,\n \"red-500\": $red-500,\n \"red-600\": $red-600,\n \"red-700\": $red-700,\n \"red-800\": $red-800,\n \"red-900\": $red-900\n) !default;\n\n$oranges: (\n \"orange-100\": $orange-100,\n \"orange-200\": $orange-200,\n \"orange-300\": $orange-300,\n \"orange-400\": $orange-400,\n \"orange-500\": $orange-500,\n \"orange-600\": $orange-600,\n \"orange-700\": $orange-700,\n \"orange-800\": $orange-800,\n \"orange-900\": $orange-900\n) !default;\n\n$yellows: (\n \"yellow-100\": $yellow-100,\n \"yellow-200\": $yellow-200,\n \"yellow-300\": $yellow-300,\n \"yellow-400\": $yellow-400,\n \"yellow-500\": $yellow-500,\n \"yellow-600\": $yellow-600,\n \"yellow-700\": $yellow-700,\n \"yellow-800\": $yellow-800,\n \"yellow-900\": $yellow-900\n) !default;\n\n$greens: (\n \"green-100\": $green-100,\n \"green-200\": $green-200,\n \"green-300\": $green-300,\n \"green-400\": $green-400,\n \"green-500\": $green-500,\n \"green-600\": $green-600,\n \"green-700\": $green-700,\n \"green-800\": $green-800,\n \"green-900\": $green-900\n) !default;\n\n$teals: (\n \"teal-100\": $teal-100,\n \"teal-200\": $teal-200,\n \"teal-300\": $teal-300,\n \"teal-400\": $teal-400,\n \"teal-500\": $teal-500,\n \"teal-600\": $teal-600,\n \"teal-700\": $teal-700,\n \"teal-800\": $teal-800,\n \"teal-900\": $teal-900\n) !default;\n\n$cyans: (\n \"cyan-100\": $cyan-100,\n \"cyan-200\": $cyan-200,\n \"cyan-300\": $cyan-300,\n \"cyan-400\": $cyan-400,\n \"cyan-500\": $cyan-500,\n \"cyan-600\": $cyan-600,\n \"cyan-700\": $cyan-700,\n \"cyan-800\": $cyan-800,\n \"cyan-900\": $cyan-900\n) !default;\n// fusv-enable\n\n// scss-docs-start theme-color-variables\n$primary: $blue !default;\n$secondary: $gray-600 !default;\n$success: $green !default;\n$info: $cyan !default;\n$warning: $yellow !default;\n$danger: $red !default;\n$light: $gray-100 !default;\n$dark: $gray-900 !default;\n// scss-docs-end theme-color-variables\n\n// scss-docs-start theme-colors-map\n$theme-colors: (\n \"primary\": $primary,\n \"secondary\": $secondary,\n \"success\": $success,\n \"info\": $info,\n \"warning\": $warning,\n \"danger\": $danger,\n \"light\": $light,\n \"dark\": $dark\n) !default;\n// scss-docs-end theme-colors-map\n\n// scss-docs-start theme-text-variables\n$primary-text-emphasis: shade-color($primary, 60%) !default;\n$secondary-text-emphasis: shade-color($secondary, 60%) !default;\n$success-text-emphasis: shade-color($success, 60%) !default;\n$info-text-emphasis: shade-color($info, 60%) !default;\n$warning-text-emphasis: shade-color($warning, 60%) !default;\n$danger-text-emphasis: shade-color($danger, 60%) !default;\n$light-text-emphasis: $gray-700 !default;\n$dark-text-emphasis: $gray-700 !default;\n// scss-docs-end theme-text-variables\n\n// scss-docs-start theme-bg-subtle-variables\n$primary-bg-subtle: tint-color($primary, 80%) !default;\n$secondary-bg-subtle: tint-color($secondary, 80%) !default;\n$success-bg-subtle: tint-color($success, 80%) !default;\n$info-bg-subtle: tint-color($info, 80%) !default;\n$warning-bg-subtle: tint-color($warning, 80%) !default;\n$danger-bg-subtle: tint-color($danger, 80%) !default;\n$light-bg-subtle: mix($gray-100, $white) !default;\n$dark-bg-subtle: $gray-400 !default;\n// scss-docs-end theme-bg-subtle-variables\n\n// scss-docs-start theme-border-subtle-variables\n$primary-border-subtle: tint-color($primary, 60%) !default;\n$secondary-border-subtle: tint-color($secondary, 60%) !default;\n$success-border-subtle: tint-color($success, 60%) !default;\n$info-border-subtle: tint-color($info, 60%) !default;\n$warning-border-subtle: tint-color($warning, 60%) !default;\n$danger-border-subtle: tint-color($danger, 60%) !default;\n$light-border-subtle: $gray-200 !default;\n$dark-border-subtle: $gray-500 !default;\n// scss-docs-end theme-border-subtle-variables\n\n// Characters which are escaped by the escape-svg function\n$escaped-characters: (\n (\"<\", \"%3c\"),\n (\">\", \"%3e\"),\n (\"#\", \"%23\"),\n (\"(\", \"%28\"),\n (\")\", \"%29\"),\n) !default;\n\n// Options\n//\n// Quickly modify global styling by enabling or disabling optional features.\n\n$enable-caret: true !default;\n$enable-rounded: true !default;\n$enable-shadows: false !default;\n$enable-gradients: false !default;\n$enable-transitions: true !default;\n$enable-reduced-motion: true !default;\n$enable-smooth-scroll: true !default;\n$enable-grid-classes: true !default;\n$enable-container-classes: true !default;\n$enable-cssgrid: false !default;\n$enable-button-pointers: true !default;\n$enable-rfs: true !default;\n$enable-validation-icons: true !default;\n$enable-negative-margins: false !default;\n$enable-deprecation-messages: true !default;\n$enable-important-utilities: true !default;\n\n$enable-dark-mode: true !default;\n$color-mode-type: data !default; // `data` or `media-query`\n\n// Prefix for :root CSS variables\n\n$variable-prefix: bs- !default; // Deprecated in v5.2.0 for the shorter `$prefix`\n$prefix: $variable-prefix !default;\n\n// Gradient\n//\n// The gradient which is added to components if `$enable-gradients` is `true`\n// This gradient is also added to elements with `.bg-gradient`\n// scss-docs-start variable-gradient\n$gradient: linear-gradient(180deg, rgba($white, .15), rgba($white, 0)) !default;\n// scss-docs-end variable-gradient\n\n// Spacing\n//\n// Control the default styling of most Bootstrap elements by modifying these\n// variables. Mostly focused on spacing.\n// You can add more entries to the $spacers map, should you need more variation.\n\n// scss-docs-start spacer-variables-maps\n$spacer: 1rem !default;\n$spacers: (\n 0: 0,\n 1: $spacer * .25,\n 2: $spacer * .5,\n 3: $spacer,\n 4: $spacer * 1.5,\n 5: $spacer * 3,\n) !default;\n// scss-docs-end spacer-variables-maps\n\n// Position\n//\n// Define the edge positioning anchors of the position utilities.\n\n// scss-docs-start position-map\n$position-values: (\n 0: 0,\n 50: 50%,\n 100: 100%\n) !default;\n// scss-docs-end position-map\n\n// Body\n//\n// Settings for the `` element.\n\n$body-text-align: null !default;\n$body-color: $gray-900 !default;\n$body-bg: $white !default;\n\n$body-secondary-color: rgba($body-color, .75) !default;\n$body-secondary-bg: $gray-200 !default;\n\n$body-tertiary-color: rgba($body-color, .5) !default;\n$body-tertiary-bg: $gray-100 !default;\n\n$body-emphasis-color: $black !default;\n\n// Links\n//\n// Style anchor elements.\n\n$link-color: $primary !default;\n$link-decoration: underline !default;\n$link-shade-percentage: 20% !default;\n$link-hover-color: shift-color($link-color, $link-shade-percentage) !default;\n$link-hover-decoration: null !default;\n\n$stretched-link-pseudo-element: after !default;\n$stretched-link-z-index: 1 !default;\n\n// Icon links\n// scss-docs-start icon-link-variables\n$icon-link-gap: .375rem !default;\n$icon-link-underline-offset: .25em !default;\n$icon-link-icon-size: 1em !default;\n$icon-link-icon-transition: .2s ease-in-out transform !default;\n$icon-link-icon-transform: translate3d(.25em, 0, 0) !default;\n// scss-docs-end icon-link-variables\n\n// Paragraphs\n//\n// Style p element.\n\n$paragraph-margin-bottom: 1rem !default;\n\n\n// Grid breakpoints\n//\n// Define the minimum dimensions at which your layout will change,\n// adapting to different screen sizes, for use in media queries.\n\n// scss-docs-start grid-breakpoints\n$grid-breakpoints: (\n xs: 0,\n sm: 576px,\n md: 768px,\n lg: 992px,\n xl: 1200px,\n xxl: 1400px\n) !default;\n// scss-docs-end grid-breakpoints\n\n@include _assert-ascending($grid-breakpoints, \"$grid-breakpoints\");\n@include _assert-starts-at-zero($grid-breakpoints, \"$grid-breakpoints\");\n\n\n// Grid containers\n//\n// Define the maximum width of `.container` for different screen sizes.\n\n// scss-docs-start container-max-widths\n$container-max-widths: (\n sm: 540px,\n md: 720px,\n lg: 960px,\n xl: 1140px,\n xxl: 1320px\n) !default;\n// scss-docs-end container-max-widths\n\n@include _assert-ascending($container-max-widths, \"$container-max-widths\");\n\n\n// Grid columns\n//\n// Set the number of columns and specify the width of the gutters.\n\n$grid-columns: 12 !default;\n$grid-gutter-width: 1.5rem !default;\n$grid-row-columns: 6 !default;\n\n// Container padding\n\n$container-padding-x: $grid-gutter-width !default;\n\n\n// Components\n//\n// Define common padding and border radius sizes and more.\n\n// scss-docs-start border-variables\n$border-width: 1px !default;\n$border-widths: (\n 1: 1px,\n 2: 2px,\n 3: 3px,\n 4: 4px,\n 5: 5px\n) !default;\n$border-style: solid !default;\n$border-color: $gray-300 !default;\n$border-color-translucent: rgba($black, .175) !default;\n// scss-docs-end border-variables\n\n// scss-docs-start border-radius-variables\n$border-radius: .375rem !default;\n$border-radius-sm: .25rem !default;\n$border-radius-lg: .5rem !default;\n$border-radius-xl: 1rem !default;\n$border-radius-xxl: 2rem !default;\n$border-radius-pill: 50rem !default;\n// scss-docs-end border-radius-variables\n// fusv-disable\n$border-radius-2xl: $border-radius-xxl !default; // Deprecated in v5.3.0\n// fusv-enable\n\n// scss-docs-start box-shadow-variables\n$box-shadow: 0 .5rem 1rem rgba($black, .15) !default;\n$box-shadow-sm: 0 .125rem .25rem rgba($black, .075) !default;\n$box-shadow-lg: 0 1rem 3rem rgba($black, .175) !default;\n$box-shadow-inset: inset 0 1px 2px rgba($black, .075) !default;\n// scss-docs-end box-shadow-variables\n\n$component-active-color: $white !default;\n$component-active-bg: $primary !default;\n\n// scss-docs-start focus-ring-variables\n$focus-ring-width: .25rem !default;\n$focus-ring-opacity: .25 !default;\n$focus-ring-color: rgba($primary, $focus-ring-opacity) !default;\n$focus-ring-blur: 0 !default;\n$focus-ring-box-shadow: 0 0 $focus-ring-blur $focus-ring-width $focus-ring-color !default;\n// scss-docs-end focus-ring-variables\n\n// scss-docs-start caret-variables\n$caret-width: .3em !default;\n$caret-vertical-align: $caret-width * .85 !default;\n$caret-spacing: $caret-width * .85 !default;\n// scss-docs-end caret-variables\n\n$transition-base: all .2s ease-in-out !default;\n$transition-fade: opacity .15s linear !default;\n// scss-docs-start collapse-transition\n$transition-collapse: height .35s ease !default;\n$transition-collapse-width: width .35s ease !default;\n// scss-docs-end collapse-transition\n\n// stylelint-disable function-disallowed-list\n// scss-docs-start aspect-ratios\n$aspect-ratios: (\n \"1x1\": 100%,\n \"4x3\": calc(3 / 4 * 100%),\n \"16x9\": calc(9 / 16 * 100%),\n \"21x9\": calc(9 / 21 * 100%)\n) !default;\n// scss-docs-end aspect-ratios\n// stylelint-enable function-disallowed-list\n\n// Typography\n//\n// Font, line-height, and color for body text, headings, and more.\n\n// scss-docs-start font-variables\n// stylelint-disable value-keyword-case\n$font-family-sans-serif: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", \"Liberation Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\" !default;\n$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace !default;\n// stylelint-enable value-keyword-case\n$font-family-base: var(--#{$prefix}font-sans-serif) !default;\n$font-family-code: var(--#{$prefix}font-monospace) !default;\n\n// $font-size-root affects the value of `rem`, which is used for as well font sizes, paddings, and margins\n// $font-size-base affects the font size of the body text\n$font-size-root: null !default;\n$font-size-base: 1rem !default; // Assumes the browser default, typically `16px`\n$font-size-sm: $font-size-base * .875 !default;\n$font-size-lg: $font-size-base * 1.25 !default;\n\n$font-weight-lighter: lighter !default;\n$font-weight-light: 300 !default;\n$font-weight-normal: 400 !default;\n$font-weight-medium: 500 !default;\n$font-weight-semibold: 600 !default;\n$font-weight-bold: 700 !default;\n$font-weight-bolder: bolder !default;\n\n$font-weight-base: $font-weight-normal !default;\n\n$line-height-base: 1.5 !default;\n$line-height-sm: 1.25 !default;\n$line-height-lg: 2 !default;\n\n$h1-font-size: $font-size-base * 2.5 !default;\n$h2-font-size: $font-size-base * 2 !default;\n$h3-font-size: $font-size-base * 1.75 !default;\n$h4-font-size: $font-size-base * 1.5 !default;\n$h5-font-size: $font-size-base * 1.25 !default;\n$h6-font-size: $font-size-base !default;\n// scss-docs-end font-variables\n\n// scss-docs-start font-sizes\n$font-sizes: (\n 1: $h1-font-size,\n 2: $h2-font-size,\n 3: $h3-font-size,\n 4: $h4-font-size,\n 5: $h5-font-size,\n 6: $h6-font-size\n) !default;\n// scss-docs-end font-sizes\n\n// scss-docs-start headings-variables\n$headings-margin-bottom: $spacer * .5 !default;\n$headings-font-family: null !default;\n$headings-font-style: null !default;\n$headings-font-weight: 500 !default;\n$headings-line-height: 1.2 !default;\n$headings-color: inherit !default;\n// scss-docs-end headings-variables\n\n// scss-docs-start display-headings\n$display-font-sizes: (\n 1: 5rem,\n 2: 4.5rem,\n 3: 4rem,\n 4: 3.5rem,\n 5: 3rem,\n 6: 2.5rem\n) !default;\n\n$display-font-family: null !default;\n$display-font-style: null !default;\n$display-font-weight: 300 !default;\n$display-line-height: $headings-line-height !default;\n// scss-docs-end display-headings\n\n// scss-docs-start type-variables\n$lead-font-size: $font-size-base * 1.25 !default;\n$lead-font-weight: 300 !default;\n\n$small-font-size: .875em !default;\n\n$sub-sup-font-size: .75em !default;\n\n// fusv-disable\n$text-muted: var(--#{$prefix}secondary-color) !default; // Deprecated in 5.3.0\n// fusv-enable\n\n$initialism-font-size: $small-font-size !default;\n\n$blockquote-margin-y: $spacer !default;\n$blockquote-font-size: $font-size-base * 1.25 !default;\n$blockquote-footer-color: $gray-600 !default;\n$blockquote-footer-font-size: $small-font-size !default;\n\n$hr-margin-y: $spacer !default;\n$hr-color: inherit !default;\n\n// fusv-disable\n$hr-bg-color: null !default; // Deprecated in v5.2.0\n$hr-height: null !default; // Deprecated in v5.2.0\n// fusv-enable\n\n$hr-border-color: null !default; // Allows for inherited colors\n$hr-border-width: var(--#{$prefix}border-width) !default;\n$hr-opacity: .25 !default;\n\n// scss-docs-start vr-variables\n$vr-border-width: var(--#{$prefix}border-width) !default;\n// scss-docs-end vr-variables\n\n$legend-margin-bottom: .5rem !default;\n$legend-font-size: 1.5rem !default;\n$legend-font-weight: null !default;\n\n$dt-font-weight: $font-weight-bold !default;\n\n$list-inline-padding: .5rem !default;\n\n$mark-padding: .1875em !default;\n$mark-color: $body-color !default;\n$mark-bg: $yellow-100 !default;\n// scss-docs-end type-variables\n\n\n// Tables\n//\n// Customizes the `.table` component with basic values, each used across all table variations.\n\n// scss-docs-start table-variables\n$table-cell-padding-y: .5rem !default;\n$table-cell-padding-x: .5rem !default;\n$table-cell-padding-y-sm: .25rem !default;\n$table-cell-padding-x-sm: .25rem !default;\n\n$table-cell-vertical-align: top !default;\n\n$table-color: var(--#{$prefix}emphasis-color) !default;\n$table-bg: var(--#{$prefix}body-bg) !default;\n$table-accent-bg: transparent !default;\n\n$table-th-font-weight: null !default;\n\n$table-striped-color: $table-color !default;\n$table-striped-bg-factor: .05 !default;\n$table-striped-bg: rgba(var(--#{$prefix}emphasis-color-rgb), $table-striped-bg-factor) !default;\n\n$table-active-color: $table-color !default;\n$table-active-bg-factor: .1 !default;\n$table-active-bg: rgba(var(--#{$prefix}emphasis-color-rgb), $table-active-bg-factor) !default;\n\n$table-hover-color: $table-color !default;\n$table-hover-bg-factor: .075 !default;\n$table-hover-bg: rgba(var(--#{$prefix}emphasis-color-rgb), $table-hover-bg-factor) !default;\n\n$table-border-factor: .2 !default;\n$table-border-width: var(--#{$prefix}border-width) !default;\n$table-border-color: var(--#{$prefix}border-color) !default;\n\n$table-striped-order: odd !default;\n$table-striped-columns-order: even !default;\n\n$table-group-separator-color: currentcolor !default;\n\n$table-caption-color: var(--#{$prefix}secondary-color) !default;\n\n$table-bg-scale: -80% !default;\n// scss-docs-end table-variables\n\n// scss-docs-start table-loop\n$table-variants: (\n \"primary\": shift-color($primary, $table-bg-scale),\n \"secondary\": shift-color($secondary, $table-bg-scale),\n \"success\": shift-color($success, $table-bg-scale),\n \"info\": shift-color($info, $table-bg-scale),\n \"warning\": shift-color($warning, $table-bg-scale),\n \"danger\": shift-color($danger, $table-bg-scale),\n \"light\": $light,\n \"dark\": $dark,\n) !default;\n// scss-docs-end table-loop\n\n\n// Buttons + Forms\n//\n// Shared variables that are reassigned to `$input-` and `$btn-` specific variables.\n\n// scss-docs-start input-btn-variables\n$input-btn-padding-y: .375rem !default;\n$input-btn-padding-x: .75rem !default;\n$input-btn-font-family: null !default;\n$input-btn-font-size: $font-size-base !default;\n$input-btn-line-height: $line-height-base !default;\n\n$input-btn-focus-width: $focus-ring-width !default;\n$input-btn-focus-color-opacity: $focus-ring-opacity !default;\n$input-btn-focus-color: $focus-ring-color !default;\n$input-btn-focus-blur: $focus-ring-blur !default;\n$input-btn-focus-box-shadow: $focus-ring-box-shadow !default;\n\n$input-btn-padding-y-sm: .25rem !default;\n$input-btn-padding-x-sm: .5rem !default;\n$input-btn-font-size-sm: $font-size-sm !default;\n\n$input-btn-padding-y-lg: .5rem !default;\n$input-btn-padding-x-lg: 1rem !default;\n$input-btn-font-size-lg: $font-size-lg !default;\n\n$input-btn-border-width: var(--#{$prefix}border-width) !default;\n// scss-docs-end input-btn-variables\n\n\n// Buttons\n//\n// For each of Bootstrap's buttons, define text, background, and border color.\n\n// scss-docs-start btn-variables\n$btn-color: var(--#{$prefix}body-color) !default;\n$btn-padding-y: $input-btn-padding-y !default;\n$btn-padding-x: $input-btn-padding-x !default;\n$btn-font-family: $input-btn-font-family !default;\n$btn-font-size: $input-btn-font-size !default;\n$btn-line-height: $input-btn-line-height !default;\n$btn-white-space: null !default; // Set to `nowrap` to prevent text wrapping\n\n$btn-padding-y-sm: $input-btn-padding-y-sm !default;\n$btn-padding-x-sm: $input-btn-padding-x-sm !default;\n$btn-font-size-sm: $input-btn-font-size-sm !default;\n\n$btn-padding-y-lg: $input-btn-padding-y-lg !default;\n$btn-padding-x-lg: $input-btn-padding-x-lg !default;\n$btn-font-size-lg: $input-btn-font-size-lg !default;\n\n$btn-border-width: $input-btn-border-width !default;\n\n$btn-font-weight: $font-weight-normal !default;\n$btn-box-shadow: inset 0 1px 0 rgba($white, .15), 0 1px 1px rgba($black, .075) !default;\n$btn-focus-width: $input-btn-focus-width !default;\n$btn-focus-box-shadow: $input-btn-focus-box-shadow !default;\n$btn-disabled-opacity: .65 !default;\n$btn-active-box-shadow: inset 0 3px 5px rgba($black, .125) !default;\n\n$btn-link-color: var(--#{$prefix}link-color) !default;\n$btn-link-hover-color: var(--#{$prefix}link-hover-color) !default;\n$btn-link-disabled-color: $gray-600 !default;\n$btn-link-focus-shadow-rgb: to-rgb(mix(color-contrast($link-color), $link-color, 15%)) !default;\n\n// Allows for customizing button radius independently from global border radius\n$btn-border-radius: var(--#{$prefix}border-radius) !default;\n$btn-border-radius-sm: var(--#{$prefix}border-radius-sm) !default;\n$btn-border-radius-lg: var(--#{$prefix}border-radius-lg) !default;\n\n$btn-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n$btn-hover-bg-shade-amount: 15% !default;\n$btn-hover-bg-tint-amount: 15% !default;\n$btn-hover-border-shade-amount: 20% !default;\n$btn-hover-border-tint-amount: 10% !default;\n$btn-active-bg-shade-amount: 20% !default;\n$btn-active-bg-tint-amount: 20% !default;\n$btn-active-border-shade-amount: 25% !default;\n$btn-active-border-tint-amount: 10% !default;\n// scss-docs-end btn-variables\n\n\n// Forms\n\n// scss-docs-start form-text-variables\n$form-text-margin-top: .25rem !default;\n$form-text-font-size: $small-font-size !default;\n$form-text-font-style: null !default;\n$form-text-font-weight: null !default;\n$form-text-color: var(--#{$prefix}secondary-color) !default;\n// scss-docs-end form-text-variables\n\n// scss-docs-start form-label-variables\n$form-label-margin-bottom: .5rem !default;\n$form-label-font-size: null !default;\n$form-label-font-style: null !default;\n$form-label-font-weight: null !default;\n$form-label-color: null !default;\n// scss-docs-end form-label-variables\n\n// scss-docs-start form-input-variables\n$input-padding-y: $input-btn-padding-y !default;\n$input-padding-x: $input-btn-padding-x !default;\n$input-font-family: $input-btn-font-family !default;\n$input-font-size: $input-btn-font-size !default;\n$input-font-weight: $font-weight-base !default;\n$input-line-height: $input-btn-line-height !default;\n\n$input-padding-y-sm: $input-btn-padding-y-sm !default;\n$input-padding-x-sm: $input-btn-padding-x-sm !default;\n$input-font-size-sm: $input-btn-font-size-sm !default;\n\n$input-padding-y-lg: $input-btn-padding-y-lg !default;\n$input-padding-x-lg: $input-btn-padding-x-lg !default;\n$input-font-size-lg: $input-btn-font-size-lg !default;\n\n$input-bg: var(--#{$prefix}body-bg) !default;\n$input-disabled-color: null !default;\n$input-disabled-bg: var(--#{$prefix}secondary-bg) !default;\n$input-disabled-border-color: null !default;\n\n$input-color: var(--#{$prefix}body-color) !default;\n$input-border-color: var(--#{$prefix}border-color) !default;\n$input-border-width: $input-btn-border-width !default;\n$input-box-shadow: var(--#{$prefix}box-shadow-inset) !default;\n\n$input-border-radius: var(--#{$prefix}border-radius) !default;\n$input-border-radius-sm: var(--#{$prefix}border-radius-sm) !default;\n$input-border-radius-lg: var(--#{$prefix}border-radius-lg) !default;\n\n$input-focus-bg: $input-bg !default;\n$input-focus-border-color: tint-color($component-active-bg, 50%) !default;\n$input-focus-color: $input-color !default;\n$input-focus-width: $input-btn-focus-width !default;\n$input-focus-box-shadow: $input-btn-focus-box-shadow !default;\n\n$input-placeholder-color: var(--#{$prefix}secondary-color) !default;\n$input-plaintext-color: var(--#{$prefix}body-color) !default;\n\n$input-height-border: calc(#{$input-border-width} * 2) !default; // stylelint-disable-line function-disallowed-list\n\n$input-height-inner: add($input-line-height * 1em, $input-padding-y * 2) !default;\n$input-height-inner-half: add($input-line-height * .5em, $input-padding-y) !default;\n$input-height-inner-quarter: add($input-line-height * .25em, $input-padding-y * .5) !default;\n\n$input-height: add($input-line-height * 1em, add($input-padding-y * 2, $input-height-border, false)) !default;\n$input-height-sm: add($input-line-height * 1em, add($input-padding-y-sm * 2, $input-height-border, false)) !default;\n$input-height-lg: add($input-line-height * 1em, add($input-padding-y-lg * 2, $input-height-border, false)) !default;\n\n$input-transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n$form-color-width: 3rem !default;\n// scss-docs-end form-input-variables\n\n// scss-docs-start form-check-variables\n$form-check-input-width: 1em !default;\n$form-check-min-height: $font-size-base * $line-height-base !default;\n$form-check-padding-start: $form-check-input-width + .5em !default;\n$form-check-margin-bottom: .125rem !default;\n$form-check-label-color: null !default;\n$form-check-label-cursor: null !default;\n$form-check-transition: null !default;\n\n$form-check-input-active-filter: brightness(90%) !default;\n\n$form-check-input-bg: $input-bg !default;\n$form-check-input-border: var(--#{$prefix}border-width) solid var(--#{$prefix}border-color) !default;\n$form-check-input-border-radius: .25em !default;\n$form-check-radio-border-radius: 50% !default;\n$form-check-input-focus-border: $input-focus-border-color !default;\n$form-check-input-focus-box-shadow: $focus-ring-box-shadow !default;\n\n$form-check-input-checked-color: $component-active-color !default;\n$form-check-input-checked-bg-color: $component-active-bg !default;\n$form-check-input-checked-border-color: $form-check-input-checked-bg-color !default;\n$form-check-input-checked-bg-image: url(\"data:image/svg+xml,\") !default;\n$form-check-radio-checked-bg-image: url(\"data:image/svg+xml,\") !default;\n\n$form-check-input-indeterminate-color: $component-active-color !default;\n$form-check-input-indeterminate-bg-color: $component-active-bg !default;\n$form-check-input-indeterminate-border-color: $form-check-input-indeterminate-bg-color !default;\n$form-check-input-indeterminate-bg-image: url(\"data:image/svg+xml,\") !default;\n\n$form-check-input-disabled-opacity: .5 !default;\n$form-check-label-disabled-opacity: $form-check-input-disabled-opacity !default;\n$form-check-btn-check-disabled-opacity: $btn-disabled-opacity !default;\n\n$form-check-inline-margin-end: 1rem !default;\n// scss-docs-end form-check-variables\n\n// scss-docs-start form-switch-variables\n$form-switch-color: rgba($black, .25) !default;\n$form-switch-width: 2em !default;\n$form-switch-padding-start: $form-switch-width + .5em !default;\n$form-switch-bg-image: url(\"data:image/svg+xml,\") !default;\n$form-switch-border-radius: $form-switch-width !default;\n$form-switch-transition: background-position .15s ease-in-out !default;\n\n$form-switch-focus-color: $input-focus-border-color !default;\n$form-switch-focus-bg-image: url(\"data:image/svg+xml,\") !default;\n\n$form-switch-checked-color: $component-active-color !default;\n$form-switch-checked-bg-image: url(\"data:image/svg+xml,\") !default;\n$form-switch-checked-bg-position: right center !default;\n// scss-docs-end form-switch-variables\n\n// scss-docs-start input-group-variables\n$input-group-addon-padding-y: $input-padding-y !default;\n$input-group-addon-padding-x: $input-padding-x !default;\n$input-group-addon-font-weight: $input-font-weight !default;\n$input-group-addon-color: $input-color !default;\n$input-group-addon-bg: var(--#{$prefix}tertiary-bg) !default;\n$input-group-addon-border-color: $input-border-color !default;\n// scss-docs-end input-group-variables\n\n// scss-docs-start form-select-variables\n$form-select-padding-y: $input-padding-y !default;\n$form-select-padding-x: $input-padding-x !default;\n$form-select-font-family: $input-font-family !default;\n$form-select-font-size: $input-font-size !default;\n$form-select-indicator-padding: $form-select-padding-x * 3 !default; // Extra padding for background-image\n$form-select-font-weight: $input-font-weight !default;\n$form-select-line-height: $input-line-height !default;\n$form-select-color: $input-color !default;\n$form-select-bg: $input-bg !default;\n$form-select-disabled-color: null !default;\n$form-select-disabled-bg: $input-disabled-bg !default;\n$form-select-disabled-border-color: $input-disabled-border-color !default;\n$form-select-bg-position: right $form-select-padding-x center !default;\n$form-select-bg-size: 16px 12px !default; // In pixels because image dimensions\n$form-select-indicator-color: $gray-800 !default;\n$form-select-indicator: url(\"data:image/svg+xml,\") !default;\n\n$form-select-feedback-icon-padding-end: $form-select-padding-x * 2.5 + $form-select-indicator-padding !default;\n$form-select-feedback-icon-position: center right $form-select-indicator-padding !default;\n$form-select-feedback-icon-size: $input-height-inner-half $input-height-inner-half !default;\n\n$form-select-border-width: $input-border-width !default;\n$form-select-border-color: $input-border-color !default;\n$form-select-border-radius: $input-border-radius !default;\n$form-select-box-shadow: var(--#{$prefix}box-shadow-inset) !default;\n\n$form-select-focus-border-color: $input-focus-border-color !default;\n$form-select-focus-width: $input-focus-width !default;\n$form-select-focus-box-shadow: 0 0 0 $form-select-focus-width $input-btn-focus-color !default;\n\n$form-select-padding-y-sm: $input-padding-y-sm !default;\n$form-select-padding-x-sm: $input-padding-x-sm !default;\n$form-select-font-size-sm: $input-font-size-sm !default;\n$form-select-border-radius-sm: $input-border-radius-sm !default;\n\n$form-select-padding-y-lg: $input-padding-y-lg !default;\n$form-select-padding-x-lg: $input-padding-x-lg !default;\n$form-select-font-size-lg: $input-font-size-lg !default;\n$form-select-border-radius-lg: $input-border-radius-lg !default;\n\n$form-select-transition: $input-transition !default;\n// scss-docs-end form-select-variables\n\n// scss-docs-start form-range-variables\n$form-range-track-width: 100% !default;\n$form-range-track-height: .5rem !default;\n$form-range-track-cursor: pointer !default;\n$form-range-track-bg: var(--#{$prefix}secondary-bg) !default;\n$form-range-track-border-radius: 1rem !default;\n$form-range-track-box-shadow: var(--#{$prefix}box-shadow-inset) !default;\n\n$form-range-thumb-width: 1rem !default;\n$form-range-thumb-height: $form-range-thumb-width !default;\n$form-range-thumb-bg: $component-active-bg !default;\n$form-range-thumb-border: 0 !default;\n$form-range-thumb-border-radius: 1rem !default;\n$form-range-thumb-box-shadow: 0 .1rem .25rem rgba($black, .1) !default;\n$form-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-focus-box-shadow !default;\n$form-range-thumb-focus-box-shadow-width: $input-focus-width !default; // For focus box shadow issue in Edge\n$form-range-thumb-active-bg: tint-color($component-active-bg, 70%) !default;\n$form-range-thumb-disabled-bg: var(--#{$prefix}secondary-color) !default;\n$form-range-thumb-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n// scss-docs-end form-range-variables\n\n// scss-docs-start form-file-variables\n$form-file-button-color: $input-color !default;\n$form-file-button-bg: var(--#{$prefix}tertiary-bg) !default;\n$form-file-button-hover-bg: var(--#{$prefix}secondary-bg) !default;\n// scss-docs-end form-file-variables\n\n// scss-docs-start form-floating-variables\n$form-floating-height: add(3.5rem, $input-height-border) !default;\n$form-floating-line-height: 1.25 !default;\n$form-floating-padding-x: $input-padding-x !default;\n$form-floating-padding-y: 1rem !default;\n$form-floating-input-padding-t: 1.625rem !default;\n$form-floating-input-padding-b: .625rem !default;\n$form-floating-label-height: 1.5em !default;\n$form-floating-label-opacity: .65 !default;\n$form-floating-label-transform: scale(.85) translateY(-.5rem) translateX(.15rem) !default;\n$form-floating-label-disabled-color: $gray-600 !default;\n$form-floating-transition: opacity .1s ease-in-out, transform .1s ease-in-out !default;\n// scss-docs-end form-floating-variables\n\n// Form validation\n\n// scss-docs-start form-feedback-variables\n$form-feedback-margin-top: $form-text-margin-top !default;\n$form-feedback-font-size: $form-text-font-size !default;\n$form-feedback-font-style: $form-text-font-style !default;\n$form-feedback-valid-color: $success !default;\n$form-feedback-invalid-color: $danger !default;\n\n$form-feedback-icon-valid-color: $form-feedback-valid-color !default;\n$form-feedback-icon-valid: url(\"data:image/svg+xml,\") !default;\n$form-feedback-icon-invalid-color: $form-feedback-invalid-color !default;\n$form-feedback-icon-invalid: url(\"data:image/svg+xml,\") !default;\n// scss-docs-end form-feedback-variables\n\n// scss-docs-start form-validation-colors\n$form-valid-color: $form-feedback-valid-color !default;\n$form-valid-border-color: $form-feedback-valid-color !default;\n$form-invalid-color: $form-feedback-invalid-color !default;\n$form-invalid-border-color: $form-feedback-invalid-color !default;\n// scss-docs-end form-validation-colors\n\n// scss-docs-start form-validation-states\n$form-validation-states: (\n \"valid\": (\n \"color\": var(--#{$prefix}form-valid-color),\n \"icon\": $form-feedback-icon-valid,\n \"tooltip-color\": #fff,\n \"tooltip-bg-color\": var(--#{$prefix}success),\n \"focus-box-shadow\": 0 0 $input-btn-focus-blur $input-focus-width rgba(var(--#{$prefix}success-rgb), $input-btn-focus-color-opacity),\n \"border-color\": var(--#{$prefix}form-valid-border-color),\n ),\n \"invalid\": (\n \"color\": var(--#{$prefix}form-invalid-color),\n \"icon\": $form-feedback-icon-invalid,\n \"tooltip-color\": #fff,\n \"tooltip-bg-color\": var(--#{$prefix}danger),\n \"focus-box-shadow\": 0 0 $input-btn-focus-blur $input-focus-width rgba(var(--#{$prefix}danger-rgb), $input-btn-focus-color-opacity),\n \"border-color\": var(--#{$prefix}form-invalid-border-color),\n )\n) !default;\n// scss-docs-end form-validation-states\n\n// Z-index master list\n//\n// Warning: Avoid customizing these values. They're used for a bird's eye view\n// of components dependent on the z-axis and are designed to all work together.\n\n// scss-docs-start zindex-stack\n$zindex-dropdown: 1000 !default;\n$zindex-sticky: 1020 !default;\n$zindex-fixed: 1030 !default;\n$zindex-offcanvas-backdrop: 1040 !default;\n$zindex-offcanvas: 1045 !default;\n$zindex-modal-backdrop: 1050 !default;\n$zindex-modal: 1055 !default;\n$zindex-popover: 1070 !default;\n$zindex-tooltip: 1080 !default;\n$zindex-toast: 1090 !default;\n// scss-docs-end zindex-stack\n\n// scss-docs-start zindex-levels-map\n$zindex-levels: (\n n1: -1,\n 0: 0,\n 1: 1,\n 2: 2,\n 3: 3\n) !default;\n// scss-docs-end zindex-levels-map\n\n\n// Navs\n\n// scss-docs-start nav-variables\n$nav-link-padding-y: .5rem !default;\n$nav-link-padding-x: 1rem !default;\n$nav-link-font-size: null !default;\n$nav-link-font-weight: null !default;\n$nav-link-color: var(--#{$prefix}link-color) !default;\n$nav-link-hover-color: var(--#{$prefix}link-hover-color) !default;\n$nav-link-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out !default;\n$nav-link-disabled-color: var(--#{$prefix}secondary-color) !default;\n$nav-link-focus-box-shadow: $focus-ring-box-shadow !default;\n\n$nav-tabs-border-color: var(--#{$prefix}border-color) !default;\n$nav-tabs-border-width: var(--#{$prefix}border-width) !default;\n$nav-tabs-border-radius: var(--#{$prefix}border-radius) !default;\n$nav-tabs-link-hover-border-color: var(--#{$prefix}secondary-bg) var(--#{$prefix}secondary-bg) $nav-tabs-border-color !default;\n$nav-tabs-link-active-color: var(--#{$prefix}emphasis-color) !default;\n$nav-tabs-link-active-bg: var(--#{$prefix}body-bg) !default;\n$nav-tabs-link-active-border-color: var(--#{$prefix}border-color) var(--#{$prefix}border-color) $nav-tabs-link-active-bg !default;\n\n$nav-pills-border-radius: var(--#{$prefix}border-radius) !default;\n$nav-pills-link-active-color: $component-active-color !default;\n$nav-pills-link-active-bg: $component-active-bg !default;\n\n$nav-underline-gap: 1rem !default;\n$nav-underline-border-width: .125rem !default;\n$nav-underline-link-active-color: var(--#{$prefix}emphasis-color) !default;\n// scss-docs-end nav-variables\n\n\n// Navbar\n\n// scss-docs-start navbar-variables\n$navbar-padding-y: $spacer * .5 !default;\n$navbar-padding-x: null !default;\n\n$navbar-nav-link-padding-x: .5rem !default;\n\n$navbar-brand-font-size: $font-size-lg !default;\n// Compute the navbar-brand padding-y so the navbar-brand will have the same height as navbar-text and nav-link\n$nav-link-height: $font-size-base * $line-height-base + $nav-link-padding-y * 2 !default;\n$navbar-brand-height: $navbar-brand-font-size * $line-height-base !default;\n$navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) * .5 !default;\n$navbar-brand-margin-end: 1rem !default;\n\n$navbar-toggler-padding-y: .25rem !default;\n$navbar-toggler-padding-x: .75rem !default;\n$navbar-toggler-font-size: $font-size-lg !default;\n$navbar-toggler-border-radius: $btn-border-radius !default;\n$navbar-toggler-focus-width: $btn-focus-width !default;\n$navbar-toggler-transition: box-shadow .15s ease-in-out !default;\n\n$navbar-light-color: rgba(var(--#{$prefix}emphasis-color-rgb), .65) !default;\n$navbar-light-hover-color: rgba(var(--#{$prefix}emphasis-color-rgb), .8) !default;\n$navbar-light-active-color: rgba(var(--#{$prefix}emphasis-color-rgb), 1) !default;\n$navbar-light-disabled-color: rgba(var(--#{$prefix}emphasis-color-rgb), .3) !default;\n$navbar-light-icon-color: rgba($body-color, .75) !default;\n$navbar-light-toggler-icon-bg: url(\"data:image/svg+xml,\") !default;\n$navbar-light-toggler-border-color: rgba(var(--#{$prefix}emphasis-color-rgb), .15) !default;\n$navbar-light-brand-color: $navbar-light-active-color !default;\n$navbar-light-brand-hover-color: $navbar-light-active-color !default;\n// scss-docs-end navbar-variables\n\n// scss-docs-start navbar-dark-variables\n$navbar-dark-color: rgba($white, .55) !default;\n$navbar-dark-hover-color: rgba($white, .75) !default;\n$navbar-dark-active-color: $white !default;\n$navbar-dark-disabled-color: rgba($white, .25) !default;\n$navbar-dark-icon-color: $navbar-dark-color !default;\n$navbar-dark-toggler-icon-bg: url(\"data:image/svg+xml,\") !default;\n$navbar-dark-toggler-border-color: rgba($white, .1) !default;\n$navbar-dark-brand-color: $navbar-dark-active-color !default;\n$navbar-dark-brand-hover-color: $navbar-dark-active-color !default;\n// scss-docs-end navbar-dark-variables\n\n\n// Dropdowns\n//\n// Dropdown menu container and contents.\n\n// scss-docs-start dropdown-variables\n$dropdown-min-width: 10rem !default;\n$dropdown-padding-x: 0 !default;\n$dropdown-padding-y: .5rem !default;\n$dropdown-spacer: .125rem !default;\n$dropdown-font-size: $font-size-base !default;\n$dropdown-color: var(--#{$prefix}body-color) !default;\n$dropdown-bg: var(--#{$prefix}body-bg) !default;\n$dropdown-border-color: var(--#{$prefix}border-color-translucent) !default;\n$dropdown-border-radius: var(--#{$prefix}border-radius) !default;\n$dropdown-border-width: var(--#{$prefix}border-width) !default;\n$dropdown-inner-border-radius: calc(#{$dropdown-border-radius} - #{$dropdown-border-width}) !default; // stylelint-disable-line function-disallowed-list\n$dropdown-divider-bg: $dropdown-border-color !default;\n$dropdown-divider-margin-y: $spacer * .5 !default;\n$dropdown-box-shadow: var(--#{$prefix}box-shadow) !default;\n\n$dropdown-link-color: var(--#{$prefix}body-color) !default;\n$dropdown-link-hover-color: $dropdown-link-color !default;\n$dropdown-link-hover-bg: var(--#{$prefix}tertiary-bg) !default;\n\n$dropdown-link-active-color: $component-active-color !default;\n$dropdown-link-active-bg: $component-active-bg !default;\n\n$dropdown-link-disabled-color: var(--#{$prefix}tertiary-color) !default;\n\n$dropdown-item-padding-y: $spacer * .25 !default;\n$dropdown-item-padding-x: $spacer !default;\n\n$dropdown-header-color: $gray-600 !default;\n$dropdown-header-padding-x: $dropdown-item-padding-x !default;\n$dropdown-header-padding-y: $dropdown-padding-y !default;\n// fusv-disable\n$dropdown-header-padding: $dropdown-header-padding-y $dropdown-header-padding-x !default; // Deprecated in v5.2.0\n// fusv-enable\n// scss-docs-end dropdown-variables\n\n// scss-docs-start dropdown-dark-variables\n$dropdown-dark-color: $gray-300 !default;\n$dropdown-dark-bg: $gray-800 !default;\n$dropdown-dark-border-color: $dropdown-border-color !default;\n$dropdown-dark-divider-bg: $dropdown-divider-bg !default;\n$dropdown-dark-box-shadow: null !default;\n$dropdown-dark-link-color: $dropdown-dark-color !default;\n$dropdown-dark-link-hover-color: $white !default;\n$dropdown-dark-link-hover-bg: rgba($white, .15) !default;\n$dropdown-dark-link-active-color: $dropdown-link-active-color !default;\n$dropdown-dark-link-active-bg: $dropdown-link-active-bg !default;\n$dropdown-dark-link-disabled-color: $gray-500 !default;\n$dropdown-dark-header-color: $gray-500 !default;\n// scss-docs-end dropdown-dark-variables\n\n\n// Pagination\n\n// scss-docs-start pagination-variables\n$pagination-padding-y: .375rem !default;\n$pagination-padding-x: .75rem !default;\n$pagination-padding-y-sm: .25rem !default;\n$pagination-padding-x-sm: .5rem !default;\n$pagination-padding-y-lg: .75rem !default;\n$pagination-padding-x-lg: 1.5rem !default;\n\n$pagination-font-size: $font-size-base !default;\n\n$pagination-color: var(--#{$prefix}link-color) !default;\n$pagination-bg: var(--#{$prefix}body-bg) !default;\n$pagination-border-radius: var(--#{$prefix}border-radius) !default;\n$pagination-border-width: var(--#{$prefix}border-width) !default;\n$pagination-margin-start: calc(#{$pagination-border-width} * -1) !default; // stylelint-disable-line function-disallowed-list\n$pagination-border-color: var(--#{$prefix}border-color) !default;\n\n$pagination-focus-color: var(--#{$prefix}link-hover-color) !default;\n$pagination-focus-bg: var(--#{$prefix}secondary-bg) !default;\n$pagination-focus-box-shadow: $focus-ring-box-shadow !default;\n$pagination-focus-outline: 0 !default;\n\n$pagination-hover-color: var(--#{$prefix}link-hover-color) !default;\n$pagination-hover-bg: var(--#{$prefix}tertiary-bg) !default;\n$pagination-hover-border-color: var(--#{$prefix}border-color) !default; // Todo in v6: remove this?\n\n$pagination-active-color: $component-active-color !default;\n$pagination-active-bg: $component-active-bg !default;\n$pagination-active-border-color: $component-active-bg !default;\n\n$pagination-disabled-color: var(--#{$prefix}secondary-color) !default;\n$pagination-disabled-bg: var(--#{$prefix}secondary-bg) !default;\n$pagination-disabled-border-color: var(--#{$prefix}border-color) !default;\n\n$pagination-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;\n\n$pagination-border-radius-sm: var(--#{$prefix}border-radius-sm) !default;\n$pagination-border-radius-lg: var(--#{$prefix}border-radius-lg) !default;\n// scss-docs-end pagination-variables\n\n\n// Placeholders\n\n// scss-docs-start placeholders\n$placeholder-opacity-max: .5 !default;\n$placeholder-opacity-min: .2 !default;\n// scss-docs-end placeholders\n\n// Cards\n\n// scss-docs-start card-variables\n$card-spacer-y: $spacer !default;\n$card-spacer-x: $spacer !default;\n$card-title-spacer-y: $spacer * .5 !default;\n$card-title-color: null !default;\n$card-subtitle-color: null !default;\n$card-border-width: var(--#{$prefix}border-width) !default;\n$card-border-color: var(--#{$prefix}border-color-translucent) !default;\n$card-border-radius: var(--#{$prefix}border-radius) !default;\n$card-box-shadow: null !default;\n$card-inner-border-radius: subtract($card-border-radius, $card-border-width) !default;\n$card-cap-padding-y: $card-spacer-y * .5 !default;\n$card-cap-padding-x: $card-spacer-x !default;\n$card-cap-bg: rgba(var(--#{$prefix}body-color-rgb), .03) !default;\n$card-cap-color: null !default;\n$card-height: null !default;\n$card-color: null !default;\n$card-bg: var(--#{$prefix}body-bg) !default;\n$card-img-overlay-padding: $spacer !default;\n$card-group-margin: $grid-gutter-width * .5 !default;\n// scss-docs-end card-variables\n\n// Accordion\n\n// scss-docs-start accordion-variables\n$accordion-padding-y: 1rem !default;\n$accordion-padding-x: 1.25rem !default;\n$accordion-color: var(--#{$prefix}body-color) !default;\n$accordion-bg: var(--#{$prefix}body-bg) !default;\n$accordion-border-width: var(--#{$prefix}border-width) !default;\n$accordion-border-color: var(--#{$prefix}border-color) !default;\n$accordion-border-radius: var(--#{$prefix}border-radius) !default;\n$accordion-inner-border-radius: subtract($accordion-border-radius, $accordion-border-width) !default;\n\n$accordion-body-padding-y: $accordion-padding-y !default;\n$accordion-body-padding-x: $accordion-padding-x !default;\n\n$accordion-button-padding-y: $accordion-padding-y !default;\n$accordion-button-padding-x: $accordion-padding-x !default;\n$accordion-button-color: var(--#{$prefix}body-color) !default;\n$accordion-button-bg: var(--#{$prefix}accordion-bg) !default;\n$accordion-transition: $btn-transition, border-radius .15s ease !default;\n$accordion-button-active-bg: var(--#{$prefix}primary-bg-subtle) !default;\n$accordion-button-active-color: var(--#{$prefix}primary-text-emphasis) !default;\n\n// fusv-disable\n$accordion-button-focus-border-color: $input-focus-border-color !default; // Deprecated in v5.3.3\n// fusv-enable\n$accordion-button-focus-box-shadow: $btn-focus-box-shadow !default;\n\n$accordion-icon-width: 1.25rem !default;\n$accordion-icon-color: $body-color !default;\n$accordion-icon-active-color: $primary-text-emphasis !default;\n$accordion-icon-transition: transform .2s ease-in-out !default;\n$accordion-icon-transform: rotate(-180deg) !default;\n\n$accordion-button-icon: url(\"data:image/svg+xml,\") !default;\n$accordion-button-active-icon: url(\"data:image/svg+xml,\") !default;\n// scss-docs-end accordion-variables\n\n// Tooltips\n\n// scss-docs-start tooltip-variables\n$tooltip-font-size: $font-size-sm !default;\n$tooltip-max-width: 200px !default;\n$tooltip-color: var(--#{$prefix}body-bg) !default;\n$tooltip-bg: var(--#{$prefix}emphasis-color) !default;\n$tooltip-border-radius: var(--#{$prefix}border-radius) !default;\n$tooltip-opacity: .9 !default;\n$tooltip-padding-y: $spacer * .25 !default;\n$tooltip-padding-x: $spacer * .5 !default;\n$tooltip-margin: null !default; // TODO: remove this in v6\n\n$tooltip-arrow-width: .8rem !default;\n$tooltip-arrow-height: .4rem !default;\n// fusv-disable\n$tooltip-arrow-color: null !default; // Deprecated in Bootstrap 5.2.0 for CSS variables\n// fusv-enable\n// scss-docs-end tooltip-variables\n\n// Form tooltips must come after regular tooltips\n// scss-docs-start tooltip-feedback-variables\n$form-feedback-tooltip-padding-y: $tooltip-padding-y !default;\n$form-feedback-tooltip-padding-x: $tooltip-padding-x !default;\n$form-feedback-tooltip-font-size: $tooltip-font-size !default;\n$form-feedback-tooltip-line-height: null !default;\n$form-feedback-tooltip-opacity: $tooltip-opacity !default;\n$form-feedback-tooltip-border-radius: $tooltip-border-radius !default;\n// scss-docs-end tooltip-feedback-variables\n\n\n// Popovers\n\n// scss-docs-start popover-variables\n$popover-font-size: $font-size-sm !default;\n$popover-bg: var(--#{$prefix}body-bg) !default;\n$popover-max-width: 276px !default;\n$popover-border-width: var(--#{$prefix}border-width) !default;\n$popover-border-color: var(--#{$prefix}border-color-translucent) !default;\n$popover-border-radius: var(--#{$prefix}border-radius-lg) !default;\n$popover-inner-border-radius: calc(#{$popover-border-radius} - #{$popover-border-width}) !default; // stylelint-disable-line function-disallowed-list\n$popover-box-shadow: var(--#{$prefix}box-shadow) !default;\n\n$popover-header-font-size: $font-size-base !default;\n$popover-header-bg: var(--#{$prefix}secondary-bg) !default;\n$popover-header-color: $headings-color !default;\n$popover-header-padding-y: .5rem !default;\n$popover-header-padding-x: $spacer !default;\n\n$popover-body-color: var(--#{$prefix}body-color) !default;\n$popover-body-padding-y: $spacer !default;\n$popover-body-padding-x: $spacer !default;\n\n$popover-arrow-width: 1rem !default;\n$popover-arrow-height: .5rem !default;\n// scss-docs-end popover-variables\n\n// fusv-disable\n// Deprecated in Bootstrap 5.2.0 for CSS variables\n$popover-arrow-color: $popover-bg !default;\n$popover-arrow-outer-color: var(--#{$prefix}border-color-translucent) !default;\n// fusv-enable\n\n\n// Toasts\n\n// scss-docs-start toast-variables\n$toast-max-width: 350px !default;\n$toast-padding-x: .75rem !default;\n$toast-padding-y: .5rem !default;\n$toast-font-size: .875rem !default;\n$toast-color: null !default;\n$toast-background-color: rgba(var(--#{$prefix}body-bg-rgb), .85) !default;\n$toast-border-width: var(--#{$prefix}border-width) !default;\n$toast-border-color: var(--#{$prefix}border-color-translucent) !default;\n$toast-border-radius: var(--#{$prefix}border-radius) !default;\n$toast-box-shadow: var(--#{$prefix}box-shadow) !default;\n$toast-spacing: $container-padding-x !default;\n\n$toast-header-color: var(--#{$prefix}secondary-color) !default;\n$toast-header-background-color: rgba(var(--#{$prefix}body-bg-rgb), .85) !default;\n$toast-header-border-color: $toast-border-color !default;\n// scss-docs-end toast-variables\n\n\n// Badges\n\n// scss-docs-start badge-variables\n$badge-font-size: .75em !default;\n$badge-font-weight: $font-weight-bold !default;\n$badge-color: $white !default;\n$badge-padding-y: .35em !default;\n$badge-padding-x: .65em !default;\n$badge-border-radius: var(--#{$prefix}border-radius) !default;\n// scss-docs-end badge-variables\n\n\n// Modals\n\n// scss-docs-start modal-variables\n$modal-inner-padding: $spacer !default;\n\n$modal-footer-margin-between: .5rem !default;\n\n$modal-dialog-margin: .5rem !default;\n$modal-dialog-margin-y-sm-up: 1.75rem !default;\n\n$modal-title-line-height: $line-height-base !default;\n\n$modal-content-color: null !default;\n$modal-content-bg: var(--#{$prefix}body-bg) !default;\n$modal-content-border-color: var(--#{$prefix}border-color-translucent) !default;\n$modal-content-border-width: var(--#{$prefix}border-width) !default;\n$modal-content-border-radius: var(--#{$prefix}border-radius-lg) !default;\n$modal-content-inner-border-radius: subtract($modal-content-border-radius, $modal-content-border-width) !default;\n$modal-content-box-shadow-xs: var(--#{$prefix}box-shadow-sm) !default;\n$modal-content-box-shadow-sm-up: var(--#{$prefix}box-shadow) !default;\n\n$modal-backdrop-bg: $black !default;\n$modal-backdrop-opacity: .5 !default;\n\n$modal-header-border-color: var(--#{$prefix}border-color) !default;\n$modal-header-border-width: $modal-content-border-width !default;\n$modal-header-padding-y: $modal-inner-padding !default;\n$modal-header-padding-x: $modal-inner-padding !default;\n$modal-header-padding: $modal-header-padding-y $modal-header-padding-x !default; // Keep this for backwards compatibility\n\n$modal-footer-bg: null !default;\n$modal-footer-border-color: $modal-header-border-color !default;\n$modal-footer-border-width: $modal-header-border-width !default;\n\n$modal-sm: 300px !default;\n$modal-md: 500px !default;\n$modal-lg: 800px !default;\n$modal-xl: 1140px !default;\n\n$modal-fade-transform: translate(0, -50px) !default;\n$modal-show-transform: none !default;\n$modal-transition: transform .3s ease-out !default;\n$modal-scale-transform: scale(1.02) !default;\n// scss-docs-end modal-variables\n\n\n// Alerts\n//\n// Define alert colors, border radius, and padding.\n\n// scss-docs-start alert-variables\n$alert-padding-y: $spacer !default;\n$alert-padding-x: $spacer !default;\n$alert-margin-bottom: 1rem !default;\n$alert-border-radius: var(--#{$prefix}border-radius) !default;\n$alert-link-font-weight: $font-weight-bold !default;\n$alert-border-width: var(--#{$prefix}border-width) !default;\n$alert-dismissible-padding-r: $alert-padding-x * 3 !default; // 3x covers width of x plus default padding on either side\n// scss-docs-end alert-variables\n\n// fusv-disable\n$alert-bg-scale: -80% !default; // Deprecated in v5.2.0, to be removed in v6\n$alert-border-scale: -70% !default; // Deprecated in v5.2.0, to be removed in v6\n$alert-color-scale: 40% !default; // Deprecated in v5.2.0, to be removed in v6\n// fusv-enable\n\n// Progress bars\n\n// scss-docs-start progress-variables\n$progress-height: 1rem !default;\n$progress-font-size: $font-size-base * .75 !default;\n$progress-bg: var(--#{$prefix}secondary-bg) !default;\n$progress-border-radius: var(--#{$prefix}border-radius) !default;\n$progress-box-shadow: var(--#{$prefix}box-shadow-inset) !default;\n$progress-bar-color: $white !default;\n$progress-bar-bg: $primary !default;\n$progress-bar-animation-timing: 1s linear infinite !default;\n$progress-bar-transition: width .6s ease !default;\n// scss-docs-end progress-variables\n\n\n// List group\n\n// scss-docs-start list-group-variables\n$list-group-color: var(--#{$prefix}body-color) !default;\n$list-group-bg: var(--#{$prefix}body-bg) !default;\n$list-group-border-color: var(--#{$prefix}border-color) !default;\n$list-group-border-width: var(--#{$prefix}border-width) !default;\n$list-group-border-radius: var(--#{$prefix}border-radius) !default;\n\n$list-group-item-padding-y: $spacer * .5 !default;\n$list-group-item-padding-x: $spacer !default;\n// fusv-disable\n$list-group-item-bg-scale: -80% !default; // Deprecated in v5.3.0\n$list-group-item-color-scale: 40% !default; // Deprecated in v5.3.0\n// fusv-enable\n\n$list-group-hover-bg: var(--#{$prefix}tertiary-bg) !default;\n$list-group-active-color: $component-active-color !default;\n$list-group-active-bg: $component-active-bg !default;\n$list-group-active-border-color: $list-group-active-bg !default;\n\n$list-group-disabled-color: var(--#{$prefix}secondary-color) !default;\n$list-group-disabled-bg: $list-group-bg !default;\n\n$list-group-action-color: var(--#{$prefix}secondary-color) !default;\n$list-group-action-hover-color: var(--#{$prefix}emphasis-color) !default;\n\n$list-group-action-active-color: var(--#{$prefix}body-color) !default;\n$list-group-action-active-bg: var(--#{$prefix}secondary-bg) !default;\n// scss-docs-end list-group-variables\n\n\n// Image thumbnails\n\n// scss-docs-start thumbnail-variables\n$thumbnail-padding: .25rem !default;\n$thumbnail-bg: var(--#{$prefix}body-bg) !default;\n$thumbnail-border-width: var(--#{$prefix}border-width) !default;\n$thumbnail-border-color: var(--#{$prefix}border-color) !default;\n$thumbnail-border-radius: var(--#{$prefix}border-radius) !default;\n$thumbnail-box-shadow: var(--#{$prefix}box-shadow-sm) !default;\n// scss-docs-end thumbnail-variables\n\n\n// Figures\n\n// scss-docs-start figure-variables\n$figure-caption-font-size: $small-font-size !default;\n$figure-caption-color: var(--#{$prefix}secondary-color) !default;\n// scss-docs-end figure-variables\n\n\n// Breadcrumbs\n\n// scss-docs-start breadcrumb-variables\n$breadcrumb-font-size: null !default;\n$breadcrumb-padding-y: 0 !default;\n$breadcrumb-padding-x: 0 !default;\n$breadcrumb-item-padding-x: .5rem !default;\n$breadcrumb-margin-bottom: 1rem !default;\n$breadcrumb-bg: null !default;\n$breadcrumb-divider-color: var(--#{$prefix}secondary-color) !default;\n$breadcrumb-active-color: var(--#{$prefix}secondary-color) !default;\n$breadcrumb-divider: quote(\"/\") !default;\n$breadcrumb-divider-flipped: $breadcrumb-divider !default;\n$breadcrumb-border-radius: null !default;\n// scss-docs-end breadcrumb-variables\n\n// Carousel\n\n// scss-docs-start carousel-variables\n$carousel-control-color: $white !default;\n$carousel-control-width: 15% !default;\n$carousel-control-opacity: .5 !default;\n$carousel-control-hover-opacity: .9 !default;\n$carousel-control-transition: opacity .15s ease !default;\n\n$carousel-indicator-width: 30px !default;\n$carousel-indicator-height: 3px !default;\n$carousel-indicator-hit-area-height: 10px !default;\n$carousel-indicator-spacer: 3px !default;\n$carousel-indicator-opacity: .5 !default;\n$carousel-indicator-active-bg: $white !default;\n$carousel-indicator-active-opacity: 1 !default;\n$carousel-indicator-transition: opacity .6s ease !default;\n\n$carousel-caption-width: 70% !default;\n$carousel-caption-color: $white !default;\n$carousel-caption-padding-y: 1.25rem !default;\n$carousel-caption-spacer: 1.25rem !default;\n\n$carousel-control-icon-width: 2rem !default;\n\n$carousel-control-prev-icon-bg: url(\"data:image/svg+xml,\") !default;\n$carousel-control-next-icon-bg: url(\"data:image/svg+xml,\") !default;\n\n$carousel-transition-duration: .6s !default;\n$carousel-transition: transform $carousel-transition-duration ease-in-out !default; // Define transform transition first if using multiple transitions (e.g., `transform 2s ease, opacity .5s ease-out`)\n// scss-docs-end carousel-variables\n\n// scss-docs-start carousel-dark-variables\n$carousel-dark-indicator-active-bg: $black !default;\n$carousel-dark-caption-color: $black !default;\n$carousel-dark-control-icon-filter: invert(1) grayscale(100) !default;\n// scss-docs-end carousel-dark-variables\n\n\n// Spinners\n\n// scss-docs-start spinner-variables\n$spinner-width: 2rem !default;\n$spinner-height: $spinner-width !default;\n$spinner-vertical-align: -.125em !default;\n$spinner-border-width: .25em !default;\n$spinner-animation-speed: .75s !default;\n\n$spinner-width-sm: 1rem !default;\n$spinner-height-sm: $spinner-width-sm !default;\n$spinner-border-width-sm: .2em !default;\n// scss-docs-end spinner-variables\n\n\n// Close\n\n// scss-docs-start close-variables\n$btn-close-width: 1em !default;\n$btn-close-height: $btn-close-width !default;\n$btn-close-padding-x: .25em !default;\n$btn-close-padding-y: $btn-close-padding-x !default;\n$btn-close-color: $black !default;\n$btn-close-bg: url(\"data:image/svg+xml,\") !default;\n$btn-close-focus-shadow: $focus-ring-box-shadow !default;\n$btn-close-opacity: .5 !default;\n$btn-close-hover-opacity: .75 !default;\n$btn-close-focus-opacity: 1 !default;\n$btn-close-disabled-opacity: .25 !default;\n$btn-close-white-filter: invert(1) grayscale(100%) brightness(200%) !default;\n// scss-docs-end close-variables\n\n\n// Offcanvas\n\n// scss-docs-start offcanvas-variables\n$offcanvas-padding-y: $modal-inner-padding !default;\n$offcanvas-padding-x: $modal-inner-padding !default;\n$offcanvas-horizontal-width: 400px !default;\n$offcanvas-vertical-height: 30vh !default;\n$offcanvas-transition-duration: .3s !default;\n$offcanvas-border-color: $modal-content-border-color !default;\n$offcanvas-border-width: $modal-content-border-width !default;\n$offcanvas-title-line-height: $modal-title-line-height !default;\n$offcanvas-bg-color: var(--#{$prefix}body-bg) !default;\n$offcanvas-color: var(--#{$prefix}body-color) !default;\n$offcanvas-box-shadow: $modal-content-box-shadow-xs !default;\n$offcanvas-backdrop-bg: $modal-backdrop-bg !default;\n$offcanvas-backdrop-opacity: $modal-backdrop-opacity !default;\n// scss-docs-end offcanvas-variables\n\n// Code\n\n$code-font-size: $small-font-size !default;\n$code-color: $pink !default;\n\n$kbd-padding-y: .1875rem !default;\n$kbd-padding-x: .375rem !default;\n$kbd-font-size: $code-font-size !default;\n$kbd-color: var(--#{$prefix}body-bg) !default;\n$kbd-bg: var(--#{$prefix}body-color) !default;\n$nested-kbd-font-weight: null !default; // Deprecated in v5.2.0, removing in v6\n\n$pre-color: null !default;\n\n@import \"variables-dark\"; // TODO: can be removed safely in v6, only here to avoid breaking changes in v5.3\n","// Row\n//\n// Rows contain your columns.\n\n:root {\n @each $name, $value in $grid-breakpoints {\n --#{$prefix}breakpoint-#{$name}: #{$value};\n }\n}\n\n@if $enable-grid-classes {\n .row {\n @include make-row();\n\n > * {\n @include make-col-ready();\n }\n }\n}\n\n@if $enable-cssgrid {\n .grid {\n display: grid;\n grid-template-rows: repeat(var(--#{$prefix}rows, 1), 1fr);\n grid-template-columns: repeat(var(--#{$prefix}columns, #{$grid-columns}), 1fr);\n gap: var(--#{$prefix}gap, #{$grid-gutter-width});\n\n @include make-cssgrid();\n }\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n@if $enable-grid-classes {\n @include make-grid-columns();\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n@mixin make-row($gutter: $grid-gutter-width) {\n --#{$prefix}gutter-x: #{$gutter};\n --#{$prefix}gutter-y: 0;\n display: flex;\n flex-wrap: wrap;\n // TODO: Revisit calc order after https://github.com/react-bootstrap/react-bootstrap/issues/6039 is fixed\n margin-top: calc(-1 * var(--#{$prefix}gutter-y)); // stylelint-disable-line function-disallowed-list\n margin-right: calc(-.5 * var(--#{$prefix}gutter-x)); // stylelint-disable-line function-disallowed-list\n margin-left: calc(-.5 * var(--#{$prefix}gutter-x)); // stylelint-disable-line function-disallowed-list\n}\n\n@mixin make-col-ready() {\n // Add box sizing if only the grid is loaded\n box-sizing: if(variable-exists(include-column-box-sizing) and $include-column-box-sizing, border-box, null);\n // Prevent columns from becoming too narrow when at smaller grid tiers by\n // always setting `width: 100%;`. This works because we set the width\n // later on to override this initial width.\n flex-shrink: 0;\n width: 100%;\n max-width: 100%; // Prevent `.col-auto`, `.col` (& responsive variants) from breaking out the grid\n padding-right: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list\n padding-left: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list\n margin-top: var(--#{$prefix}gutter-y);\n}\n\n@mixin make-col($size: false, $columns: $grid-columns) {\n @if $size {\n flex: 0 0 auto;\n width: percentage(divide($size, $columns));\n\n } @else {\n flex: 1 1 0;\n max-width: 100%;\n }\n}\n\n@mixin make-col-auto() {\n flex: 0 0 auto;\n width: auto;\n}\n\n@mixin make-col-offset($size, $columns: $grid-columns) {\n $num: divide($size, $columns);\n margin-left: if($num == 0, 0, percentage($num));\n}\n\n// Row columns\n//\n// Specify on a parent element(e.g., .row) to force immediate children into NN\n// number of columns. Supports wrapping to new lines, but does not do a Masonry\n// style grid.\n@mixin row-cols($count) {\n > * {\n flex: 0 0 auto;\n width: percentage(divide(1, $count));\n }\n}\n\n// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `$grid-columns`.\n\n@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {\n @each $breakpoint in map-keys($breakpoints) {\n $infix: breakpoint-infix($breakpoint, $breakpoints);\n\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n // Provide basic `.col-{bp}` classes for equal-width flexbox columns\n .col#{$infix} {\n flex: 1 0 0%; // Flexbugs #4: https://github.com/philipwalton/flexbugs#flexbug-4\n }\n\n .row-cols#{$infix}-auto > * {\n @include make-col-auto();\n }\n\n @if $grid-row-columns > 0 {\n @for $i from 1 through $grid-row-columns {\n .row-cols#{$infix}-#{$i} {\n @include row-cols($i);\n }\n }\n }\n\n .col#{$infix}-auto {\n @include make-col-auto();\n }\n\n @if $columns > 0 {\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @include make-col($i, $columns);\n }\n }\n\n // `$columns - 1` because offsetting by the width of an entire row isn't possible\n @for $i from 0 through ($columns - 1) {\n @if not ($infix == \"\" and $i == 0) { // Avoid emitting useless .offset-0\n .offset#{$infix}-#{$i} {\n @include make-col-offset($i, $columns);\n }\n }\n }\n }\n\n // Gutters\n //\n // Make use of `.g-*`, `.gx-*` or `.gy-*` utilities to change spacing between the columns.\n @each $key, $value in $gutters {\n .g#{$infix}-#{$key},\n .gx#{$infix}-#{$key} {\n --#{$prefix}gutter-x: #{$value};\n }\n\n .g#{$infix}-#{$key},\n .gy#{$infix}-#{$key} {\n --#{$prefix}gutter-y: #{$value};\n }\n }\n }\n }\n}\n\n@mixin make-cssgrid($columns: $grid-columns, $breakpoints: $grid-breakpoints) {\n @each $breakpoint in map-keys($breakpoints) {\n $infix: breakpoint-infix($breakpoint, $breakpoints);\n\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n @if $columns > 0 {\n @for $i from 1 through $columns {\n .g-col#{$infix}-#{$i} {\n grid-column: auto / span $i;\n }\n }\n\n // Start with `1` because `0` is an invalid value.\n // Ends with `$columns - 1` because offsetting by the width of an entire row isn't possible.\n @for $i from 1 through ($columns - 1) {\n .g-start#{$infix}-#{$i} {\n grid-column-start: $i;\n }\n }\n }\n }\n }\n}\n","// Utility generator\n// Used to generate utilities & print utilities\n@mixin generate-utility($utility, $infix: \"\", $is-rfs-media-query: false) {\n $values: map-get($utility, values);\n\n // If the values are a list or string, convert it into a map\n @if type-of($values) == \"string\" or type-of(nth($values, 1)) != \"list\" {\n $values: zip($values, $values);\n }\n\n @each $key, $value in $values {\n $properties: map-get($utility, property);\n\n // Multiple properties are possible, for example with vertical or horizontal margins or paddings\n @if type-of($properties) == \"string\" {\n $properties: append((), $properties);\n }\n\n // Use custom class if present\n $property-class: if(map-has-key($utility, class), map-get($utility, class), nth($properties, 1));\n $property-class: if($property-class == null, \"\", $property-class);\n\n // Use custom CSS variable name if present, otherwise default to `class`\n $css-variable-name: if(map-has-key($utility, css-variable-name), map-get($utility, css-variable-name), map-get($utility, class));\n\n // State params to generate pseudo-classes\n $state: if(map-has-key($utility, state), map-get($utility, state), ());\n\n $infix: if($property-class == \"\" and str-slice($infix, 1, 1) == \"-\", str-slice($infix, 2), $infix);\n\n // Don't prefix if value key is null (e.g. with shadow class)\n $property-class-modifier: if($key, if($property-class == \"\" and $infix == \"\", \"\", \"-\") + $key, \"\");\n\n @if map-get($utility, rfs) {\n // Inside the media query\n @if $is-rfs-media-query {\n $val: rfs-value($value);\n\n // Do not render anything if fluid and non fluid values are the same\n $value: if($val == rfs-fluid-value($value), null, $val);\n }\n @else {\n $value: rfs-fluid-value($value);\n }\n }\n\n $is-css-var: map-get($utility, css-var);\n $is-local-vars: map-get($utility, local-vars);\n $is-rtl: map-get($utility, rtl);\n\n @if $value != null {\n @if $is-rtl == false {\n /* rtl:begin:remove */\n }\n\n @if $is-css-var {\n .#{$property-class + $infix + $property-class-modifier} {\n --#{$prefix}#{$css-variable-name}: #{$value};\n }\n\n @each $pseudo in $state {\n .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} {\n --#{$prefix}#{$css-variable-name}: #{$value};\n }\n }\n } @else {\n .#{$property-class + $infix + $property-class-modifier} {\n @each $property in $properties {\n @if $is-local-vars {\n @each $local-var, $variable in $is-local-vars {\n --#{$prefix}#{$local-var}: #{$variable};\n }\n }\n #{$property}: $value if($enable-important-utilities, !important, null);\n }\n }\n\n @each $pseudo in $state {\n .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} {\n @each $property in $properties {\n @if $is-local-vars {\n @each $local-var, $variable in $is-local-vars {\n --#{$prefix}#{$local-var}: #{$variable};\n }\n }\n #{$property}: $value if($enable-important-utilities, !important, null);\n }\n }\n }\n }\n\n @if $is-rtl == false {\n /* rtl:end:remove */\n }\n }\n }\n}\n","// Loop over each breakpoint\n@each $breakpoint in map-keys($grid-breakpoints) {\n\n // Generate media query if needed\n @include media-breakpoint-up($breakpoint) {\n $infix: breakpoint-infix($breakpoint, $grid-breakpoints);\n\n // Loop over each utility property\n @each $key, $utility in $utilities {\n // The utility can be disabled with `false`, thus check if the utility is a map first\n // Only proceed if responsive media queries are enabled or if it's the base media query\n @if type-of($utility) == \"map\" and (map-get($utility, responsive) or $infix == \"\") {\n @include generate-utility($utility, $infix);\n }\n }\n }\n}\n\n// RFS rescaling\n@media (min-width: $rfs-mq-value) {\n @each $breakpoint in map-keys($grid-breakpoints) {\n $infix: breakpoint-infix($breakpoint, $grid-breakpoints);\n\n @if (map-get($grid-breakpoints, $breakpoint) < $rfs-breakpoint) {\n // Loop over each utility property\n @each $key, $utility in $utilities {\n // The utility can be disabled with `false`, thus check if the utility is a map first\n // Only proceed if responsive media queries are enabled or if it's the base media query\n @if type-of($utility) == \"map\" and map-get($utility, rfs) and (map-get($utility, responsive) or $infix == \"\") {\n @include generate-utility($utility, $infix, true);\n }\n }\n }\n }\n}\n\n\n// Print utilities\n@media print {\n @each $key, $utility in $utilities {\n // The utility can be disabled with `false`, thus check if the utility is a map first\n // Then check if the utility needs print styles\n @if type-of($utility) == \"map\" and map-get($utility, print) == true {\n @include generate-utility($utility, \"-print\");\n }\n }\n}\n"]} \ No newline at end of file diff --git a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css b/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css deleted file mode 100644 index 672cbc2..0000000 --- a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Bootstrap Grid v5.3.3 (https://getbootstrap.com/) - * Copyright 2011-2024 The Bootstrap Authors - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-left:calc(var(--bs-gutter-x) * .5);padding-right:calc(var(--bs-gutter-x) * .5);margin-left:auto;margin-right:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}:root{--bs-breakpoint-xs:0;--bs-breakpoint-sm:576px;--bs-breakpoint-md:768px;--bs-breakpoint-lg:992px;--bs-breakpoint-xl:1200px;--bs-breakpoint-xxl:1400px}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-left:calc(-.5 * var(--bs-gutter-x));margin-right:calc(-.5 * var(--bs-gutter-x))}.row>*{box-sizing:border-box;flex-shrink:0;width:100%;max-width:100%;padding-left:calc(var(--bs-gutter-x) * .5);padding-right:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.66666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-right:8.33333333%}.offset-2{margin-right:16.66666667%}.offset-3{margin-right:25%}.offset-4{margin-right:33.33333333%}.offset-5{margin-right:41.66666667%}.offset-6{margin-right:50%}.offset-7{margin-right:58.33333333%}.offset-8{margin-right:66.66666667%}.offset-9{margin-right:75%}.offset-10{margin-right:83.33333333%}.offset-11{margin-right:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.66666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-right:0}.offset-sm-1{margin-right:8.33333333%}.offset-sm-2{margin-right:16.66666667%}.offset-sm-3{margin-right:25%}.offset-sm-4{margin-right:33.33333333%}.offset-sm-5{margin-right:41.66666667%}.offset-sm-6{margin-right:50%}.offset-sm-7{margin-right:58.33333333%}.offset-sm-8{margin-right:66.66666667%}.offset-sm-9{margin-right:75%}.offset-sm-10{margin-right:83.33333333%}.offset-sm-11{margin-right:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.66666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-right:0}.offset-md-1{margin-right:8.33333333%}.offset-md-2{margin-right:16.66666667%}.offset-md-3{margin-right:25%}.offset-md-4{margin-right:33.33333333%}.offset-md-5{margin-right:41.66666667%}.offset-md-6{margin-right:50%}.offset-md-7{margin-right:58.33333333%}.offset-md-8{margin-right:66.66666667%}.offset-md-9{margin-right:75%}.offset-md-10{margin-right:83.33333333%}.offset-md-11{margin-right:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.66666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-right:0}.offset-lg-1{margin-right:8.33333333%}.offset-lg-2{margin-right:16.66666667%}.offset-lg-3{margin-right:25%}.offset-lg-4{margin-right:33.33333333%}.offset-lg-5{margin-right:41.66666667%}.offset-lg-6{margin-right:50%}.offset-lg-7{margin-right:58.33333333%}.offset-lg-8{margin-right:66.66666667%}.offset-lg-9{margin-right:75%}.offset-lg-10{margin-right:83.33333333%}.offset-lg-11{margin-right:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.66666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-right:0}.offset-xl-1{margin-right:8.33333333%}.offset-xl-2{margin-right:16.66666667%}.offset-xl-3{margin-right:25%}.offset-xl-4{margin-right:33.33333333%}.offset-xl-5{margin-right:41.66666667%}.offset-xl-6{margin-right:50%}.offset-xl-7{margin-right:58.33333333%}.offset-xl-8{margin-right:66.66666667%}.offset-xl-9{margin-right:75%}.offset-xl-10{margin-right:83.33333333%}.offset-xl-11{margin-right:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.66666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-right:0}.offset-xxl-1{margin-right:8.33333333%}.offset-xxl-2{margin-right:16.66666667%}.offset-xxl-3{margin-right:25%}.offset-xxl-4{margin-right:33.33333333%}.offset-xxl-5{margin-right:41.66666667%}.offset-xxl-6{margin-right:50%}.offset-xxl-7{margin-right:58.33333333%}.offset-xxl-8{margin-right:66.66666667%}.offset-xxl-9{margin-right:75%}.offset-xxl-10{margin-right:83.33333333%}.offset-xxl-11{margin-right:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-inline-grid{display:inline-grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-left:0!important;margin-right:0!important}.mx-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-3{margin-left:1rem!important;margin-right:1rem!important}.mx-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-5{margin-left:3rem!important;margin-right:3rem!important}.mx-auto{margin-left:auto!important;margin-right:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-left:0!important}.me-1{margin-left:.25rem!important}.me-2{margin-left:.5rem!important}.me-3{margin-left:1rem!important}.me-4{margin-left:1.5rem!important}.me-5{margin-left:3rem!important}.me-auto{margin-left:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-right:0!important}.ms-1{margin-right:.25rem!important}.ms-2{margin-right:.5rem!important}.ms-3{margin-right:1rem!important}.ms-4{margin-right:1.5rem!important}.ms-5{margin-right:3rem!important}.ms-auto{margin-right:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-left:0!important;padding-right:0!important}.px-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-3{padding-left:1rem!important;padding-right:1rem!important}.px-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-5{padding-left:3rem!important;padding-right:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-left:0!important}.pe-1{padding-left:.25rem!important}.pe-2{padding-left:.5rem!important}.pe-3{padding-left:1rem!important}.pe-4{padding-left:1.5rem!important}.pe-5{padding-left:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-right:0!important}.ps-1{padding-right:.25rem!important}.ps-2{padding-right:.5rem!important}.ps-3{padding-right:1rem!important}.ps-4{padding-right:1.5rem!important}.ps-5{padding-right:3rem!important}@media (min-width:576px){.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-inline-grid{display:inline-grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-left:0!important;margin-right:0!important}.mx-sm-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-sm-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-sm-3{margin-left:1rem!important;margin-right:1rem!important}.mx-sm-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-sm-5{margin-left:3rem!important;margin-right:3rem!important}.mx-sm-auto{margin-left:auto!important;margin-right:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-left:0!important}.me-sm-1{margin-left:.25rem!important}.me-sm-2{margin-left:.5rem!important}.me-sm-3{margin-left:1rem!important}.me-sm-4{margin-left:1.5rem!important}.me-sm-5{margin-left:3rem!important}.me-sm-auto{margin-left:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-right:0!important}.ms-sm-1{margin-right:.25rem!important}.ms-sm-2{margin-right:.5rem!important}.ms-sm-3{margin-right:1rem!important}.ms-sm-4{margin-right:1.5rem!important}.ms-sm-5{margin-right:3rem!important}.ms-sm-auto{margin-right:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-left:0!important;padding-right:0!important}.px-sm-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-sm-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-sm-3{padding-left:1rem!important;padding-right:1rem!important}.px-sm-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-sm-5{padding-left:3rem!important;padding-right:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-left:0!important}.pe-sm-1{padding-left:.25rem!important}.pe-sm-2{padding-left:.5rem!important}.pe-sm-3{padding-left:1rem!important}.pe-sm-4{padding-left:1.5rem!important}.pe-sm-5{padding-left:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-right:0!important}.ps-sm-1{padding-right:.25rem!important}.ps-sm-2{padding-right:.5rem!important}.ps-sm-3{padding-right:1rem!important}.ps-sm-4{padding-right:1.5rem!important}.ps-sm-5{padding-right:3rem!important}}@media (min-width:768px){.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-inline-grid{display:inline-grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-left:0!important;margin-right:0!important}.mx-md-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-md-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-md-3{margin-left:1rem!important;margin-right:1rem!important}.mx-md-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-md-5{margin-left:3rem!important;margin-right:3rem!important}.mx-md-auto{margin-left:auto!important;margin-right:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-left:0!important}.me-md-1{margin-left:.25rem!important}.me-md-2{margin-left:.5rem!important}.me-md-3{margin-left:1rem!important}.me-md-4{margin-left:1.5rem!important}.me-md-5{margin-left:3rem!important}.me-md-auto{margin-left:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-right:0!important}.ms-md-1{margin-right:.25rem!important}.ms-md-2{margin-right:.5rem!important}.ms-md-3{margin-right:1rem!important}.ms-md-4{margin-right:1.5rem!important}.ms-md-5{margin-right:3rem!important}.ms-md-auto{margin-right:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-left:0!important;padding-right:0!important}.px-md-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-md-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-md-3{padding-left:1rem!important;padding-right:1rem!important}.px-md-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-md-5{padding-left:3rem!important;padding-right:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-left:0!important}.pe-md-1{padding-left:.25rem!important}.pe-md-2{padding-left:.5rem!important}.pe-md-3{padding-left:1rem!important}.pe-md-4{padding-left:1.5rem!important}.pe-md-5{padding-left:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-right:0!important}.ps-md-1{padding-right:.25rem!important}.ps-md-2{padding-right:.5rem!important}.ps-md-3{padding-right:1rem!important}.ps-md-4{padding-right:1.5rem!important}.ps-md-5{padding-right:3rem!important}}@media (min-width:992px){.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-inline-grid{display:inline-grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-left:0!important;margin-right:0!important}.mx-lg-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-lg-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-lg-3{margin-left:1rem!important;margin-right:1rem!important}.mx-lg-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-lg-5{margin-left:3rem!important;margin-right:3rem!important}.mx-lg-auto{margin-left:auto!important;margin-right:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-left:0!important}.me-lg-1{margin-left:.25rem!important}.me-lg-2{margin-left:.5rem!important}.me-lg-3{margin-left:1rem!important}.me-lg-4{margin-left:1.5rem!important}.me-lg-5{margin-left:3rem!important}.me-lg-auto{margin-left:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-right:0!important}.ms-lg-1{margin-right:.25rem!important}.ms-lg-2{margin-right:.5rem!important}.ms-lg-3{margin-right:1rem!important}.ms-lg-4{margin-right:1.5rem!important}.ms-lg-5{margin-right:3rem!important}.ms-lg-auto{margin-right:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-left:0!important;padding-right:0!important}.px-lg-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-lg-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-lg-3{padding-left:1rem!important;padding-right:1rem!important}.px-lg-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-lg-5{padding-left:3rem!important;padding-right:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-left:0!important}.pe-lg-1{padding-left:.25rem!important}.pe-lg-2{padding-left:.5rem!important}.pe-lg-3{padding-left:1rem!important}.pe-lg-4{padding-left:1.5rem!important}.pe-lg-5{padding-left:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-right:0!important}.ps-lg-1{padding-right:.25rem!important}.ps-lg-2{padding-right:.5rem!important}.ps-lg-3{padding-right:1rem!important}.ps-lg-4{padding-right:1.5rem!important}.ps-lg-5{padding-right:3rem!important}}@media (min-width:1200px){.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-inline-grid{display:inline-grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-left:0!important;margin-right:0!important}.mx-xl-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-xl-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-xl-3{margin-left:1rem!important;margin-right:1rem!important}.mx-xl-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-xl-5{margin-left:3rem!important;margin-right:3rem!important}.mx-xl-auto{margin-left:auto!important;margin-right:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-left:0!important}.me-xl-1{margin-left:.25rem!important}.me-xl-2{margin-left:.5rem!important}.me-xl-3{margin-left:1rem!important}.me-xl-4{margin-left:1.5rem!important}.me-xl-5{margin-left:3rem!important}.me-xl-auto{margin-left:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-right:0!important}.ms-xl-1{margin-right:.25rem!important}.ms-xl-2{margin-right:.5rem!important}.ms-xl-3{margin-right:1rem!important}.ms-xl-4{margin-right:1.5rem!important}.ms-xl-5{margin-right:3rem!important}.ms-xl-auto{margin-right:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-left:0!important;padding-right:0!important}.px-xl-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-xl-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-xl-3{padding-left:1rem!important;padding-right:1rem!important}.px-xl-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-xl-5{padding-left:3rem!important;padding-right:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-left:0!important}.pe-xl-1{padding-left:.25rem!important}.pe-xl-2{padding-left:.5rem!important}.pe-xl-3{padding-left:1rem!important}.pe-xl-4{padding-left:1.5rem!important}.pe-xl-5{padding-left:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-right:0!important}.ps-xl-1{padding-right:.25rem!important}.ps-xl-2{padding-right:.5rem!important}.ps-xl-3{padding-right:1rem!important}.ps-xl-4{padding-right:1.5rem!important}.ps-xl-5{padding-right:3rem!important}}@media (min-width:1400px){.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-inline-grid{display:inline-grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-left:0!important;margin-right:0!important}.mx-xxl-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-xxl-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-xxl-3{margin-left:1rem!important;margin-right:1rem!important}.mx-xxl-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-xxl-5{margin-left:3rem!important;margin-right:3rem!important}.mx-xxl-auto{margin-left:auto!important;margin-right:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-left:0!important}.me-xxl-1{margin-left:.25rem!important}.me-xxl-2{margin-left:.5rem!important}.me-xxl-3{margin-left:1rem!important}.me-xxl-4{margin-left:1.5rem!important}.me-xxl-5{margin-left:3rem!important}.me-xxl-auto{margin-left:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-right:0!important}.ms-xxl-1{margin-right:.25rem!important}.ms-xxl-2{margin-right:.5rem!important}.ms-xxl-3{margin-right:1rem!important}.ms-xxl-4{margin-right:1.5rem!important}.ms-xxl-5{margin-right:3rem!important}.ms-xxl-auto{margin-right:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-left:0!important;padding-right:0!important}.px-xxl-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-xxl-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-xxl-3{padding-left:1rem!important;padding-right:1rem!important}.px-xxl-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-xxl-5{padding-left:3rem!important;padding-right:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-left:0!important}.pe-xxl-1{padding-left:.25rem!important}.pe-xxl-2{padding-left:.5rem!important}.pe-xxl-3{padding-left:1rem!important}.pe-xxl-4{padding-left:1.5rem!important}.pe-xxl-5{padding-left:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-right:0!important}.ps-xxl-1{padding-right:.25rem!important}.ps-xxl-2{padding-right:.5rem!important}.ps-xxl-3{padding-right:1rem!important}.ps-xxl-4{padding-right:1.5rem!important}.ps-xxl-5{padding-right:3rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-inline-grid{display:inline-grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} -/*# sourceMappingURL=bootstrap-grid.rtl.min.css.map */ \ No newline at end of file diff --git a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map b/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map deleted file mode 100644 index 1c926af..0000000 --- a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.rtl.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../scss/mixins/_banner.scss","../../scss/_containers.scss","dist/css/bootstrap-grid.rtl.css","../../scss/mixins/_container.scss","../../scss/mixins/_breakpoints.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_utilities.scss","../../scss/utilities/_api.scss"],"names":[],"mappings":"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","sourcesContent":["@mixin bsBanner($file) {\n /*!\n * Bootstrap #{$file} v5.3.3 (https://getbootstrap.com/)\n * Copyright 2011-2024 The Bootstrap Authors\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n}\n","// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n@if $enable-container-classes {\n // Single container class with breakpoint max-widths\n .container,\n // 100% wide container at all breakpoints\n .container-fluid {\n @include make-container();\n }\n\n // Responsive containers that are 100% wide until a breakpoint\n @each $breakpoint, $container-max-width in $container-max-widths {\n .container-#{$breakpoint} {\n @extend .container-fluid;\n }\n\n @include media-breakpoint-up($breakpoint, $grid-breakpoints) {\n %responsive-container-#{$breakpoint} {\n max-width: $container-max-width;\n }\n\n // Extend each breakpoint which is smaller or equal to the current breakpoint\n $extend-breakpoint: true;\n\n @each $name, $width in $grid-breakpoints {\n @if ($extend-breakpoint) {\n .container#{breakpoint-infix($name, $grid-breakpoints)} {\n @extend %responsive-container-#{$breakpoint};\n }\n\n // Once the current breakpoint is reached, stop extending\n @if ($breakpoint == $name) {\n $extend-breakpoint: false;\n }\n }\n }\n }\n }\n}\n","/*!\n * Bootstrap Grid v5.3.3 (https://getbootstrap.com/)\n * Copyright 2011-2024 The Bootstrap Authors\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n.container,\n.container-fluid,\n.container-xxl,\n.container-xl,\n.container-lg,\n.container-md,\n.container-sm {\n --bs-gutter-x: 1.5rem;\n --bs-gutter-y: 0;\n width: 100%;\n padding-left: calc(var(--bs-gutter-x) * 0.5);\n padding-right: calc(var(--bs-gutter-x) * 0.5);\n margin-left: auto;\n margin-right: auto;\n}\n\n@media (min-width: 576px) {\n .container-sm, .container {\n max-width: 540px;\n }\n}\n@media (min-width: 768px) {\n .container-md, .container-sm, .container {\n max-width: 720px;\n }\n}\n@media (min-width: 992px) {\n .container-lg, .container-md, .container-sm, .container {\n max-width: 960px;\n }\n}\n@media (min-width: 1200px) {\n .container-xl, .container-lg, .container-md, .container-sm, .container {\n max-width: 1140px;\n }\n}\n@media (min-width: 1400px) {\n .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container {\n max-width: 1320px;\n }\n}\n:root {\n --bs-breakpoint-xs: 0;\n --bs-breakpoint-sm: 576px;\n --bs-breakpoint-md: 768px;\n --bs-breakpoint-lg: 992px;\n --bs-breakpoint-xl: 1200px;\n --bs-breakpoint-xxl: 1400px;\n}\n\n.row {\n --bs-gutter-x: 1.5rem;\n --bs-gutter-y: 0;\n display: flex;\n flex-wrap: wrap;\n margin-top: calc(-1 * var(--bs-gutter-y));\n margin-left: calc(-0.5 * var(--bs-gutter-x));\n margin-right: calc(-0.5 * var(--bs-gutter-x));\n}\n.row > * {\n box-sizing: border-box;\n flex-shrink: 0;\n width: 100%;\n max-width: 100%;\n padding-left: calc(var(--bs-gutter-x) * 0.5);\n padding-right: calc(var(--bs-gutter-x) * 0.5);\n margin-top: var(--bs-gutter-y);\n}\n\n.col {\n flex: 1 0 0%;\n}\n\n.row-cols-auto > * {\n flex: 0 0 auto;\n width: auto;\n}\n\n.row-cols-1 > * {\n flex: 0 0 auto;\n width: 100%;\n}\n\n.row-cols-2 > * {\n flex: 0 0 auto;\n width: 50%;\n}\n\n.row-cols-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n}\n\n.row-cols-4 > * {\n flex: 0 0 auto;\n width: 25%;\n}\n\n.row-cols-5 > * {\n flex: 0 0 auto;\n width: 20%;\n}\n\n.row-cols-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n}\n\n.col-auto {\n flex: 0 0 auto;\n width: auto;\n}\n\n.col-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n}\n\n.col-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n}\n\n.col-3 {\n flex: 0 0 auto;\n width: 25%;\n}\n\n.col-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n}\n\n.col-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n}\n\n.col-6 {\n flex: 0 0 auto;\n width: 50%;\n}\n\n.col-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n}\n\n.col-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n}\n\n.col-9 {\n flex: 0 0 auto;\n width: 75%;\n}\n\n.col-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n}\n\n.col-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n}\n\n.col-12 {\n flex: 0 0 auto;\n width: 100%;\n}\n\n.offset-1 {\n margin-right: 8.33333333%;\n}\n\n.offset-2 {\n margin-right: 16.66666667%;\n}\n\n.offset-3 {\n margin-right: 25%;\n}\n\n.offset-4 {\n margin-right: 33.33333333%;\n}\n\n.offset-5 {\n margin-right: 41.66666667%;\n}\n\n.offset-6 {\n margin-right: 50%;\n}\n\n.offset-7 {\n margin-right: 58.33333333%;\n}\n\n.offset-8 {\n margin-right: 66.66666667%;\n}\n\n.offset-9 {\n margin-right: 75%;\n}\n\n.offset-10 {\n margin-right: 83.33333333%;\n}\n\n.offset-11 {\n margin-right: 91.66666667%;\n}\n\n.g-0,\n.gx-0 {\n --bs-gutter-x: 0;\n}\n\n.g-0,\n.gy-0 {\n --bs-gutter-y: 0;\n}\n\n.g-1,\n.gx-1 {\n --bs-gutter-x: 0.25rem;\n}\n\n.g-1,\n.gy-1 {\n --bs-gutter-y: 0.25rem;\n}\n\n.g-2,\n.gx-2 {\n --bs-gutter-x: 0.5rem;\n}\n\n.g-2,\n.gy-2 {\n --bs-gutter-y: 0.5rem;\n}\n\n.g-3,\n.gx-3 {\n --bs-gutter-x: 1rem;\n}\n\n.g-3,\n.gy-3 {\n --bs-gutter-y: 1rem;\n}\n\n.g-4,\n.gx-4 {\n --bs-gutter-x: 1.5rem;\n}\n\n.g-4,\n.gy-4 {\n --bs-gutter-y: 1.5rem;\n}\n\n.g-5,\n.gx-5 {\n --bs-gutter-x: 3rem;\n}\n\n.g-5,\n.gy-5 {\n --bs-gutter-y: 3rem;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n flex: 1 0 0%;\n }\n .row-cols-sm-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-sm-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-sm-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-sm-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-sm-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-sm-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-sm-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-sm-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-sm-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-sm-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-sm-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-sm-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-sm-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-sm-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-sm-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-sm-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-sm-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-sm-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-sm-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-sm-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-sm-0 {\n margin-right: 0;\n }\n .offset-sm-1 {\n margin-right: 8.33333333%;\n }\n .offset-sm-2 {\n margin-right: 16.66666667%;\n }\n .offset-sm-3 {\n margin-right: 25%;\n }\n .offset-sm-4 {\n margin-right: 33.33333333%;\n }\n .offset-sm-5 {\n margin-right: 41.66666667%;\n }\n .offset-sm-6 {\n margin-right: 50%;\n }\n .offset-sm-7 {\n margin-right: 58.33333333%;\n }\n .offset-sm-8 {\n margin-right: 66.66666667%;\n }\n .offset-sm-9 {\n margin-right: 75%;\n }\n .offset-sm-10 {\n margin-right: 83.33333333%;\n }\n .offset-sm-11 {\n margin-right: 91.66666667%;\n }\n .g-sm-0,\n .gx-sm-0 {\n --bs-gutter-x: 0;\n }\n .g-sm-0,\n .gy-sm-0 {\n --bs-gutter-y: 0;\n }\n .g-sm-1,\n .gx-sm-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-sm-1,\n .gy-sm-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-sm-2,\n .gx-sm-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-sm-2,\n .gy-sm-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-sm-3,\n .gx-sm-3 {\n --bs-gutter-x: 1rem;\n }\n .g-sm-3,\n .gy-sm-3 {\n --bs-gutter-y: 1rem;\n }\n .g-sm-4,\n .gx-sm-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-sm-4,\n .gy-sm-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-sm-5,\n .gx-sm-5 {\n --bs-gutter-x: 3rem;\n }\n .g-sm-5,\n .gy-sm-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 768px) {\n .col-md {\n flex: 1 0 0%;\n }\n .row-cols-md-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-md-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-md-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-md-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-md-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-md-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-md-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-md-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-md-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-md-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-md-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-md-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-md-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-md-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-md-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-md-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-md-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-md-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-md-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-md-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-md-0 {\n margin-right: 0;\n }\n .offset-md-1 {\n margin-right: 8.33333333%;\n }\n .offset-md-2 {\n margin-right: 16.66666667%;\n }\n .offset-md-3 {\n margin-right: 25%;\n }\n .offset-md-4 {\n margin-right: 33.33333333%;\n }\n .offset-md-5 {\n margin-right: 41.66666667%;\n }\n .offset-md-6 {\n margin-right: 50%;\n }\n .offset-md-7 {\n margin-right: 58.33333333%;\n }\n .offset-md-8 {\n margin-right: 66.66666667%;\n }\n .offset-md-9 {\n margin-right: 75%;\n }\n .offset-md-10 {\n margin-right: 83.33333333%;\n }\n .offset-md-11 {\n margin-right: 91.66666667%;\n }\n .g-md-0,\n .gx-md-0 {\n --bs-gutter-x: 0;\n }\n .g-md-0,\n .gy-md-0 {\n --bs-gutter-y: 0;\n }\n .g-md-1,\n .gx-md-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-md-1,\n .gy-md-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-md-2,\n .gx-md-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-md-2,\n .gy-md-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-md-3,\n .gx-md-3 {\n --bs-gutter-x: 1rem;\n }\n .g-md-3,\n .gy-md-3 {\n --bs-gutter-y: 1rem;\n }\n .g-md-4,\n .gx-md-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-md-4,\n .gy-md-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-md-5,\n .gx-md-5 {\n --bs-gutter-x: 3rem;\n }\n .g-md-5,\n .gy-md-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 992px) {\n .col-lg {\n flex: 1 0 0%;\n }\n .row-cols-lg-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-lg-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-lg-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-lg-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-lg-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-lg-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-lg-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-lg-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-lg-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-lg-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-lg-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-lg-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-lg-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-lg-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-lg-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-lg-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-lg-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-lg-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-lg-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-lg-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-lg-0 {\n margin-right: 0;\n }\n .offset-lg-1 {\n margin-right: 8.33333333%;\n }\n .offset-lg-2 {\n margin-right: 16.66666667%;\n }\n .offset-lg-3 {\n margin-right: 25%;\n }\n .offset-lg-4 {\n margin-right: 33.33333333%;\n }\n .offset-lg-5 {\n margin-right: 41.66666667%;\n }\n .offset-lg-6 {\n margin-right: 50%;\n }\n .offset-lg-7 {\n margin-right: 58.33333333%;\n }\n .offset-lg-8 {\n margin-right: 66.66666667%;\n }\n .offset-lg-9 {\n margin-right: 75%;\n }\n .offset-lg-10 {\n margin-right: 83.33333333%;\n }\n .offset-lg-11 {\n margin-right: 91.66666667%;\n }\n .g-lg-0,\n .gx-lg-0 {\n --bs-gutter-x: 0;\n }\n .g-lg-0,\n .gy-lg-0 {\n --bs-gutter-y: 0;\n }\n .g-lg-1,\n .gx-lg-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-lg-1,\n .gy-lg-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-lg-2,\n .gx-lg-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-lg-2,\n .gy-lg-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-lg-3,\n .gx-lg-3 {\n --bs-gutter-x: 1rem;\n }\n .g-lg-3,\n .gy-lg-3 {\n --bs-gutter-y: 1rem;\n }\n .g-lg-4,\n .gx-lg-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-lg-4,\n .gy-lg-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-lg-5,\n .gx-lg-5 {\n --bs-gutter-x: 3rem;\n }\n .g-lg-5,\n .gy-lg-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 1200px) {\n .col-xl {\n flex: 1 0 0%;\n }\n .row-cols-xl-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-xl-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-xl-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-xl-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-xl-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-xl-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-xl-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xl-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-xl-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-xl-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xl-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-xl-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-xl-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-xl-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-xl-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-xl-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-xl-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-xl-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-xl-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-xl-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-xl-0 {\n margin-right: 0;\n }\n .offset-xl-1 {\n margin-right: 8.33333333%;\n }\n .offset-xl-2 {\n margin-right: 16.66666667%;\n }\n .offset-xl-3 {\n margin-right: 25%;\n }\n .offset-xl-4 {\n margin-right: 33.33333333%;\n }\n .offset-xl-5 {\n margin-right: 41.66666667%;\n }\n .offset-xl-6 {\n margin-right: 50%;\n }\n .offset-xl-7 {\n margin-right: 58.33333333%;\n }\n .offset-xl-8 {\n margin-right: 66.66666667%;\n }\n .offset-xl-9 {\n margin-right: 75%;\n }\n .offset-xl-10 {\n margin-right: 83.33333333%;\n }\n .offset-xl-11 {\n margin-right: 91.66666667%;\n }\n .g-xl-0,\n .gx-xl-0 {\n --bs-gutter-x: 0;\n }\n .g-xl-0,\n .gy-xl-0 {\n --bs-gutter-y: 0;\n }\n .g-xl-1,\n .gx-xl-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-xl-1,\n .gy-xl-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-xl-2,\n .gx-xl-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-xl-2,\n .gy-xl-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-xl-3,\n .gx-xl-3 {\n --bs-gutter-x: 1rem;\n }\n .g-xl-3,\n .gy-xl-3 {\n --bs-gutter-y: 1rem;\n }\n .g-xl-4,\n .gx-xl-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-xl-4,\n .gy-xl-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-xl-5,\n .gx-xl-5 {\n --bs-gutter-x: 3rem;\n }\n .g-xl-5,\n .gy-xl-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 1400px) {\n .col-xxl {\n flex: 1 0 0%;\n }\n .row-cols-xxl-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-xxl-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-xxl-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-xxl-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-xxl-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-xxl-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-xxl-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xxl-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-xxl-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-xxl-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xxl-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-xxl-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-xxl-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-xxl-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-xxl-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-xxl-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-xxl-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-xxl-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-xxl-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-xxl-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-xxl-0 {\n margin-right: 0;\n }\n .offset-xxl-1 {\n margin-right: 8.33333333%;\n }\n .offset-xxl-2 {\n margin-right: 16.66666667%;\n }\n .offset-xxl-3 {\n margin-right: 25%;\n }\n .offset-xxl-4 {\n margin-right: 33.33333333%;\n }\n .offset-xxl-5 {\n margin-right: 41.66666667%;\n }\n .offset-xxl-6 {\n margin-right: 50%;\n }\n .offset-xxl-7 {\n margin-right: 58.33333333%;\n }\n .offset-xxl-8 {\n margin-right: 66.66666667%;\n }\n .offset-xxl-9 {\n margin-right: 75%;\n }\n .offset-xxl-10 {\n margin-right: 83.33333333%;\n }\n .offset-xxl-11 {\n margin-right: 91.66666667%;\n }\n .g-xxl-0,\n .gx-xxl-0 {\n --bs-gutter-x: 0;\n }\n .g-xxl-0,\n .gy-xxl-0 {\n --bs-gutter-y: 0;\n }\n .g-xxl-1,\n .gx-xxl-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-xxl-1,\n .gy-xxl-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-xxl-2,\n .gx-xxl-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-xxl-2,\n .gy-xxl-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-xxl-3,\n .gx-xxl-3 {\n --bs-gutter-x: 1rem;\n }\n .g-xxl-3,\n .gy-xxl-3 {\n --bs-gutter-y: 1rem;\n }\n .g-xxl-4,\n .gx-xxl-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-xxl-4,\n .gy-xxl-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-xxl-5,\n .gx-xxl-5 {\n --bs-gutter-x: 3rem;\n }\n .g-xxl-5,\n .gy-xxl-5 {\n --bs-gutter-y: 3rem;\n }\n}\n.d-inline {\n display: inline !important;\n}\n\n.d-inline-block {\n display: inline-block !important;\n}\n\n.d-block {\n display: block !important;\n}\n\n.d-grid {\n display: grid !important;\n}\n\n.d-inline-grid {\n display: inline-grid !important;\n}\n\n.d-table {\n display: table !important;\n}\n\n.d-table-row {\n display: table-row !important;\n}\n\n.d-table-cell {\n display: table-cell !important;\n}\n\n.d-flex {\n display: flex !important;\n}\n\n.d-inline-flex {\n display: inline-flex !important;\n}\n\n.d-none {\n display: none !important;\n}\n\n.flex-fill {\n flex: 1 1 auto !important;\n}\n\n.flex-row {\n flex-direction: row !important;\n}\n\n.flex-column {\n flex-direction: column !important;\n}\n\n.flex-row-reverse {\n flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n flex-direction: column-reverse !important;\n}\n\n.flex-grow-0 {\n flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n flex-shrink: 1 !important;\n}\n\n.flex-wrap {\n flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n}\n\n.justify-content-start {\n justify-content: flex-start !important;\n}\n\n.justify-content-end {\n justify-content: flex-end !important;\n}\n\n.justify-content-center {\n justify-content: center !important;\n}\n\n.justify-content-between {\n justify-content: space-between !important;\n}\n\n.justify-content-around {\n justify-content: space-around !important;\n}\n\n.justify-content-evenly {\n justify-content: space-evenly !important;\n}\n\n.align-items-start {\n align-items: flex-start !important;\n}\n\n.align-items-end {\n align-items: flex-end !important;\n}\n\n.align-items-center {\n align-items: center !important;\n}\n\n.align-items-baseline {\n align-items: baseline !important;\n}\n\n.align-items-stretch {\n align-items: stretch !important;\n}\n\n.align-content-start {\n align-content: flex-start !important;\n}\n\n.align-content-end {\n align-content: flex-end !important;\n}\n\n.align-content-center {\n align-content: center !important;\n}\n\n.align-content-between {\n align-content: space-between !important;\n}\n\n.align-content-around {\n align-content: space-around !important;\n}\n\n.align-content-stretch {\n align-content: stretch !important;\n}\n\n.align-self-auto {\n align-self: auto !important;\n}\n\n.align-self-start {\n align-self: flex-start !important;\n}\n\n.align-self-end {\n align-self: flex-end !important;\n}\n\n.align-self-center {\n align-self: center !important;\n}\n\n.align-self-baseline {\n align-self: baseline !important;\n}\n\n.align-self-stretch {\n align-self: stretch !important;\n}\n\n.order-first {\n order: -1 !important;\n}\n\n.order-0 {\n order: 0 !important;\n}\n\n.order-1 {\n order: 1 !important;\n}\n\n.order-2 {\n order: 2 !important;\n}\n\n.order-3 {\n order: 3 !important;\n}\n\n.order-4 {\n order: 4 !important;\n}\n\n.order-5 {\n order: 5 !important;\n}\n\n.order-last {\n order: 6 !important;\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mx-0 {\n margin-left: 0 !important;\n margin-right: 0 !important;\n}\n\n.mx-1 {\n margin-left: 0.25rem !important;\n margin-right: 0.25rem !important;\n}\n\n.mx-2 {\n margin-left: 0.5rem !important;\n margin-right: 0.5rem !important;\n}\n\n.mx-3 {\n margin-left: 1rem !important;\n margin-right: 1rem !important;\n}\n\n.mx-4 {\n margin-left: 1.5rem !important;\n margin-right: 1.5rem !important;\n}\n\n.mx-5 {\n margin-left: 3rem !important;\n margin-right: 3rem !important;\n}\n\n.mx-auto {\n margin-left: auto !important;\n margin-right: auto !important;\n}\n\n.my-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n.my-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n}\n\n.my-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n}\n\n.my-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n}\n\n.my-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n}\n\n.my-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n}\n\n.my-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n}\n\n.mt-0 {\n margin-top: 0 !important;\n}\n\n.mt-1 {\n margin-top: 0.25rem !important;\n}\n\n.mt-2 {\n margin-top: 0.5rem !important;\n}\n\n.mt-3 {\n margin-top: 1rem !important;\n}\n\n.mt-4 {\n margin-top: 1.5rem !important;\n}\n\n.mt-5 {\n margin-top: 3rem !important;\n}\n\n.mt-auto {\n margin-top: auto !important;\n}\n\n.me-0 {\n margin-left: 0 !important;\n}\n\n.me-1 {\n margin-left: 0.25rem !important;\n}\n\n.me-2 {\n margin-left: 0.5rem !important;\n}\n\n.me-3 {\n margin-left: 1rem !important;\n}\n\n.me-4 {\n margin-left: 1.5rem !important;\n}\n\n.me-5 {\n margin-left: 3rem !important;\n}\n\n.me-auto {\n margin-left: auto !important;\n}\n\n.mb-0 {\n margin-bottom: 0 !important;\n}\n\n.mb-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.mb-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.mb-3 {\n margin-bottom: 1rem !important;\n}\n\n.mb-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.mb-5 {\n margin-bottom: 3rem !important;\n}\n\n.mb-auto {\n margin-bottom: auto !important;\n}\n\n.ms-0 {\n margin-right: 0 !important;\n}\n\n.ms-1 {\n margin-right: 0.25rem !important;\n}\n\n.ms-2 {\n margin-right: 0.5rem !important;\n}\n\n.ms-3 {\n margin-right: 1rem !important;\n}\n\n.ms-4 {\n margin-right: 1.5rem !important;\n}\n\n.ms-5 {\n margin-right: 3rem !important;\n}\n\n.ms-auto {\n margin-right: auto !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.px-0 {\n padding-left: 0 !important;\n padding-right: 0 !important;\n}\n\n.px-1 {\n padding-left: 0.25rem !important;\n padding-right: 0.25rem !important;\n}\n\n.px-2 {\n padding-left: 0.5rem !important;\n padding-right: 0.5rem !important;\n}\n\n.px-3 {\n padding-left: 1rem !important;\n padding-right: 1rem !important;\n}\n\n.px-4 {\n padding-left: 1.5rem !important;\n padding-right: 1.5rem !important;\n}\n\n.px-5 {\n padding-left: 3rem !important;\n padding-right: 3rem !important;\n}\n\n.py-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n}\n\n.py-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n}\n\n.py-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n}\n\n.py-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n}\n\n.py-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n}\n\n.py-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n}\n\n.pt-0 {\n padding-top: 0 !important;\n}\n\n.pt-1 {\n padding-top: 0.25rem !important;\n}\n\n.pt-2 {\n padding-top: 0.5rem !important;\n}\n\n.pt-3 {\n padding-top: 1rem !important;\n}\n\n.pt-4 {\n padding-top: 1.5rem !important;\n}\n\n.pt-5 {\n padding-top: 3rem !important;\n}\n\n.pe-0 {\n padding-left: 0 !important;\n}\n\n.pe-1 {\n padding-left: 0.25rem !important;\n}\n\n.pe-2 {\n padding-left: 0.5rem !important;\n}\n\n.pe-3 {\n padding-left: 1rem !important;\n}\n\n.pe-4 {\n padding-left: 1.5rem !important;\n}\n\n.pe-5 {\n padding-left: 3rem !important;\n}\n\n.pb-0 {\n padding-bottom: 0 !important;\n}\n\n.pb-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pb-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pb-3 {\n padding-bottom: 1rem !important;\n}\n\n.pb-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pb-5 {\n padding-bottom: 3rem !important;\n}\n\n.ps-0 {\n padding-right: 0 !important;\n}\n\n.ps-1 {\n padding-right: 0.25rem !important;\n}\n\n.ps-2 {\n padding-right: 0.5rem !important;\n}\n\n.ps-3 {\n padding-right: 1rem !important;\n}\n\n.ps-4 {\n padding-right: 1.5rem !important;\n}\n\n.ps-5 {\n padding-right: 3rem !important;\n}\n\n@media (min-width: 576px) {\n .d-sm-inline {\n display: inline !important;\n }\n .d-sm-inline-block {\n display: inline-block !important;\n }\n .d-sm-block {\n display: block !important;\n }\n .d-sm-grid {\n display: grid !important;\n }\n .d-sm-inline-grid {\n display: inline-grid !important;\n }\n .d-sm-table {\n display: table !important;\n }\n .d-sm-table-row {\n display: table-row !important;\n }\n .d-sm-table-cell {\n display: table-cell !important;\n }\n .d-sm-flex {\n display: flex !important;\n }\n .d-sm-inline-flex {\n display: inline-flex !important;\n }\n .d-sm-none {\n display: none !important;\n }\n .flex-sm-fill {\n flex: 1 1 auto !important;\n }\n .flex-sm-row {\n flex-direction: row !important;\n }\n .flex-sm-column {\n flex-direction: column !important;\n }\n .flex-sm-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-sm-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-sm-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-sm-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-sm-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-sm-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-sm-wrap {\n flex-wrap: wrap !important;\n }\n .flex-sm-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-sm-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-sm-start {\n justify-content: flex-start !important;\n }\n .justify-content-sm-end {\n justify-content: flex-end !important;\n }\n .justify-content-sm-center {\n justify-content: center !important;\n }\n .justify-content-sm-between {\n justify-content: space-between !important;\n }\n .justify-content-sm-around {\n justify-content: space-around !important;\n }\n .justify-content-sm-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-sm-start {\n align-items: flex-start !important;\n }\n .align-items-sm-end {\n align-items: flex-end !important;\n }\n .align-items-sm-center {\n align-items: center !important;\n }\n .align-items-sm-baseline {\n align-items: baseline !important;\n }\n .align-items-sm-stretch {\n align-items: stretch !important;\n }\n .align-content-sm-start {\n align-content: flex-start !important;\n }\n .align-content-sm-end {\n align-content: flex-end !important;\n }\n .align-content-sm-center {\n align-content: center !important;\n }\n .align-content-sm-between {\n align-content: space-between !important;\n }\n .align-content-sm-around {\n align-content: space-around !important;\n }\n .align-content-sm-stretch {\n align-content: stretch !important;\n }\n .align-self-sm-auto {\n align-self: auto !important;\n }\n .align-self-sm-start {\n align-self: flex-start !important;\n }\n .align-self-sm-end {\n align-self: flex-end !important;\n }\n .align-self-sm-center {\n align-self: center !important;\n }\n .align-self-sm-baseline {\n align-self: baseline !important;\n }\n .align-self-sm-stretch {\n align-self: stretch !important;\n }\n .order-sm-first {\n order: -1 !important;\n }\n .order-sm-0 {\n order: 0 !important;\n }\n .order-sm-1 {\n order: 1 !important;\n }\n .order-sm-2 {\n order: 2 !important;\n }\n .order-sm-3 {\n order: 3 !important;\n }\n .order-sm-4 {\n order: 4 !important;\n }\n .order-sm-5 {\n order: 5 !important;\n }\n .order-sm-last {\n order: 6 !important;\n }\n .m-sm-0 {\n margin: 0 !important;\n }\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n .m-sm-3 {\n margin: 1rem !important;\n }\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n .m-sm-5 {\n margin: 3rem !important;\n }\n .m-sm-auto {\n margin: auto !important;\n }\n .mx-sm-0 {\n margin-left: 0 !important;\n margin-right: 0 !important;\n }\n .mx-sm-1 {\n margin-left: 0.25rem !important;\n margin-right: 0.25rem !important;\n }\n .mx-sm-2 {\n margin-left: 0.5rem !important;\n margin-right: 0.5rem !important;\n }\n .mx-sm-3 {\n margin-left: 1rem !important;\n margin-right: 1rem !important;\n }\n .mx-sm-4 {\n margin-left: 1.5rem !important;\n margin-right: 1.5rem !important;\n }\n .mx-sm-5 {\n margin-left: 3rem !important;\n margin-right: 3rem !important;\n }\n .mx-sm-auto {\n margin-left: auto !important;\n margin-right: auto !important;\n }\n .my-sm-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-sm-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-sm-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-sm-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-sm-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-sm-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-sm-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-sm-0 {\n margin-top: 0 !important;\n }\n .mt-sm-1 {\n margin-top: 0.25rem !important;\n }\n .mt-sm-2 {\n margin-top: 0.5rem !important;\n }\n .mt-sm-3 {\n margin-top: 1rem !important;\n }\n .mt-sm-4 {\n margin-top: 1.5rem !important;\n }\n .mt-sm-5 {\n margin-top: 3rem !important;\n }\n .mt-sm-auto {\n margin-top: auto !important;\n }\n .me-sm-0 {\n margin-left: 0 !important;\n }\n .me-sm-1 {\n margin-left: 0.25rem !important;\n }\n .me-sm-2 {\n margin-left: 0.5rem !important;\n }\n .me-sm-3 {\n margin-left: 1rem !important;\n }\n .me-sm-4 {\n margin-left: 1.5rem !important;\n }\n .me-sm-5 {\n margin-left: 3rem !important;\n }\n .me-sm-auto {\n margin-left: auto !important;\n }\n .mb-sm-0 {\n margin-bottom: 0 !important;\n }\n .mb-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-sm-3 {\n margin-bottom: 1rem !important;\n }\n .mb-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-sm-5 {\n margin-bottom: 3rem !important;\n }\n .mb-sm-auto {\n margin-bottom: auto !important;\n }\n .ms-sm-0 {\n margin-right: 0 !important;\n }\n .ms-sm-1 {\n margin-right: 0.25rem !important;\n }\n .ms-sm-2 {\n margin-right: 0.5rem !important;\n }\n .ms-sm-3 {\n margin-right: 1rem !important;\n }\n .ms-sm-4 {\n margin-right: 1.5rem !important;\n }\n .ms-sm-5 {\n margin-right: 3rem !important;\n }\n .ms-sm-auto {\n margin-right: auto !important;\n }\n .p-sm-0 {\n padding: 0 !important;\n }\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n .p-sm-3 {\n padding: 1rem !important;\n }\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n .p-sm-5 {\n padding: 3rem !important;\n }\n .px-sm-0 {\n padding-left: 0 !important;\n padding-right: 0 !important;\n }\n .px-sm-1 {\n padding-left: 0.25rem !important;\n padding-right: 0.25rem !important;\n }\n .px-sm-2 {\n padding-left: 0.5rem !important;\n padding-right: 0.5rem !important;\n }\n .px-sm-3 {\n padding-left: 1rem !important;\n padding-right: 1rem !important;\n }\n .px-sm-4 {\n padding-left: 1.5rem !important;\n padding-right: 1.5rem !important;\n }\n .px-sm-5 {\n padding-left: 3rem !important;\n padding-right: 3rem !important;\n }\n .py-sm-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-sm-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-sm-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-sm-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-sm-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-sm-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-sm-0 {\n padding-top: 0 !important;\n }\n .pt-sm-1 {\n padding-top: 0.25rem !important;\n }\n .pt-sm-2 {\n padding-top: 0.5rem !important;\n }\n .pt-sm-3 {\n padding-top: 1rem !important;\n }\n .pt-sm-4 {\n padding-top: 1.5rem !important;\n }\n .pt-sm-5 {\n padding-top: 3rem !important;\n }\n .pe-sm-0 {\n padding-left: 0 !important;\n }\n .pe-sm-1 {\n padding-left: 0.25rem !important;\n }\n .pe-sm-2 {\n padding-left: 0.5rem !important;\n }\n .pe-sm-3 {\n padding-left: 1rem !important;\n }\n .pe-sm-4 {\n padding-left: 1.5rem !important;\n }\n .pe-sm-5 {\n padding-left: 3rem !important;\n }\n .pb-sm-0 {\n padding-bottom: 0 !important;\n }\n .pb-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-sm-3 {\n padding-bottom: 1rem !important;\n }\n .pb-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-sm-5 {\n padding-bottom: 3rem !important;\n }\n .ps-sm-0 {\n padding-right: 0 !important;\n }\n .ps-sm-1 {\n padding-right: 0.25rem !important;\n }\n .ps-sm-2 {\n padding-right: 0.5rem !important;\n }\n .ps-sm-3 {\n padding-right: 1rem !important;\n }\n .ps-sm-4 {\n padding-right: 1.5rem !important;\n }\n .ps-sm-5 {\n padding-right: 3rem !important;\n }\n}\n@media (min-width: 768px) {\n .d-md-inline {\n display: inline !important;\n }\n .d-md-inline-block {\n display: inline-block !important;\n }\n .d-md-block {\n display: block !important;\n }\n .d-md-grid {\n display: grid !important;\n }\n .d-md-inline-grid {\n display: inline-grid !important;\n }\n .d-md-table {\n display: table !important;\n }\n .d-md-table-row {\n display: table-row !important;\n }\n .d-md-table-cell {\n display: table-cell !important;\n }\n .d-md-flex {\n display: flex !important;\n }\n .d-md-inline-flex {\n display: inline-flex !important;\n }\n .d-md-none {\n display: none !important;\n }\n .flex-md-fill {\n flex: 1 1 auto !important;\n }\n .flex-md-row {\n flex-direction: row !important;\n }\n .flex-md-column {\n flex-direction: column !important;\n }\n .flex-md-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-md-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-md-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-md-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-md-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-md-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-md-wrap {\n flex-wrap: wrap !important;\n }\n .flex-md-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-md-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-md-start {\n justify-content: flex-start !important;\n }\n .justify-content-md-end {\n justify-content: flex-end !important;\n }\n .justify-content-md-center {\n justify-content: center !important;\n }\n .justify-content-md-between {\n justify-content: space-between !important;\n }\n .justify-content-md-around {\n justify-content: space-around !important;\n }\n .justify-content-md-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-md-start {\n align-items: flex-start !important;\n }\n .align-items-md-end {\n align-items: flex-end !important;\n }\n .align-items-md-center {\n align-items: center !important;\n }\n .align-items-md-baseline {\n align-items: baseline !important;\n }\n .align-items-md-stretch {\n align-items: stretch !important;\n }\n .align-content-md-start {\n align-content: flex-start !important;\n }\n .align-content-md-end {\n align-content: flex-end !important;\n }\n .align-content-md-center {\n align-content: center !important;\n }\n .align-content-md-between {\n align-content: space-between !important;\n }\n .align-content-md-around {\n align-content: space-around !important;\n }\n .align-content-md-stretch {\n align-content: stretch !important;\n }\n .align-self-md-auto {\n align-self: auto !important;\n }\n .align-self-md-start {\n align-self: flex-start !important;\n }\n .align-self-md-end {\n align-self: flex-end !important;\n }\n .align-self-md-center {\n align-self: center !important;\n }\n .align-self-md-baseline {\n align-self: baseline !important;\n }\n .align-self-md-stretch {\n align-self: stretch !important;\n }\n .order-md-first {\n order: -1 !important;\n }\n .order-md-0 {\n order: 0 !important;\n }\n .order-md-1 {\n order: 1 !important;\n }\n .order-md-2 {\n order: 2 !important;\n }\n .order-md-3 {\n order: 3 !important;\n }\n .order-md-4 {\n order: 4 !important;\n }\n .order-md-5 {\n order: 5 !important;\n }\n .order-md-last {\n order: 6 !important;\n }\n .m-md-0 {\n margin: 0 !important;\n }\n .m-md-1 {\n margin: 0.25rem !important;\n }\n .m-md-2 {\n margin: 0.5rem !important;\n }\n .m-md-3 {\n margin: 1rem !important;\n }\n .m-md-4 {\n margin: 1.5rem !important;\n }\n .m-md-5 {\n margin: 3rem !important;\n }\n .m-md-auto {\n margin: auto !important;\n }\n .mx-md-0 {\n margin-left: 0 !important;\n margin-right: 0 !important;\n }\n .mx-md-1 {\n margin-left: 0.25rem !important;\n margin-right: 0.25rem !important;\n }\n .mx-md-2 {\n margin-left: 0.5rem !important;\n margin-right: 0.5rem !important;\n }\n .mx-md-3 {\n margin-left: 1rem !important;\n margin-right: 1rem !important;\n }\n .mx-md-4 {\n margin-left: 1.5rem !important;\n margin-right: 1.5rem !important;\n }\n .mx-md-5 {\n margin-left: 3rem !important;\n margin-right: 3rem !important;\n }\n .mx-md-auto {\n margin-left: auto !important;\n margin-right: auto !important;\n }\n .my-md-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-md-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-md-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-md-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-md-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-md-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-md-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-md-0 {\n margin-top: 0 !important;\n }\n .mt-md-1 {\n margin-top: 0.25rem !important;\n }\n .mt-md-2 {\n margin-top: 0.5rem !important;\n }\n .mt-md-3 {\n margin-top: 1rem !important;\n }\n .mt-md-4 {\n margin-top: 1.5rem !important;\n }\n .mt-md-5 {\n margin-top: 3rem !important;\n }\n .mt-md-auto {\n margin-top: auto !important;\n }\n .me-md-0 {\n margin-left: 0 !important;\n }\n .me-md-1 {\n margin-left: 0.25rem !important;\n }\n .me-md-2 {\n margin-left: 0.5rem !important;\n }\n .me-md-3 {\n margin-left: 1rem !important;\n }\n .me-md-4 {\n margin-left: 1.5rem !important;\n }\n .me-md-5 {\n margin-left: 3rem !important;\n }\n .me-md-auto {\n margin-left: auto !important;\n }\n .mb-md-0 {\n margin-bottom: 0 !important;\n }\n .mb-md-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-md-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-md-3 {\n margin-bottom: 1rem !important;\n }\n .mb-md-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-md-5 {\n margin-bottom: 3rem !important;\n }\n .mb-md-auto {\n margin-bottom: auto !important;\n }\n .ms-md-0 {\n margin-right: 0 !important;\n }\n .ms-md-1 {\n margin-right: 0.25rem !important;\n }\n .ms-md-2 {\n margin-right: 0.5rem !important;\n }\n .ms-md-3 {\n margin-right: 1rem !important;\n }\n .ms-md-4 {\n margin-right: 1.5rem !important;\n }\n .ms-md-5 {\n margin-right: 3rem !important;\n }\n .ms-md-auto {\n margin-right: auto !important;\n }\n .p-md-0 {\n padding: 0 !important;\n }\n .p-md-1 {\n padding: 0.25rem !important;\n }\n .p-md-2 {\n padding: 0.5rem !important;\n }\n .p-md-3 {\n padding: 1rem !important;\n }\n .p-md-4 {\n padding: 1.5rem !important;\n }\n .p-md-5 {\n padding: 3rem !important;\n }\n .px-md-0 {\n padding-left: 0 !important;\n padding-right: 0 !important;\n }\n .px-md-1 {\n padding-left: 0.25rem !important;\n padding-right: 0.25rem !important;\n }\n .px-md-2 {\n padding-left: 0.5rem !important;\n padding-right: 0.5rem !important;\n }\n .px-md-3 {\n padding-left: 1rem !important;\n padding-right: 1rem !important;\n }\n .px-md-4 {\n padding-left: 1.5rem !important;\n padding-right: 1.5rem !important;\n }\n .px-md-5 {\n padding-left: 3rem !important;\n padding-right: 3rem !important;\n }\n .py-md-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-md-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-md-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-md-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-md-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-md-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-md-0 {\n padding-top: 0 !important;\n }\n .pt-md-1 {\n padding-top: 0.25rem !important;\n }\n .pt-md-2 {\n padding-top: 0.5rem !important;\n }\n .pt-md-3 {\n padding-top: 1rem !important;\n }\n .pt-md-4 {\n padding-top: 1.5rem !important;\n }\n .pt-md-5 {\n padding-top: 3rem !important;\n }\n .pe-md-0 {\n padding-left: 0 !important;\n }\n .pe-md-1 {\n padding-left: 0.25rem !important;\n }\n .pe-md-2 {\n padding-left: 0.5rem !important;\n }\n .pe-md-3 {\n padding-left: 1rem !important;\n }\n .pe-md-4 {\n padding-left: 1.5rem !important;\n }\n .pe-md-5 {\n padding-left: 3rem !important;\n }\n .pb-md-0 {\n padding-bottom: 0 !important;\n }\n .pb-md-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-md-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-md-3 {\n padding-bottom: 1rem !important;\n }\n .pb-md-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-md-5 {\n padding-bottom: 3rem !important;\n }\n .ps-md-0 {\n padding-right: 0 !important;\n }\n .ps-md-1 {\n padding-right: 0.25rem !important;\n }\n .ps-md-2 {\n padding-right: 0.5rem !important;\n }\n .ps-md-3 {\n padding-right: 1rem !important;\n }\n .ps-md-4 {\n padding-right: 1.5rem !important;\n }\n .ps-md-5 {\n padding-right: 3rem !important;\n }\n}\n@media (min-width: 992px) {\n .d-lg-inline {\n display: inline !important;\n }\n .d-lg-inline-block {\n display: inline-block !important;\n }\n .d-lg-block {\n display: block !important;\n }\n .d-lg-grid {\n display: grid !important;\n }\n .d-lg-inline-grid {\n display: inline-grid !important;\n }\n .d-lg-table {\n display: table !important;\n }\n .d-lg-table-row {\n display: table-row !important;\n }\n .d-lg-table-cell {\n display: table-cell !important;\n }\n .d-lg-flex {\n display: flex !important;\n }\n .d-lg-inline-flex {\n display: inline-flex !important;\n }\n .d-lg-none {\n display: none !important;\n }\n .flex-lg-fill {\n flex: 1 1 auto !important;\n }\n .flex-lg-row {\n flex-direction: row !important;\n }\n .flex-lg-column {\n flex-direction: column !important;\n }\n .flex-lg-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-lg-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-lg-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-lg-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-lg-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-lg-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-lg-wrap {\n flex-wrap: wrap !important;\n }\n .flex-lg-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-lg-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-lg-start {\n justify-content: flex-start !important;\n }\n .justify-content-lg-end {\n justify-content: flex-end !important;\n }\n .justify-content-lg-center {\n justify-content: center !important;\n }\n .justify-content-lg-between {\n justify-content: space-between !important;\n }\n .justify-content-lg-around {\n justify-content: space-around !important;\n }\n .justify-content-lg-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-lg-start {\n align-items: flex-start !important;\n }\n .align-items-lg-end {\n align-items: flex-end !important;\n }\n .align-items-lg-center {\n align-items: center !important;\n }\n .align-items-lg-baseline {\n align-items: baseline !important;\n }\n .align-items-lg-stretch {\n align-items: stretch !important;\n }\n .align-content-lg-start {\n align-content: flex-start !important;\n }\n .align-content-lg-end {\n align-content: flex-end !important;\n }\n .align-content-lg-center {\n align-content: center !important;\n }\n .align-content-lg-between {\n align-content: space-between !important;\n }\n .align-content-lg-around {\n align-content: space-around !important;\n }\n .align-content-lg-stretch {\n align-content: stretch !important;\n }\n .align-self-lg-auto {\n align-self: auto !important;\n }\n .align-self-lg-start {\n align-self: flex-start !important;\n }\n .align-self-lg-end {\n align-self: flex-end !important;\n }\n .align-self-lg-center {\n align-self: center !important;\n }\n .align-self-lg-baseline {\n align-self: baseline !important;\n }\n .align-self-lg-stretch {\n align-self: stretch !important;\n }\n .order-lg-first {\n order: -1 !important;\n }\n .order-lg-0 {\n order: 0 !important;\n }\n .order-lg-1 {\n order: 1 !important;\n }\n .order-lg-2 {\n order: 2 !important;\n }\n .order-lg-3 {\n order: 3 !important;\n }\n .order-lg-4 {\n order: 4 !important;\n }\n .order-lg-5 {\n order: 5 !important;\n }\n .order-lg-last {\n order: 6 !important;\n }\n .m-lg-0 {\n margin: 0 !important;\n }\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n .m-lg-3 {\n margin: 1rem !important;\n }\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n .m-lg-5 {\n margin: 3rem !important;\n }\n .m-lg-auto {\n margin: auto !important;\n }\n .mx-lg-0 {\n margin-left: 0 !important;\n margin-right: 0 !important;\n }\n .mx-lg-1 {\n margin-left: 0.25rem !important;\n margin-right: 0.25rem !important;\n }\n .mx-lg-2 {\n margin-left: 0.5rem !important;\n margin-right: 0.5rem !important;\n }\n .mx-lg-3 {\n margin-left: 1rem !important;\n margin-right: 1rem !important;\n }\n .mx-lg-4 {\n margin-left: 1.5rem !important;\n margin-right: 1.5rem !important;\n }\n .mx-lg-5 {\n margin-left: 3rem !important;\n margin-right: 3rem !important;\n }\n .mx-lg-auto {\n margin-left: auto !important;\n margin-right: auto !important;\n }\n .my-lg-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-lg-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-lg-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-lg-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-lg-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-lg-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-lg-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-lg-0 {\n margin-top: 0 !important;\n }\n .mt-lg-1 {\n margin-top: 0.25rem !important;\n }\n .mt-lg-2 {\n margin-top: 0.5rem !important;\n }\n .mt-lg-3 {\n margin-top: 1rem !important;\n }\n .mt-lg-4 {\n margin-top: 1.5rem !important;\n }\n .mt-lg-5 {\n margin-top: 3rem !important;\n }\n .mt-lg-auto {\n margin-top: auto !important;\n }\n .me-lg-0 {\n margin-left: 0 !important;\n }\n .me-lg-1 {\n margin-left: 0.25rem !important;\n }\n .me-lg-2 {\n margin-left: 0.5rem !important;\n }\n .me-lg-3 {\n margin-left: 1rem !important;\n }\n .me-lg-4 {\n margin-left: 1.5rem !important;\n }\n .me-lg-5 {\n margin-left: 3rem !important;\n }\n .me-lg-auto {\n margin-left: auto !important;\n }\n .mb-lg-0 {\n margin-bottom: 0 !important;\n }\n .mb-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-lg-3 {\n margin-bottom: 1rem !important;\n }\n .mb-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-lg-5 {\n margin-bottom: 3rem !important;\n }\n .mb-lg-auto {\n margin-bottom: auto !important;\n }\n .ms-lg-0 {\n margin-right: 0 !important;\n }\n .ms-lg-1 {\n margin-right: 0.25rem !important;\n }\n .ms-lg-2 {\n margin-right: 0.5rem !important;\n }\n .ms-lg-3 {\n margin-right: 1rem !important;\n }\n .ms-lg-4 {\n margin-right: 1.5rem !important;\n }\n .ms-lg-5 {\n margin-right: 3rem !important;\n }\n .ms-lg-auto {\n margin-right: auto !important;\n }\n .p-lg-0 {\n padding: 0 !important;\n }\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n .p-lg-3 {\n padding: 1rem !important;\n }\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n .p-lg-5 {\n padding: 3rem !important;\n }\n .px-lg-0 {\n padding-left: 0 !important;\n padding-right: 0 !important;\n }\n .px-lg-1 {\n padding-left: 0.25rem !important;\n padding-right: 0.25rem !important;\n }\n .px-lg-2 {\n padding-left: 0.5rem !important;\n padding-right: 0.5rem !important;\n }\n .px-lg-3 {\n padding-left: 1rem !important;\n padding-right: 1rem !important;\n }\n .px-lg-4 {\n padding-left: 1.5rem !important;\n padding-right: 1.5rem !important;\n }\n .px-lg-5 {\n padding-left: 3rem !important;\n padding-right: 3rem !important;\n }\n .py-lg-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-lg-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-lg-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-lg-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-lg-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-lg-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-lg-0 {\n padding-top: 0 !important;\n }\n .pt-lg-1 {\n padding-top: 0.25rem !important;\n }\n .pt-lg-2 {\n padding-top: 0.5rem !important;\n }\n .pt-lg-3 {\n padding-top: 1rem !important;\n }\n .pt-lg-4 {\n padding-top: 1.5rem !important;\n }\n .pt-lg-5 {\n padding-top: 3rem !important;\n }\n .pe-lg-0 {\n padding-left: 0 !important;\n }\n .pe-lg-1 {\n padding-left: 0.25rem !important;\n }\n .pe-lg-2 {\n padding-left: 0.5rem !important;\n }\n .pe-lg-3 {\n padding-left: 1rem !important;\n }\n .pe-lg-4 {\n padding-left: 1.5rem !important;\n }\n .pe-lg-5 {\n padding-left: 3rem !important;\n }\n .pb-lg-0 {\n padding-bottom: 0 !important;\n }\n .pb-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-lg-3 {\n padding-bottom: 1rem !important;\n }\n .pb-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-lg-5 {\n padding-bottom: 3rem !important;\n }\n .ps-lg-0 {\n padding-right: 0 !important;\n }\n .ps-lg-1 {\n padding-right: 0.25rem !important;\n }\n .ps-lg-2 {\n padding-right: 0.5rem !important;\n }\n .ps-lg-3 {\n padding-right: 1rem !important;\n }\n .ps-lg-4 {\n padding-right: 1.5rem !important;\n }\n .ps-lg-5 {\n padding-right: 3rem !important;\n }\n}\n@media (min-width: 1200px) {\n .d-xl-inline {\n display: inline !important;\n }\n .d-xl-inline-block {\n display: inline-block !important;\n }\n .d-xl-block {\n display: block !important;\n }\n .d-xl-grid {\n display: grid !important;\n }\n .d-xl-inline-grid {\n display: inline-grid !important;\n }\n .d-xl-table {\n display: table !important;\n }\n .d-xl-table-row {\n display: table-row !important;\n }\n .d-xl-table-cell {\n display: table-cell !important;\n }\n .d-xl-flex {\n display: flex !important;\n }\n .d-xl-inline-flex {\n display: inline-flex !important;\n }\n .d-xl-none {\n display: none !important;\n }\n .flex-xl-fill {\n flex: 1 1 auto !important;\n }\n .flex-xl-row {\n flex-direction: row !important;\n }\n .flex-xl-column {\n flex-direction: column !important;\n }\n .flex-xl-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-xl-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-xl-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-xl-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-xl-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-xl-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-xl-wrap {\n flex-wrap: wrap !important;\n }\n .flex-xl-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-xl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-xl-start {\n justify-content: flex-start !important;\n }\n .justify-content-xl-end {\n justify-content: flex-end !important;\n }\n .justify-content-xl-center {\n justify-content: center !important;\n }\n .justify-content-xl-between {\n justify-content: space-between !important;\n }\n .justify-content-xl-around {\n justify-content: space-around !important;\n }\n .justify-content-xl-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-xl-start {\n align-items: flex-start !important;\n }\n .align-items-xl-end {\n align-items: flex-end !important;\n }\n .align-items-xl-center {\n align-items: center !important;\n }\n .align-items-xl-baseline {\n align-items: baseline !important;\n }\n .align-items-xl-stretch {\n align-items: stretch !important;\n }\n .align-content-xl-start {\n align-content: flex-start !important;\n }\n .align-content-xl-end {\n align-content: flex-end !important;\n }\n .align-content-xl-center {\n align-content: center !important;\n }\n .align-content-xl-between {\n align-content: space-between !important;\n }\n .align-content-xl-around {\n align-content: space-around !important;\n }\n .align-content-xl-stretch {\n align-content: stretch !important;\n }\n .align-self-xl-auto {\n align-self: auto !important;\n }\n .align-self-xl-start {\n align-self: flex-start !important;\n }\n .align-self-xl-end {\n align-self: flex-end !important;\n }\n .align-self-xl-center {\n align-self: center !important;\n }\n .align-self-xl-baseline {\n align-self: baseline !important;\n }\n .align-self-xl-stretch {\n align-self: stretch !important;\n }\n .order-xl-first {\n order: -1 !important;\n }\n .order-xl-0 {\n order: 0 !important;\n }\n .order-xl-1 {\n order: 1 !important;\n }\n .order-xl-2 {\n order: 2 !important;\n }\n .order-xl-3 {\n order: 3 !important;\n }\n .order-xl-4 {\n order: 4 !important;\n }\n .order-xl-5 {\n order: 5 !important;\n }\n .order-xl-last {\n order: 6 !important;\n }\n .m-xl-0 {\n margin: 0 !important;\n }\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n .m-xl-3 {\n margin: 1rem !important;\n }\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n .m-xl-5 {\n margin: 3rem !important;\n }\n .m-xl-auto {\n margin: auto !important;\n }\n .mx-xl-0 {\n margin-left: 0 !important;\n margin-right: 0 !important;\n }\n .mx-xl-1 {\n margin-left: 0.25rem !important;\n margin-right: 0.25rem !important;\n }\n .mx-xl-2 {\n margin-left: 0.5rem !important;\n margin-right: 0.5rem !important;\n }\n .mx-xl-3 {\n margin-left: 1rem !important;\n margin-right: 1rem !important;\n }\n .mx-xl-4 {\n margin-left: 1.5rem !important;\n margin-right: 1.5rem !important;\n }\n .mx-xl-5 {\n margin-left: 3rem !important;\n margin-right: 3rem !important;\n }\n .mx-xl-auto {\n margin-left: auto !important;\n margin-right: auto !important;\n }\n .my-xl-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-xl-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-xl-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-xl-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-xl-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-xl-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-xl-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-xl-0 {\n margin-top: 0 !important;\n }\n .mt-xl-1 {\n margin-top: 0.25rem !important;\n }\n .mt-xl-2 {\n margin-top: 0.5rem !important;\n }\n .mt-xl-3 {\n margin-top: 1rem !important;\n }\n .mt-xl-4 {\n margin-top: 1.5rem !important;\n }\n .mt-xl-5 {\n margin-top: 3rem !important;\n }\n .mt-xl-auto {\n margin-top: auto !important;\n }\n .me-xl-0 {\n margin-left: 0 !important;\n }\n .me-xl-1 {\n margin-left: 0.25rem !important;\n }\n .me-xl-2 {\n margin-left: 0.5rem !important;\n }\n .me-xl-3 {\n margin-left: 1rem !important;\n }\n .me-xl-4 {\n margin-left: 1.5rem !important;\n }\n .me-xl-5 {\n margin-left: 3rem !important;\n }\n .me-xl-auto {\n margin-left: auto !important;\n }\n .mb-xl-0 {\n margin-bottom: 0 !important;\n }\n .mb-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-xl-3 {\n margin-bottom: 1rem !important;\n }\n .mb-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-xl-5 {\n margin-bottom: 3rem !important;\n }\n .mb-xl-auto {\n margin-bottom: auto !important;\n }\n .ms-xl-0 {\n margin-right: 0 !important;\n }\n .ms-xl-1 {\n margin-right: 0.25rem !important;\n }\n .ms-xl-2 {\n margin-right: 0.5rem !important;\n }\n .ms-xl-3 {\n margin-right: 1rem !important;\n }\n .ms-xl-4 {\n margin-right: 1.5rem !important;\n }\n .ms-xl-5 {\n margin-right: 3rem !important;\n }\n .ms-xl-auto {\n margin-right: auto !important;\n }\n .p-xl-0 {\n padding: 0 !important;\n }\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n .p-xl-3 {\n padding: 1rem !important;\n }\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n .p-xl-5 {\n padding: 3rem !important;\n }\n .px-xl-0 {\n padding-left: 0 !important;\n padding-right: 0 !important;\n }\n .px-xl-1 {\n padding-left: 0.25rem !important;\n padding-right: 0.25rem !important;\n }\n .px-xl-2 {\n padding-left: 0.5rem !important;\n padding-right: 0.5rem !important;\n }\n .px-xl-3 {\n padding-left: 1rem !important;\n padding-right: 1rem !important;\n }\n .px-xl-4 {\n padding-left: 1.5rem !important;\n padding-right: 1.5rem !important;\n }\n .px-xl-5 {\n padding-left: 3rem !important;\n padding-right: 3rem !important;\n }\n .py-xl-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-xl-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-xl-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-xl-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-xl-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-xl-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-xl-0 {\n padding-top: 0 !important;\n }\n .pt-xl-1 {\n padding-top: 0.25rem !important;\n }\n .pt-xl-2 {\n padding-top: 0.5rem !important;\n }\n .pt-xl-3 {\n padding-top: 1rem !important;\n }\n .pt-xl-4 {\n padding-top: 1.5rem !important;\n }\n .pt-xl-5 {\n padding-top: 3rem !important;\n }\n .pe-xl-0 {\n padding-left: 0 !important;\n }\n .pe-xl-1 {\n padding-left: 0.25rem !important;\n }\n .pe-xl-2 {\n padding-left: 0.5rem !important;\n }\n .pe-xl-3 {\n padding-left: 1rem !important;\n }\n .pe-xl-4 {\n padding-left: 1.5rem !important;\n }\n .pe-xl-5 {\n padding-left: 3rem !important;\n }\n .pb-xl-0 {\n padding-bottom: 0 !important;\n }\n .pb-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-xl-3 {\n padding-bottom: 1rem !important;\n }\n .pb-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-xl-5 {\n padding-bottom: 3rem !important;\n }\n .ps-xl-0 {\n padding-right: 0 !important;\n }\n .ps-xl-1 {\n padding-right: 0.25rem !important;\n }\n .ps-xl-2 {\n padding-right: 0.5rem !important;\n }\n .ps-xl-3 {\n padding-right: 1rem !important;\n }\n .ps-xl-4 {\n padding-right: 1.5rem !important;\n }\n .ps-xl-5 {\n padding-right: 3rem !important;\n }\n}\n@media (min-width: 1400px) {\n .d-xxl-inline {\n display: inline !important;\n }\n .d-xxl-inline-block {\n display: inline-block !important;\n }\n .d-xxl-block {\n display: block !important;\n }\n .d-xxl-grid {\n display: grid !important;\n }\n .d-xxl-inline-grid {\n display: inline-grid !important;\n }\n .d-xxl-table {\n display: table !important;\n }\n .d-xxl-table-row {\n display: table-row !important;\n }\n .d-xxl-table-cell {\n display: table-cell !important;\n }\n .d-xxl-flex {\n display: flex !important;\n }\n .d-xxl-inline-flex {\n display: inline-flex !important;\n }\n .d-xxl-none {\n display: none !important;\n }\n .flex-xxl-fill {\n flex: 1 1 auto !important;\n }\n .flex-xxl-row {\n flex-direction: row !important;\n }\n .flex-xxl-column {\n flex-direction: column !important;\n }\n .flex-xxl-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-xxl-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-xxl-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-xxl-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-xxl-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-xxl-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-xxl-wrap {\n flex-wrap: wrap !important;\n }\n .flex-xxl-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-xxl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-xxl-start {\n justify-content: flex-start !important;\n }\n .justify-content-xxl-end {\n justify-content: flex-end !important;\n }\n .justify-content-xxl-center {\n justify-content: center !important;\n }\n .justify-content-xxl-between {\n justify-content: space-between !important;\n }\n .justify-content-xxl-around {\n justify-content: space-around !important;\n }\n .justify-content-xxl-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-xxl-start {\n align-items: flex-start !important;\n }\n .align-items-xxl-end {\n align-items: flex-end !important;\n }\n .align-items-xxl-center {\n align-items: center !important;\n }\n .align-items-xxl-baseline {\n align-items: baseline !important;\n }\n .align-items-xxl-stretch {\n align-items: stretch !important;\n }\n .align-content-xxl-start {\n align-content: flex-start !important;\n }\n .align-content-xxl-end {\n align-content: flex-end !important;\n }\n .align-content-xxl-center {\n align-content: center !important;\n }\n .align-content-xxl-between {\n align-content: space-between !important;\n }\n .align-content-xxl-around {\n align-content: space-around !important;\n }\n .align-content-xxl-stretch {\n align-content: stretch !important;\n }\n .align-self-xxl-auto {\n align-self: auto !important;\n }\n .align-self-xxl-start {\n align-self: flex-start !important;\n }\n .align-self-xxl-end {\n align-self: flex-end !important;\n }\n .align-self-xxl-center {\n align-self: center !important;\n }\n .align-self-xxl-baseline {\n align-self: baseline !important;\n }\n .align-self-xxl-stretch {\n align-self: stretch !important;\n }\n .order-xxl-first {\n order: -1 !important;\n }\n .order-xxl-0 {\n order: 0 !important;\n }\n .order-xxl-1 {\n order: 1 !important;\n }\n .order-xxl-2 {\n order: 2 !important;\n }\n .order-xxl-3 {\n order: 3 !important;\n }\n .order-xxl-4 {\n order: 4 !important;\n }\n .order-xxl-5 {\n order: 5 !important;\n }\n .order-xxl-last {\n order: 6 !important;\n }\n .m-xxl-0 {\n margin: 0 !important;\n }\n .m-xxl-1 {\n margin: 0.25rem !important;\n }\n .m-xxl-2 {\n margin: 0.5rem !important;\n }\n .m-xxl-3 {\n margin: 1rem !important;\n }\n .m-xxl-4 {\n margin: 1.5rem !important;\n }\n .m-xxl-5 {\n margin: 3rem !important;\n }\n .m-xxl-auto {\n margin: auto !important;\n }\n .mx-xxl-0 {\n margin-left: 0 !important;\n margin-right: 0 !important;\n }\n .mx-xxl-1 {\n margin-left: 0.25rem !important;\n margin-right: 0.25rem !important;\n }\n .mx-xxl-2 {\n margin-left: 0.5rem !important;\n margin-right: 0.5rem !important;\n }\n .mx-xxl-3 {\n margin-left: 1rem !important;\n margin-right: 1rem !important;\n }\n .mx-xxl-4 {\n margin-left: 1.5rem !important;\n margin-right: 1.5rem !important;\n }\n .mx-xxl-5 {\n margin-left: 3rem !important;\n margin-right: 3rem !important;\n }\n .mx-xxl-auto {\n margin-left: auto !important;\n margin-right: auto !important;\n }\n .my-xxl-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-xxl-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-xxl-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-xxl-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-xxl-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-xxl-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-xxl-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-xxl-0 {\n margin-top: 0 !important;\n }\n .mt-xxl-1 {\n margin-top: 0.25rem !important;\n }\n .mt-xxl-2 {\n margin-top: 0.5rem !important;\n }\n .mt-xxl-3 {\n margin-top: 1rem !important;\n }\n .mt-xxl-4 {\n margin-top: 1.5rem !important;\n }\n .mt-xxl-5 {\n margin-top: 3rem !important;\n }\n .mt-xxl-auto {\n margin-top: auto !important;\n }\n .me-xxl-0 {\n margin-left: 0 !important;\n }\n .me-xxl-1 {\n margin-left: 0.25rem !important;\n }\n .me-xxl-2 {\n margin-left: 0.5rem !important;\n }\n .me-xxl-3 {\n margin-left: 1rem !important;\n }\n .me-xxl-4 {\n margin-left: 1.5rem !important;\n }\n .me-xxl-5 {\n margin-left: 3rem !important;\n }\n .me-xxl-auto {\n margin-left: auto !important;\n }\n .mb-xxl-0 {\n margin-bottom: 0 !important;\n }\n .mb-xxl-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-xxl-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-xxl-3 {\n margin-bottom: 1rem !important;\n }\n .mb-xxl-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-xxl-5 {\n margin-bottom: 3rem !important;\n }\n .mb-xxl-auto {\n margin-bottom: auto !important;\n }\n .ms-xxl-0 {\n margin-right: 0 !important;\n }\n .ms-xxl-1 {\n margin-right: 0.25rem !important;\n }\n .ms-xxl-2 {\n margin-right: 0.5rem !important;\n }\n .ms-xxl-3 {\n margin-right: 1rem !important;\n }\n .ms-xxl-4 {\n margin-right: 1.5rem !important;\n }\n .ms-xxl-5 {\n margin-right: 3rem !important;\n }\n .ms-xxl-auto {\n margin-right: auto !important;\n }\n .p-xxl-0 {\n padding: 0 !important;\n }\n .p-xxl-1 {\n padding: 0.25rem !important;\n }\n .p-xxl-2 {\n padding: 0.5rem !important;\n }\n .p-xxl-3 {\n padding: 1rem !important;\n }\n .p-xxl-4 {\n padding: 1.5rem !important;\n }\n .p-xxl-5 {\n padding: 3rem !important;\n }\n .px-xxl-0 {\n padding-left: 0 !important;\n padding-right: 0 !important;\n }\n .px-xxl-1 {\n padding-left: 0.25rem !important;\n padding-right: 0.25rem !important;\n }\n .px-xxl-2 {\n padding-left: 0.5rem !important;\n padding-right: 0.5rem !important;\n }\n .px-xxl-3 {\n padding-left: 1rem !important;\n padding-right: 1rem !important;\n }\n .px-xxl-4 {\n padding-left: 1.5rem !important;\n padding-right: 1.5rem !important;\n }\n .px-xxl-5 {\n padding-left: 3rem !important;\n padding-right: 3rem !important;\n }\n .py-xxl-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-xxl-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-xxl-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-xxl-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-xxl-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-xxl-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-xxl-0 {\n padding-top: 0 !important;\n }\n .pt-xxl-1 {\n padding-top: 0.25rem !important;\n }\n .pt-xxl-2 {\n padding-top: 0.5rem !important;\n }\n .pt-xxl-3 {\n padding-top: 1rem !important;\n }\n .pt-xxl-4 {\n padding-top: 1.5rem !important;\n }\n .pt-xxl-5 {\n padding-top: 3rem !important;\n }\n .pe-xxl-0 {\n padding-left: 0 !important;\n }\n .pe-xxl-1 {\n padding-left: 0.25rem !important;\n }\n .pe-xxl-2 {\n padding-left: 0.5rem !important;\n }\n .pe-xxl-3 {\n padding-left: 1rem !important;\n }\n .pe-xxl-4 {\n padding-left: 1.5rem !important;\n }\n .pe-xxl-5 {\n padding-left: 3rem !important;\n }\n .pb-xxl-0 {\n padding-bottom: 0 !important;\n }\n .pb-xxl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-xxl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-xxl-3 {\n padding-bottom: 1rem !important;\n }\n .pb-xxl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-xxl-5 {\n padding-bottom: 3rem !important;\n }\n .ps-xxl-0 {\n padding-right: 0 !important;\n }\n .ps-xxl-1 {\n padding-right: 0.25rem !important;\n }\n .ps-xxl-2 {\n padding-right: 0.5rem !important;\n }\n .ps-xxl-3 {\n padding-right: 1rem !important;\n }\n .ps-xxl-4 {\n padding-right: 1.5rem !important;\n }\n .ps-xxl-5 {\n padding-right: 3rem !important;\n }\n}\n@media print {\n .d-print-inline {\n display: inline !important;\n }\n .d-print-inline-block {\n display: inline-block !important;\n }\n .d-print-block {\n display: block !important;\n }\n .d-print-grid {\n display: grid !important;\n }\n .d-print-inline-grid {\n display: inline-grid !important;\n }\n .d-print-table {\n display: table !important;\n }\n .d-print-table-row {\n display: table-row !important;\n }\n .d-print-table-cell {\n display: table-cell !important;\n }\n .d-print-flex {\n display: flex !important;\n }\n .d-print-inline-flex {\n display: inline-flex !important;\n }\n .d-print-none {\n display: none !important;\n }\n}\n/*# sourceMappingURL=bootstrap-grid.rtl.css.map */","// Container mixins\n\n@mixin make-container($gutter: $container-padding-x) {\n --#{$prefix}gutter-x: #{$gutter};\n --#{$prefix}gutter-y: 0;\n width: 100%;\n padding-right: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list\n padding-left: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list\n margin-right: auto;\n margin-left: auto;\n}\n","// Breakpoint viewport sizes and media queries.\n//\n// Breakpoints are defined as a map of (name: minimum width), order from small to large:\n//\n// (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)\n//\n// The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default.\n\n// Name of the next breakpoint, or null for the last breakpoint.\n//\n// >> breakpoint-next(sm)\n// md\n// >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// md\n// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl xxl))\n// md\n@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {\n $n: index($breakpoint-names, $name);\n @if not $n {\n @error \"breakpoint `#{$name}` not found in `#{$breakpoints}`\";\n }\n @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);\n}\n\n// Minimum breakpoint width. Null for the smallest (first) breakpoint.\n//\n// >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// 576px\n@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {\n $min: map-get($breakpoints, $name);\n @return if($min != 0, $min, null);\n}\n\n// Maximum breakpoint width.\n// The maximum value is reduced by 0.02px to work around the limitations of\n// `min-` and `max-` prefixes and viewports with fractional widths.\n// See https://www.w3.org/TR/mediaqueries-4/#mq-min-max\n// Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari.\n// See https://bugs.webkit.org/show_bug.cgi?id=178261\n//\n// >> breakpoint-max(md, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// 767.98px\n@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {\n $max: map-get($breakpoints, $name);\n @return if($max and $max > 0, $max - .02, null);\n}\n\n// Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front.\n// Useful for making responsive utilities.\n//\n// >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// \"\" (Returns a blank string)\n// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px))\n// \"-sm\"\n@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {\n @return if(breakpoint-min($name, $breakpoints) == null, \"\", \"-#{$name}\");\n}\n\n// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.\n// Makes the @content apply to the given breakpoint and wider.\n@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n @if $min {\n @media (min-width: $min) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media of at most the maximum breakpoint width. No query for the largest breakpoint.\n// Makes the @content apply to the given breakpoint and narrower.\n@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {\n $max: breakpoint-max($name, $breakpoints);\n @if $max {\n @media (max-width: $max) {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Media that spans multiple breakpoint widths.\n// Makes the @content apply between the min and max breakpoints\n@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($lower, $breakpoints);\n $max: breakpoint-max($upper, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($lower, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($upper, $breakpoints) {\n @content;\n }\n }\n}\n\n// Media between the breakpoint's minimum and maximum widths.\n// No minimum for the smallest breakpoint, and no maximum for the largest one.\n// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.\n@mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) {\n $min: breakpoint-min($name, $breakpoints);\n $next: breakpoint-next($name, $breakpoints);\n $max: breakpoint-max($next, $breakpoints);\n\n @if $min != null and $max != null {\n @media (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else if $max == null {\n @include media-breakpoint-up($name, $breakpoints) {\n @content;\n }\n } @else if $min == null {\n @include media-breakpoint-down($next, $breakpoints) {\n @content;\n }\n }\n}\n","// Row\n//\n// Rows contain your columns.\n\n:root {\n @each $name, $value in $grid-breakpoints {\n --#{$prefix}breakpoint-#{$name}: #{$value};\n }\n}\n\n@if $enable-grid-classes {\n .row {\n @include make-row();\n\n > * {\n @include make-col-ready();\n }\n }\n}\n\n@if $enable-cssgrid {\n .grid {\n display: grid;\n grid-template-rows: repeat(var(--#{$prefix}rows, 1), 1fr);\n grid-template-columns: repeat(var(--#{$prefix}columns, #{$grid-columns}), 1fr);\n gap: var(--#{$prefix}gap, #{$grid-gutter-width});\n\n @include make-cssgrid();\n }\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n@if $enable-grid-classes {\n @include make-grid-columns();\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n@mixin make-row($gutter: $grid-gutter-width) {\n --#{$prefix}gutter-x: #{$gutter};\n --#{$prefix}gutter-y: 0;\n display: flex;\n flex-wrap: wrap;\n // TODO: Revisit calc order after https://github.com/react-bootstrap/react-bootstrap/issues/6039 is fixed\n margin-top: calc(-1 * var(--#{$prefix}gutter-y)); // stylelint-disable-line function-disallowed-list\n margin-right: calc(-.5 * var(--#{$prefix}gutter-x)); // stylelint-disable-line function-disallowed-list\n margin-left: calc(-.5 * var(--#{$prefix}gutter-x)); // stylelint-disable-line function-disallowed-list\n}\n\n@mixin make-col-ready() {\n // Add box sizing if only the grid is loaded\n box-sizing: if(variable-exists(include-column-box-sizing) and $include-column-box-sizing, border-box, null);\n // Prevent columns from becoming too narrow when at smaller grid tiers by\n // always setting `width: 100%;`. This works because we set the width\n // later on to override this initial width.\n flex-shrink: 0;\n width: 100%;\n max-width: 100%; // Prevent `.col-auto`, `.col` (& responsive variants) from breaking out the grid\n padding-right: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list\n padding-left: calc(var(--#{$prefix}gutter-x) * .5); // stylelint-disable-line function-disallowed-list\n margin-top: var(--#{$prefix}gutter-y);\n}\n\n@mixin make-col($size: false, $columns: $grid-columns) {\n @if $size {\n flex: 0 0 auto;\n width: percentage(divide($size, $columns));\n\n } @else {\n flex: 1 1 0;\n max-width: 100%;\n }\n}\n\n@mixin make-col-auto() {\n flex: 0 0 auto;\n width: auto;\n}\n\n@mixin make-col-offset($size, $columns: $grid-columns) {\n $num: divide($size, $columns);\n margin-left: if($num == 0, 0, percentage($num));\n}\n\n// Row columns\n//\n// Specify on a parent element(e.g., .row) to force immediate children into NN\n// number of columns. Supports wrapping to new lines, but does not do a Masonry\n// style grid.\n@mixin row-cols($count) {\n > * {\n flex: 0 0 auto;\n width: percentage(divide(1, $count));\n }\n}\n\n// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `$grid-columns`.\n\n@mixin make-grid-columns($columns: $grid-columns, $gutter: $grid-gutter-width, $breakpoints: $grid-breakpoints) {\n @each $breakpoint in map-keys($breakpoints) {\n $infix: breakpoint-infix($breakpoint, $breakpoints);\n\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n // Provide basic `.col-{bp}` classes for equal-width flexbox columns\n .col#{$infix} {\n flex: 1 0 0%; // Flexbugs #4: https://github.com/philipwalton/flexbugs#flexbug-4\n }\n\n .row-cols#{$infix}-auto > * {\n @include make-col-auto();\n }\n\n @if $grid-row-columns > 0 {\n @for $i from 1 through $grid-row-columns {\n .row-cols#{$infix}-#{$i} {\n @include row-cols($i);\n }\n }\n }\n\n .col#{$infix}-auto {\n @include make-col-auto();\n }\n\n @if $columns > 0 {\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @include make-col($i, $columns);\n }\n }\n\n // `$columns - 1` because offsetting by the width of an entire row isn't possible\n @for $i from 0 through ($columns - 1) {\n @if not ($infix == \"\" and $i == 0) { // Avoid emitting useless .offset-0\n .offset#{$infix}-#{$i} {\n @include make-col-offset($i, $columns);\n }\n }\n }\n }\n\n // Gutters\n //\n // Make use of `.g-*`, `.gx-*` or `.gy-*` utilities to change spacing between the columns.\n @each $key, $value in $gutters {\n .g#{$infix}-#{$key},\n .gx#{$infix}-#{$key} {\n --#{$prefix}gutter-x: #{$value};\n }\n\n .g#{$infix}-#{$key},\n .gy#{$infix}-#{$key} {\n --#{$prefix}gutter-y: #{$value};\n }\n }\n }\n }\n}\n\n@mixin make-cssgrid($columns: $grid-columns, $breakpoints: $grid-breakpoints) {\n @each $breakpoint in map-keys($breakpoints) {\n $infix: breakpoint-infix($breakpoint, $breakpoints);\n\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n @if $columns > 0 {\n @for $i from 1 through $columns {\n .g-col#{$infix}-#{$i} {\n grid-column: auto / span $i;\n }\n }\n\n // Start with `1` because `0` is an invalid value.\n // Ends with `$columns - 1` because offsetting by the width of an entire row isn't possible.\n @for $i from 1 through ($columns - 1) {\n .g-start#{$infix}-#{$i} {\n grid-column-start: $i;\n }\n }\n }\n }\n }\n}\n","// Utility generator\n// Used to generate utilities & print utilities\n@mixin generate-utility($utility, $infix: \"\", $is-rfs-media-query: false) {\n $values: map-get($utility, values);\n\n // If the values are a list or string, convert it into a map\n @if type-of($values) == \"string\" or type-of(nth($values, 1)) != \"list\" {\n $values: zip($values, $values);\n }\n\n @each $key, $value in $values {\n $properties: map-get($utility, property);\n\n // Multiple properties are possible, for example with vertical or horizontal margins or paddings\n @if type-of($properties) == \"string\" {\n $properties: append((), $properties);\n }\n\n // Use custom class if present\n $property-class: if(map-has-key($utility, class), map-get($utility, class), nth($properties, 1));\n $property-class: if($property-class == null, \"\", $property-class);\n\n // Use custom CSS variable name if present, otherwise default to `class`\n $css-variable-name: if(map-has-key($utility, css-variable-name), map-get($utility, css-variable-name), map-get($utility, class));\n\n // State params to generate pseudo-classes\n $state: if(map-has-key($utility, state), map-get($utility, state), ());\n\n $infix: if($property-class == \"\" and str-slice($infix, 1, 1) == \"-\", str-slice($infix, 2), $infix);\n\n // Don't prefix if value key is null (e.g. with shadow class)\n $property-class-modifier: if($key, if($property-class == \"\" and $infix == \"\", \"\", \"-\") + $key, \"\");\n\n @if map-get($utility, rfs) {\n // Inside the media query\n @if $is-rfs-media-query {\n $val: rfs-value($value);\n\n // Do not render anything if fluid and non fluid values are the same\n $value: if($val == rfs-fluid-value($value), null, $val);\n }\n @else {\n $value: rfs-fluid-value($value);\n }\n }\n\n $is-css-var: map-get($utility, css-var);\n $is-local-vars: map-get($utility, local-vars);\n $is-rtl: map-get($utility, rtl);\n\n @if $value != null {\n @if $is-rtl == false {\n /* rtl:begin:remove */\n }\n\n @if $is-css-var {\n .#{$property-class + $infix + $property-class-modifier} {\n --#{$prefix}#{$css-variable-name}: #{$value};\n }\n\n @each $pseudo in $state {\n .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} {\n --#{$prefix}#{$css-variable-name}: #{$value};\n }\n }\n } @else {\n .#{$property-class + $infix + $property-class-modifier} {\n @each $property in $properties {\n @if $is-local-vars {\n @each $local-var, $variable in $is-local-vars {\n --#{$prefix}#{$local-var}: #{$variable};\n }\n }\n #{$property}: $value if($enable-important-utilities, !important, null);\n }\n }\n\n @each $pseudo in $state {\n .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} {\n @each $property in $properties {\n @if $is-local-vars {\n @each $local-var, $variable in $is-local-vars {\n --#{$prefix}#{$local-var}: #{$variable};\n }\n }\n #{$property}: $value if($enable-important-utilities, !important, null);\n }\n }\n }\n }\n\n @if $is-rtl == false {\n /* rtl:end:remove */\n }\n }\n }\n}\n","// Loop over each breakpoint\n@each $breakpoint in map-keys($grid-breakpoints) {\n\n // Generate media query if needed\n @include media-breakpoint-up($breakpoint) {\n $infix: breakpoint-infix($breakpoint, $grid-breakpoints);\n\n // Loop over each utility property\n @each $key, $utility in $utilities {\n // The utility can be disabled with `false`, thus check if the utility is a map first\n // Only proceed if responsive media queries are enabled or if it's the base media query\n @if type-of($utility) == \"map\" and (map-get($utility, responsive) or $infix == \"\") {\n @include generate-utility($utility, $infix);\n }\n }\n }\n}\n\n// RFS rescaling\n@media (min-width: $rfs-mq-value) {\n @each $breakpoint in map-keys($grid-breakpoints) {\n $infix: breakpoint-infix($breakpoint, $grid-breakpoints);\n\n @if (map-get($grid-breakpoints, $breakpoint) < $rfs-breakpoint) {\n // Loop over each utility property\n @each $key, $utility in $utilities {\n // The utility can be disabled with `false`, thus check if the utility is a map first\n // Only proceed if responsive media queries are enabled or if it's the base media query\n @if type-of($utility) == \"map\" and map-get($utility, rfs) and (map-get($utility, responsive) or $infix == \"\") {\n @include generate-utility($utility, $infix, true);\n }\n }\n }\n }\n}\n\n\n// Print utilities\n@media print {\n @each $key, $utility in $utilities {\n // The utility can be disabled with `false`, thus check if the utility is a map first\n // Then check if the utility needs print styles\n @if type-of($utility) == \"map\" and map-get($utility, print) == true {\n @include generate-utility($utility, \"-print\");\n }\n }\n}\n"]} \ No newline at end of file diff --git a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css b/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css deleted file mode 100644 index 6305410..0000000 --- a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css +++ /dev/null @@ -1,597 +0,0 @@ -/*! - * Bootstrap Reboot v5.3.3 (https://getbootstrap.com/) - * Copyright 2011-2024 The Bootstrap Authors - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ -:root, -[data-bs-theme=light] { - --bs-blue: #0d6efd; - --bs-indigo: #6610f2; - --bs-purple: #6f42c1; - --bs-pink: #d63384; - --bs-red: #dc3545; - --bs-orange: #fd7e14; - --bs-yellow: #ffc107; - --bs-green: #198754; - --bs-teal: #20c997; - --bs-cyan: #0dcaf0; - --bs-black: #000; - --bs-white: #fff; - --bs-gray: #6c757d; - --bs-gray-dark: #343a40; - --bs-gray-100: #f8f9fa; - --bs-gray-200: #e9ecef; - --bs-gray-300: #dee2e6; - --bs-gray-400: #ced4da; - --bs-gray-500: #adb5bd; - --bs-gray-600: #6c757d; - --bs-gray-700: #495057; - --bs-gray-800: #343a40; - --bs-gray-900: #212529; - --bs-primary: #0d6efd; - --bs-secondary: #6c757d; - --bs-success: #198754; - --bs-info: #0dcaf0; - --bs-warning: #ffc107; - --bs-danger: #dc3545; - --bs-light: #f8f9fa; - --bs-dark: #212529; - --bs-primary-rgb: 13, 110, 253; - --bs-secondary-rgb: 108, 117, 125; - --bs-success-rgb: 25, 135, 84; - --bs-info-rgb: 13, 202, 240; - --bs-warning-rgb: 255, 193, 7; - --bs-danger-rgb: 220, 53, 69; - --bs-light-rgb: 248, 249, 250; - --bs-dark-rgb: 33, 37, 41; - --bs-primary-text-emphasis: #052c65; - --bs-secondary-text-emphasis: #2b2f32; - --bs-success-text-emphasis: #0a3622; - --bs-info-text-emphasis: #055160; - --bs-warning-text-emphasis: #664d03; - --bs-danger-text-emphasis: #58151c; - --bs-light-text-emphasis: #495057; - --bs-dark-text-emphasis: #495057; - --bs-primary-bg-subtle: #cfe2ff; - --bs-secondary-bg-subtle: #e2e3e5; - --bs-success-bg-subtle: #d1e7dd; - --bs-info-bg-subtle: #cff4fc; - --bs-warning-bg-subtle: #fff3cd; - --bs-danger-bg-subtle: #f8d7da; - --bs-light-bg-subtle: #fcfcfd; - --bs-dark-bg-subtle: #ced4da; - --bs-primary-border-subtle: #9ec5fe; - --bs-secondary-border-subtle: #c4c8cb; - --bs-success-border-subtle: #a3cfbb; - --bs-info-border-subtle: #9eeaf9; - --bs-warning-border-subtle: #ffe69c; - --bs-danger-border-subtle: #f1aeb5; - --bs-light-border-subtle: #e9ecef; - --bs-dark-border-subtle: #adb5bd; - --bs-white-rgb: 255, 255, 255; - --bs-black-rgb: 0, 0, 0; - --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); - --bs-body-font-family: var(--bs-font-sans-serif); - --bs-body-font-size: 1rem; - --bs-body-font-weight: 400; - --bs-body-line-height: 1.5; - --bs-body-color: #212529; - --bs-body-color-rgb: 33, 37, 41; - --bs-body-bg: #fff; - --bs-body-bg-rgb: 255, 255, 255; - --bs-emphasis-color: #000; - --bs-emphasis-color-rgb: 0, 0, 0; - --bs-secondary-color: rgba(33, 37, 41, 0.75); - --bs-secondary-color-rgb: 33, 37, 41; - --bs-secondary-bg: #e9ecef; - --bs-secondary-bg-rgb: 233, 236, 239; - --bs-tertiary-color: rgba(33, 37, 41, 0.5); - --bs-tertiary-color-rgb: 33, 37, 41; - --bs-tertiary-bg: #f8f9fa; - --bs-tertiary-bg-rgb: 248, 249, 250; - --bs-heading-color: inherit; - --bs-link-color: #0d6efd; - --bs-link-color-rgb: 13, 110, 253; - --bs-link-decoration: underline; - --bs-link-hover-color: #0a58ca; - --bs-link-hover-color-rgb: 10, 88, 202; - --bs-code-color: #d63384; - --bs-highlight-color: #212529; - --bs-highlight-bg: #fff3cd; - --bs-border-width: 1px; - --bs-border-style: solid; - --bs-border-color: #dee2e6; - --bs-border-color-translucent: rgba(0, 0, 0, 0.175); - --bs-border-radius: 0.375rem; - --bs-border-radius-sm: 0.25rem; - --bs-border-radius-lg: 0.5rem; - --bs-border-radius-xl: 1rem; - --bs-border-radius-xxl: 2rem; - --bs-border-radius-2xl: var(--bs-border-radius-xxl); - --bs-border-radius-pill: 50rem; - --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); - --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); - --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); - --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); - --bs-focus-ring-width: 0.25rem; - --bs-focus-ring-opacity: 0.25; - --bs-focus-ring-color: rgba(13, 110, 253, 0.25); - --bs-form-valid-color: #198754; - --bs-form-valid-border-color: #198754; - --bs-form-invalid-color: #dc3545; - --bs-form-invalid-border-color: #dc3545; -} - -[data-bs-theme=dark] { - color-scheme: dark; - --bs-body-color: #dee2e6; - --bs-body-color-rgb: 222, 226, 230; - --bs-body-bg: #212529; - --bs-body-bg-rgb: 33, 37, 41; - --bs-emphasis-color: #fff; - --bs-emphasis-color-rgb: 255, 255, 255; - --bs-secondary-color: rgba(222, 226, 230, 0.75); - --bs-secondary-color-rgb: 222, 226, 230; - --bs-secondary-bg: #343a40; - --bs-secondary-bg-rgb: 52, 58, 64; - --bs-tertiary-color: rgba(222, 226, 230, 0.5); - --bs-tertiary-color-rgb: 222, 226, 230; - --bs-tertiary-bg: #2b3035; - --bs-tertiary-bg-rgb: 43, 48, 53; - --bs-primary-text-emphasis: #6ea8fe; - --bs-secondary-text-emphasis: #a7acb1; - --bs-success-text-emphasis: #75b798; - --bs-info-text-emphasis: #6edff6; - --bs-warning-text-emphasis: #ffda6a; - --bs-danger-text-emphasis: #ea868f; - --bs-light-text-emphasis: #f8f9fa; - --bs-dark-text-emphasis: #dee2e6; - --bs-primary-bg-subtle: #031633; - --bs-secondary-bg-subtle: #161719; - --bs-success-bg-subtle: #051b11; - --bs-info-bg-subtle: #032830; - --bs-warning-bg-subtle: #332701; - --bs-danger-bg-subtle: #2c0b0e; - --bs-light-bg-subtle: #343a40; - --bs-dark-bg-subtle: #1a1d20; - --bs-primary-border-subtle: #084298; - --bs-secondary-border-subtle: #41464b; - --bs-success-border-subtle: #0f5132; - --bs-info-border-subtle: #087990; - --bs-warning-border-subtle: #997404; - --bs-danger-border-subtle: #842029; - --bs-light-border-subtle: #495057; - --bs-dark-border-subtle: #343a40; - --bs-heading-color: inherit; - --bs-link-color: #6ea8fe; - --bs-link-hover-color: #8bb9fe; - --bs-link-color-rgb: 110, 168, 254; - --bs-link-hover-color-rgb: 139, 185, 254; - --bs-code-color: #e685b5; - --bs-highlight-color: #dee2e6; - --bs-highlight-bg: #664d03; - --bs-border-color: #495057; - --bs-border-color-translucent: rgba(255, 255, 255, 0.15); - --bs-form-valid-color: #75b798; - --bs-form-valid-border-color: #75b798; - --bs-form-invalid-color: #ea868f; - --bs-form-invalid-border-color: #ea868f; -} - -*, -*::before, -*::after { - box-sizing: border-box; -} - -@media (prefers-reduced-motion: no-preference) { - :root { - scroll-behavior: smooth; - } -} - -body { - margin: 0; - font-family: var(--bs-body-font-family); - font-size: var(--bs-body-font-size); - font-weight: var(--bs-body-font-weight); - line-height: var(--bs-body-line-height); - color: var(--bs-body-color); - text-align: var(--bs-body-text-align); - background-color: var(--bs-body-bg); - -webkit-text-size-adjust: 100%; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} - -hr { - margin: 1rem 0; - color: inherit; - border: 0; - border-top: var(--bs-border-width) solid; - opacity: 0.25; -} - -h6, h5, h4, h3, h2, h1 { - margin-top: 0; - margin-bottom: 0.5rem; - font-weight: 500; - line-height: 1.2; - color: var(--bs-heading-color); -} - -h1 { - font-size: calc(1.375rem + 1.5vw); -} -@media (min-width: 1200px) { - h1 { - font-size: 2.5rem; - } -} - -h2 { - font-size: calc(1.325rem + 0.9vw); -} -@media (min-width: 1200px) { - h2 { - font-size: 2rem; - } -} - -h3 { - font-size: calc(1.3rem + 0.6vw); -} -@media (min-width: 1200px) { - h3 { - font-size: 1.75rem; - } -} - -h4 { - font-size: calc(1.275rem + 0.3vw); -} -@media (min-width: 1200px) { - h4 { - font-size: 1.5rem; - } -} - -h5 { - font-size: 1.25rem; -} - -h6 { - font-size: 1rem; -} - -p { - margin-top: 0; - margin-bottom: 1rem; -} - -abbr[title] { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; - cursor: help; - -webkit-text-decoration-skip-ink: none; - text-decoration-skip-ink: none; -} - -address { - margin-bottom: 1rem; - font-style: normal; - line-height: inherit; -} - -ol, -ul { - padding-left: 2rem; -} - -ol, -ul, -dl { - margin-top: 0; - margin-bottom: 1rem; -} - -ol ol, -ul ul, -ol ul, -ul ol { - margin-bottom: 0; -} - -dt { - font-weight: 700; -} - -dd { - margin-bottom: 0.5rem; - margin-left: 0; -} - -blockquote { - margin: 0 0 1rem; -} - -b, -strong { - font-weight: bolder; -} - -small { - font-size: 0.875em; -} - -mark { - padding: 0.1875em; - color: var(--bs-highlight-color); - background-color: var(--bs-highlight-bg); -} - -sub, -sup { - position: relative; - font-size: 0.75em; - line-height: 0; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -a { - color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); - text-decoration: underline; -} -a:hover { - --bs-link-color-rgb: var(--bs-link-hover-color-rgb); -} - -a:not([href]):not([class]), a:not([href]):not([class]):hover { - color: inherit; - text-decoration: none; -} - -pre, -code, -kbd, -samp { - font-family: var(--bs-font-monospace); - font-size: 1em; -} - -pre { - display: block; - margin-top: 0; - margin-bottom: 1rem; - overflow: auto; - font-size: 0.875em; -} -pre code { - font-size: inherit; - color: inherit; - word-break: normal; -} - -code { - font-size: 0.875em; - color: var(--bs-code-color); - word-wrap: break-word; -} -a > code { - color: inherit; -} - -kbd { - padding: 0.1875rem 0.375rem; - font-size: 0.875em; - color: var(--bs-body-bg); - background-color: var(--bs-body-color); - border-radius: 0.25rem; -} -kbd kbd { - padding: 0; - font-size: 1em; -} - -figure { - margin: 0 0 1rem; -} - -img, -svg { - vertical-align: middle; -} - -table { - caption-side: bottom; - border-collapse: collapse; -} - -caption { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - color: var(--bs-secondary-color); - text-align: left; -} - -th { - text-align: inherit; - text-align: -webkit-match-parent; -} - -thead, -tbody, -tfoot, -tr, -td, -th { - border-color: inherit; - border-style: solid; - border-width: 0; -} - -label { - display: inline-block; -} - -button { - border-radius: 0; -} - -button:focus:not(:focus-visible) { - outline: 0; -} - -input, -button, -select, -optgroup, -textarea { - margin: 0; - font-family: inherit; - font-size: inherit; - line-height: inherit; -} - -button, -select { - text-transform: none; -} - -[role=button] { - cursor: pointer; -} - -select { - word-wrap: normal; -} -select:disabled { - opacity: 1; -} - -[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator { - display: none !important; -} - -button, -[type=button], -[type=reset], -[type=submit] { - -webkit-appearance: button; -} -button:not(:disabled), -[type=button]:not(:disabled), -[type=reset]:not(:disabled), -[type=submit]:not(:disabled) { - cursor: pointer; -} - -::-moz-focus-inner { - padding: 0; - border-style: none; -} - -textarea { - resize: vertical; -} - -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; -} - -legend { - float: left; - width: 100%; - padding: 0; - margin-bottom: 0.5rem; - font-size: calc(1.275rem + 0.3vw); - line-height: inherit; -} -@media (min-width: 1200px) { - legend { - font-size: 1.5rem; - } -} -legend + * { - clear: left; -} - -::-webkit-datetime-edit-fields-wrapper, -::-webkit-datetime-edit-text, -::-webkit-datetime-edit-minute, -::-webkit-datetime-edit-hour-field, -::-webkit-datetime-edit-day-field, -::-webkit-datetime-edit-month-field, -::-webkit-datetime-edit-year-field { - padding: 0; -} - -::-webkit-inner-spin-button { - height: auto; -} - -[type=search] { - -webkit-appearance: textfield; - outline-offset: -2px; -} - -/* rtl:raw: -[type="tel"], -[type="url"], -[type="email"], -[type="number"] { - direction: ltr; -} -*/ -::-webkit-search-decoration { - -webkit-appearance: none; -} - -::-webkit-color-swatch-wrapper { - padding: 0; -} - -::-webkit-file-upload-button { - font: inherit; - -webkit-appearance: button; -} - -::file-selector-button { - font: inherit; - -webkit-appearance: button; -} - -output { - display: inline-block; -} - -iframe { - border: 0; -} - -summary { - display: list-item; - cursor: pointer; -} - -progress { - vertical-align: baseline; -} - -[hidden] { - display: none !important; -} - -/*# sourceMappingURL=bootstrap-reboot.css.map */ \ No newline at end of file diff --git a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map b/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map deleted file mode 100644 index 5fe522b..0000000 --- a/src/Presentation.Blazor/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../scss/mixins/_banner.scss","../../scss/_root.scss","../../scss/vendor/_rfs.scss","bootstrap-reboot.css","../../scss/mixins/_color-mode.scss","../../scss/_reboot.scss","../../scss/_variables.scss","../../scss/mixins/_border-radius.scss"],"names":[],"mappings":"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","file":"bootstrap-reboot.css","sourcesContent":["@mixin bsBanner($file) {\n /*!\n * Bootstrap #{$file} v5.3.3 (https://getbootstrap.com/)\n * Copyright 2011-2024 The Bootstrap Authors\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n}\n",":root,\n[data-bs-theme=\"light\"] {\n // Note: Custom variable values only support SassScript inside `#{}`.\n\n // Colors\n //\n // Generate palettes for full colors, grays, and theme colors.\n\n @each $color, $value in $colors {\n --#{$prefix}#{$color}: #{$value};\n }\n\n @each $color, $value in $grays {\n --#{$prefix}gray-#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors {\n --#{$prefix}#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors-rgb {\n --#{$prefix}#{$color}-rgb: #{$value};\n }\n\n @each $color, $value in $theme-colors-text {\n --#{$prefix}#{$color}-text-emphasis: #{$value};\n }\n\n @each $color, $value in $theme-colors-bg-subtle {\n --#{$prefix}#{$color}-bg-subtle: #{$value};\n }\n\n @each $color, $value in $theme-colors-border-subtle {\n --#{$prefix}#{$color}-border-subtle: #{$value};\n }\n\n --#{$prefix}white-rgb: #{to-rgb($white)};\n --#{$prefix}black-rgb: #{to-rgb($black)};\n\n // Fonts\n\n // Note: Use `inspect` for lists so that quoted items keep the quotes.\n // See https://github.com/sass/sass/issues/2383#issuecomment-336349172\n --#{$prefix}font-sans-serif: #{inspect($font-family-sans-serif)};\n --#{$prefix}font-monospace: #{inspect($font-family-monospace)};\n --#{$prefix}gradient: #{$gradient};\n\n // Root and body\n // scss-docs-start root-body-variables\n @if $font-size-root != null {\n --#{$prefix}root-font-size: #{$font-size-root};\n }\n --#{$prefix}body-font-family: #{inspect($font-family-base)};\n @include rfs($font-size-base, --#{$prefix}body-font-size);\n --#{$prefix}body-font-weight: #{$font-weight-base};\n --#{$prefix}body-line-height: #{$line-height-base};\n @if $body-text-align != null {\n --#{$prefix}body-text-align: #{$body-text-align};\n }\n\n --#{$prefix}body-color: #{$body-color};\n --#{$prefix}body-color-rgb: #{to-rgb($body-color)};\n --#{$prefix}body-bg: #{$body-bg};\n --#{$prefix}body-bg-rgb: #{to-rgb($body-bg)};\n\n --#{$prefix}emphasis-color: #{$body-emphasis-color};\n --#{$prefix}emphasis-color-rgb: #{to-rgb($body-emphasis-color)};\n\n --#{$prefix}secondary-color: #{$body-secondary-color};\n --#{$prefix}secondary-color-rgb: #{to-rgb($body-secondary-color)};\n --#{$prefix}secondary-bg: #{$body-secondary-bg};\n --#{$prefix}secondary-bg-rgb: #{to-rgb($body-secondary-bg)};\n\n --#{$prefix}tertiary-color: #{$body-tertiary-color};\n --#{$prefix}tertiary-color-rgb: #{to-rgb($body-tertiary-color)};\n --#{$prefix}tertiary-bg: #{$body-tertiary-bg};\n --#{$prefix}tertiary-bg-rgb: #{to-rgb($body-tertiary-bg)};\n // scss-docs-end root-body-variables\n\n --#{$prefix}heading-color: #{$headings-color};\n\n --#{$prefix}link-color: #{$link-color};\n --#{$prefix}link-color-rgb: #{to-rgb($link-color)};\n --#{$prefix}link-decoration: #{$link-decoration};\n\n --#{$prefix}link-hover-color: #{$link-hover-color};\n --#{$prefix}link-hover-color-rgb: #{to-rgb($link-hover-color)};\n\n @if $link-hover-decoration != null {\n --#{$prefix}link-hover-decoration: #{$link-hover-decoration};\n }\n\n --#{$prefix}code-color: #{$code-color};\n --#{$prefix}highlight-color: #{$mark-color};\n --#{$prefix}highlight-bg: #{$mark-bg};\n\n // scss-docs-start root-border-var\n --#{$prefix}border-width: #{$border-width};\n --#{$prefix}border-style: #{$border-style};\n --#{$prefix}border-color: #{$border-color};\n --#{$prefix}border-color-translucent: #{$border-color-translucent};\n\n --#{$prefix}border-radius: #{$border-radius};\n --#{$prefix}border-radius-sm: #{$border-radius-sm};\n --#{$prefix}border-radius-lg: #{$border-radius-lg};\n --#{$prefix}border-radius-xl: #{$border-radius-xl};\n --#{$prefix}border-radius-xxl: #{$border-radius-xxl};\n --#{$prefix}border-radius-2xl: var(--#{$prefix}border-radius-xxl); // Deprecated in v5.3.0 for consistency\n --#{$prefix}border-radius-pill: #{$border-radius-pill};\n // scss-docs-end root-border-var\n\n --#{$prefix}box-shadow: #{$box-shadow};\n --#{$prefix}box-shadow-sm: #{$box-shadow-sm};\n --#{$prefix}box-shadow-lg: #{$box-shadow-lg};\n --#{$prefix}box-shadow-inset: #{$box-shadow-inset};\n\n // Focus styles\n // scss-docs-start root-focus-variables\n --#{$prefix}focus-ring-width: #{$focus-ring-width};\n --#{$prefix}focus-ring-opacity: #{$focus-ring-opacity};\n --#{$prefix}focus-ring-color: #{$focus-ring-color};\n // scss-docs-end root-focus-variables\n\n // scss-docs-start root-form-validation-variables\n --#{$prefix}form-valid-color: #{$form-valid-color};\n --#{$prefix}form-valid-border-color: #{$form-valid-border-color};\n --#{$prefix}form-invalid-color: #{$form-invalid-color};\n --#{$prefix}form-invalid-border-color: #{$form-invalid-border-color};\n // scss-docs-end root-form-validation-variables\n}\n\n@if $enable-dark-mode {\n @include color-mode(dark, true) {\n color-scheme: dark;\n\n // scss-docs-start root-dark-mode-vars\n --#{$prefix}body-color: #{$body-color-dark};\n --#{$prefix}body-color-rgb: #{to-rgb($body-color-dark)};\n --#{$prefix}body-bg: #{$body-bg-dark};\n --#{$prefix}body-bg-rgb: #{to-rgb($body-bg-dark)};\n\n --#{$prefix}emphasis-color: #{$body-emphasis-color-dark};\n --#{$prefix}emphasis-color-rgb: #{to-rgb($body-emphasis-color-dark)};\n\n --#{$prefix}secondary-color: #{$body-secondary-color-dark};\n --#{$prefix}secondary-color-rgb: #{to-rgb($body-secondary-color-dark)};\n --#{$prefix}secondary-bg: #{$body-secondary-bg-dark};\n --#{$prefix}secondary-bg-rgb: #{to-rgb($body-secondary-bg-dark)};\n\n --#{$prefix}tertiary-color: #{$body-tertiary-color-dark};\n --#{$prefix}tertiary-color-rgb: #{to-rgb($body-tertiary-color-dark)};\n --#{$prefix}tertiary-bg: #{$body-tertiary-bg-dark};\n --#{$prefix}tertiary-bg-rgb: #{to-rgb($body-tertiary-bg-dark)};\n\n @each $color, $value in $theme-colors-text-dark {\n --#{$prefix}#{$color}-text-emphasis: #{$value};\n }\n\n @each $color, $value in $theme-colors-bg-subtle-dark {\n --#{$prefix}#{$color}-bg-subtle: #{$value};\n }\n\n @each $color, $value in $theme-colors-border-subtle-dark {\n --#{$prefix}#{$color}-border-subtle: #{$value};\n }\n\n --#{$prefix}heading-color: #{$headings-color-dark};\n\n --#{$prefix}link-color: #{$link-color-dark};\n --#{$prefix}link-hover-color: #{$link-hover-color-dark};\n --#{$prefix}link-color-rgb: #{to-rgb($link-color-dark)};\n --#{$prefix}link-hover-color-rgb: #{to-rgb($link-hover-color-dark)};\n\n --#{$prefix}code-color: #{$code-color-dark};\n --#{$prefix}highlight-color: #{$mark-color-dark};\n --#{$prefix}highlight-bg: #{$mark-bg-dark};\n\n --#{$prefix}border-color: #{$border-color-dark};\n --#{$prefix}border-color-translucent: #{$border-color-translucent-dark};\n\n --#{$prefix}form-valid-color: #{$form-valid-color-dark};\n --#{$prefix}form-valid-border-color: #{$form-valid-border-color-dark};\n --#{$prefix}form-invalid-color: #{$form-invalid-color-dark};\n --#{$prefix}form-invalid-border-color: #{$form-invalid-border-color-dark};\n // scss-docs-end root-dark-mode-vars\n }\n}\n","// stylelint-disable scss/dimension-no-non-numeric-values\n\n// SCSS RFS mixin\n//\n// Automated responsive values for font sizes, paddings, margins and much more\n//\n// Licensed under MIT (https://github.com/twbs/rfs/blob/main/LICENSE)\n\n// Configuration\n\n// Base value\n$rfs-base-value: 1.25rem !default;\n$rfs-unit: rem !default;\n\n@if $rfs-unit != rem and $rfs-unit != px {\n @error \"`#{$rfs-unit}` is not a valid unit for $rfs-unit. Use `px` or `rem`.\";\n}\n\n// Breakpoint at where values start decreasing if screen width is smaller\n$rfs-breakpoint: 1200px !default;\n$rfs-breakpoint-unit: px !default;\n\n@if $rfs-breakpoint-unit != px and $rfs-breakpoint-unit != em and $rfs-breakpoint-unit != rem {\n @error \"`#{$rfs-breakpoint-unit}` is not a valid unit for $rfs-breakpoint-unit. Use `px`, `em` or `rem`.\";\n}\n\n// Resize values based on screen height and width\n$rfs-two-dimensional: false !default;\n\n// Factor of decrease\n$rfs-factor: 10 !default;\n\n@if type-of($rfs-factor) != number or $rfs-factor <= 1 {\n @error \"`#{$rfs-factor}` is not a valid $rfs-factor, it must be greater than 1.\";\n}\n\n// Mode. Possibilities: \"min-media-query\", \"max-media-query\"\n$rfs-mode: min-media-query !default;\n\n// Generate enable or disable classes. Possibilities: false, \"enable\" or \"disable\"\n$rfs-class: false !default;\n\n// 1 rem = $rfs-rem-value px\n$rfs-rem-value: 16 !default;\n\n// Safari iframe resize bug: https://github.com/twbs/rfs/issues/14\n$rfs-safari-iframe-resize-bug-fix: false !default;\n\n// Disable RFS by setting $enable-rfs to false\n$enable-rfs: true !default;\n\n// Cache $rfs-base-value unit\n$rfs-base-value-unit: unit($rfs-base-value);\n\n@function divide($dividend, $divisor, $precision: 10) {\n $sign: if($dividend > 0 and $divisor > 0 or $dividend < 0 and $divisor < 0, 1, -1);\n $dividend: abs($dividend);\n $divisor: abs($divisor);\n @if $dividend == 0 {\n @return 0;\n }\n @if $divisor == 0 {\n @error \"Cannot divide by 0\";\n }\n $remainder: $dividend;\n $result: 0;\n $factor: 10;\n @while ($remainder > 0 and $precision >= 0) {\n $quotient: 0;\n @while ($remainder >= $divisor) {\n $remainder: $remainder - $divisor;\n $quotient: $quotient + 1;\n }\n $result: $result * 10 + $quotient;\n $factor: $factor * .1;\n $remainder: $remainder * 10;\n $precision: $precision - 1;\n @if ($precision < 0 and $remainder >= $divisor * 5) {\n $result: $result + 1;\n }\n }\n $result: $result * $factor * $sign;\n $dividend-unit: unit($dividend);\n $divisor-unit: unit($divisor);\n $unit-map: (\n \"px\": 1px,\n \"rem\": 1rem,\n \"em\": 1em,\n \"%\": 1%\n );\n @if ($dividend-unit != $divisor-unit and map-has-key($unit-map, $dividend-unit)) {\n $result: $result * map-get($unit-map, $dividend-unit);\n }\n @return $result;\n}\n\n// Remove px-unit from $rfs-base-value for calculations\n@if $rfs-base-value-unit == px {\n $rfs-base-value: divide($rfs-base-value, $rfs-base-value * 0 + 1);\n}\n@else if $rfs-base-value-unit == rem {\n $rfs-base-value: divide($rfs-base-value, divide($rfs-base-value * 0 + 1, $rfs-rem-value));\n}\n\n// Cache $rfs-breakpoint unit to prevent multiple calls\n$rfs-breakpoint-unit-cache: unit($rfs-breakpoint);\n\n// Remove unit from $rfs-breakpoint for calculations\n@if $rfs-breakpoint-unit-cache == px {\n $rfs-breakpoint: divide($rfs-breakpoint, $rfs-breakpoint * 0 + 1);\n}\n@else if $rfs-breakpoint-unit-cache == rem or $rfs-breakpoint-unit-cache == \"em\" {\n $rfs-breakpoint: divide($rfs-breakpoint, divide($rfs-breakpoint * 0 + 1, $rfs-rem-value));\n}\n\n// Calculate the media query value\n$rfs-mq-value: if($rfs-breakpoint-unit == px, #{$rfs-breakpoint}px, #{divide($rfs-breakpoint, $rfs-rem-value)}#{$rfs-breakpoint-unit});\n$rfs-mq-property-width: if($rfs-mode == max-media-query, max-width, min-width);\n$rfs-mq-property-height: if($rfs-mode == max-media-query, max-height, min-height);\n\n// Internal mixin used to determine which media query needs to be used\n@mixin _rfs-media-query {\n @if $rfs-two-dimensional {\n @if $rfs-mode == max-media-query {\n @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}), (#{$rfs-mq-property-height}: #{$rfs-mq-value}) {\n @content;\n }\n }\n @else {\n @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}) and (#{$rfs-mq-property-height}: #{$rfs-mq-value}) {\n @content;\n }\n }\n }\n @else {\n @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}) {\n @content;\n }\n }\n}\n\n// Internal mixin that adds disable classes to the selector if needed.\n@mixin _rfs-rule {\n @if $rfs-class == disable and $rfs-mode == max-media-query {\n // Adding an extra class increases specificity, which prevents the media query to override the property\n &,\n .disable-rfs &,\n &.disable-rfs {\n @content;\n }\n }\n @else if $rfs-class == enable and $rfs-mode == min-media-query {\n .enable-rfs &,\n &.enable-rfs {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Internal mixin that adds enable classes to the selector if needed.\n@mixin _rfs-media-query-rule {\n\n @if $rfs-class == enable {\n @if $rfs-mode == min-media-query {\n @content;\n }\n\n @include _rfs-media-query () {\n .enable-rfs &,\n &.enable-rfs {\n @content;\n }\n }\n }\n @else {\n @if $rfs-class == disable and $rfs-mode == min-media-query {\n .disable-rfs &,\n &.disable-rfs {\n @content;\n }\n }\n @include _rfs-media-query () {\n @content;\n }\n }\n}\n\n// Helper function to get the formatted non-responsive value\n@function rfs-value($values) {\n // Convert to list\n $values: if(type-of($values) != list, ($values,), $values);\n\n $val: \"\";\n\n // Loop over each value and calculate value\n @each $value in $values {\n @if $value == 0 {\n $val: $val + \" 0\";\n }\n @else {\n // Cache $value unit\n $unit: if(type-of($value) == \"number\", unit($value), false);\n\n @if $unit == px {\n // Convert to rem if needed\n $val: $val + \" \" + if($rfs-unit == rem, #{divide($value, $value * 0 + $rfs-rem-value)}rem, $value);\n }\n @else if $unit == rem {\n // Convert to px if needed\n $val: $val + \" \" + if($rfs-unit == px, #{divide($value, $value * 0 + 1) * $rfs-rem-value}px, $value);\n } @else {\n // If $value isn't a number (like inherit) or $value has a unit (not px or rem, like 1.5em) or $ is 0, just print the value\n $val: $val + \" \" + $value;\n }\n }\n }\n\n // Remove first space\n @return unquote(str-slice($val, 2));\n}\n\n// Helper function to get the responsive value calculated by RFS\n@function rfs-fluid-value($values) {\n // Convert to list\n $values: if(type-of($values) != list, ($values,), $values);\n\n $val: \"\";\n\n // Loop over each value and calculate value\n @each $value in $values {\n @if $value == 0 {\n $val: $val + \" 0\";\n } @else {\n // Cache $value unit\n $unit: if(type-of($value) == \"number\", unit($value), false);\n\n // If $value isn't a number (like inherit) or $value has a unit (not px or rem, like 1.5em) or $ is 0, just print the value\n @if not $unit or $unit != px and $unit != rem {\n $val: $val + \" \" + $value;\n } @else {\n // Remove unit from $value for calculations\n $value: divide($value, $value * 0 + if($unit == px, 1, divide(1, $rfs-rem-value)));\n\n // Only add the media query if the value is greater than the minimum value\n @if abs($value) <= $rfs-base-value or not $enable-rfs {\n $val: $val + \" \" + if($rfs-unit == rem, #{divide($value, $rfs-rem-value)}rem, #{$value}px);\n }\n @else {\n // Calculate the minimum value\n $value-min: $rfs-base-value + divide(abs($value) - $rfs-base-value, $rfs-factor);\n\n // Calculate difference between $value and the minimum value\n $value-diff: abs($value) - $value-min;\n\n // Base value formatting\n $min-width: if($rfs-unit == rem, #{divide($value-min, $rfs-rem-value)}rem, #{$value-min}px);\n\n // Use negative value if needed\n $min-width: if($value < 0, -$min-width, $min-width);\n\n // Use `vmin` if two-dimensional is enabled\n $variable-unit: if($rfs-two-dimensional, vmin, vw);\n\n // Calculate the variable width between 0 and $rfs-breakpoint\n $variable-width: #{divide($value-diff * 100, $rfs-breakpoint)}#{$variable-unit};\n\n // Return the calculated value\n $val: $val + \" calc(\" + $min-width + if($value < 0, \" - \", \" + \") + $variable-width + \")\";\n }\n }\n }\n }\n\n // Remove first space\n @return unquote(str-slice($val, 2));\n}\n\n// RFS mixin\n@mixin rfs($values, $property: font-size) {\n @if $values != null {\n $val: rfs-value($values);\n $fluid-val: rfs-fluid-value($values);\n\n // Do not print the media query if responsive & non-responsive values are the same\n @if $val == $fluid-val {\n #{$property}: $val;\n }\n @else {\n @include _rfs-rule () {\n #{$property}: if($rfs-mode == max-media-query, $val, $fluid-val);\n\n // Include safari iframe resize fix if needed\n min-width: if($rfs-safari-iframe-resize-bug-fix, (0 * 1vw), null);\n }\n\n @include _rfs-media-query-rule () {\n #{$property}: if($rfs-mode == max-media-query, $fluid-val, $val);\n }\n }\n }\n}\n\n// Shorthand helper mixins\n@mixin font-size($value) {\n @include rfs($value);\n}\n\n@mixin padding($value) {\n @include rfs($value, padding);\n}\n\n@mixin padding-top($value) {\n @include rfs($value, padding-top);\n}\n\n@mixin padding-right($value) {\n @include rfs($value, padding-right);\n}\n\n@mixin padding-bottom($value) {\n @include rfs($value, padding-bottom);\n}\n\n@mixin padding-left($value) {\n @include rfs($value, padding-left);\n}\n\n@mixin margin($value) {\n @include rfs($value, margin);\n}\n\n@mixin margin-top($value) {\n @include rfs($value, margin-top);\n}\n\n@mixin margin-right($value) {\n @include rfs($value, margin-right);\n}\n\n@mixin margin-bottom($value) {\n @include rfs($value, margin-bottom);\n}\n\n@mixin margin-left($value) {\n @include rfs($value, margin-left);\n}\n","/*!\n * Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)\n * Copyright 2011-2024 The Bootstrap Authors\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n:root,\n[data-bs-theme=light] {\n --bs-blue: #0d6efd;\n --bs-indigo: #6610f2;\n --bs-purple: #6f42c1;\n --bs-pink: #d63384;\n --bs-red: #dc3545;\n --bs-orange: #fd7e14;\n --bs-yellow: #ffc107;\n --bs-green: #198754;\n --bs-teal: #20c997;\n --bs-cyan: #0dcaf0;\n --bs-black: #000;\n --bs-white: #fff;\n --bs-gray: #6c757d;\n --bs-gray-dark: #343a40;\n --bs-gray-100: #f8f9fa;\n --bs-gray-200: #e9ecef;\n --bs-gray-300: #dee2e6;\n --bs-gray-400: #ced4da;\n --bs-gray-500: #adb5bd;\n --bs-gray-600: #6c757d;\n --bs-gray-700: #495057;\n --bs-gray-800: #343a40;\n --bs-gray-900: #212529;\n --bs-primary: #0d6efd;\n --bs-secondary: #6c757d;\n --bs-success: #198754;\n --bs-info: #0dcaf0;\n --bs-warning: #ffc107;\n --bs-danger: #dc3545;\n --bs-light: #f8f9fa;\n --bs-dark: #212529;\n --bs-primary-rgb: 13, 110, 253;\n --bs-secondary-rgb: 108, 117, 125;\n --bs-success-rgb: 25, 135, 84;\n --bs-info-rgb: 13, 202, 240;\n --bs-warning-rgb: 255, 193, 7;\n --bs-danger-rgb: 220, 53, 69;\n --bs-light-rgb: 248, 249, 250;\n --bs-dark-rgb: 33, 37, 41;\n --bs-primary-text-emphasis: #052c65;\n --bs-secondary-text-emphasis: #2b2f32;\n --bs-success-text-emphasis: #0a3622;\n --bs-info-text-emphasis: #055160;\n --bs-warning-text-emphasis: #664d03;\n --bs-danger-text-emphasis: #58151c;\n --bs-light-text-emphasis: #495057;\n --bs-dark-text-emphasis: #495057;\n --bs-primary-bg-subtle: #cfe2ff;\n --bs-secondary-bg-subtle: #e2e3e5;\n --bs-success-bg-subtle: #d1e7dd;\n --bs-info-bg-subtle: #cff4fc;\n --bs-warning-bg-subtle: #fff3cd;\n --bs-danger-bg-subtle: #f8d7da;\n --bs-light-bg-subtle: #fcfcfd;\n --bs-dark-bg-subtle: #ced4da;\n --bs-primary-border-subtle: #9ec5fe;\n --bs-secondary-border-subtle: #c4c8cb;\n --bs-success-border-subtle: #a3cfbb;\n --bs-info-border-subtle: #9eeaf9;\n --bs-warning-border-subtle: #ffe69c;\n --bs-danger-border-subtle: #f1aeb5;\n --bs-light-border-subtle: #e9ecef;\n --bs-dark-border-subtle: #adb5bd;\n --bs-white-rgb: 255, 255, 255;\n --bs-black-rgb: 0, 0, 0;\n --bs-font-sans-serif: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", \"Liberation Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));\n --bs-body-font-family: var(--bs-font-sans-serif);\n --bs-body-font-size: 1rem;\n --bs-body-font-weight: 400;\n --bs-body-line-height: 1.5;\n --bs-body-color: #212529;\n --bs-body-color-rgb: 33, 37, 41;\n --bs-body-bg: #fff;\n --bs-body-bg-rgb: 255, 255, 255;\n --bs-emphasis-color: #000;\n --bs-emphasis-color-rgb: 0, 0, 0;\n --bs-secondary-color: rgba(33, 37, 41, 0.75);\n --bs-secondary-color-rgb: 33, 37, 41;\n --bs-secondary-bg: #e9ecef;\n --bs-secondary-bg-rgb: 233, 236, 239;\n --bs-tertiary-color: rgba(33, 37, 41, 0.5);\n --bs-tertiary-color-rgb: 33, 37, 41;\n --bs-tertiary-bg: #f8f9fa;\n --bs-tertiary-bg-rgb: 248, 249, 250;\n --bs-heading-color: inherit;\n --bs-link-color: #0d6efd;\n --bs-link-color-rgb: 13, 110, 253;\n --bs-link-decoration: underline;\n --bs-link-hover-color: #0a58ca;\n --bs-link-hover-color-rgb: 10, 88, 202;\n --bs-code-color: #d63384;\n --bs-highlight-color: #212529;\n --bs-highlight-bg: #fff3cd;\n --bs-border-width: 1px;\n --bs-border-style: solid;\n --bs-border-color: #dee2e6;\n --bs-border-color-translucent: rgba(0, 0, 0, 0.175);\n --bs-border-radius: 0.375rem;\n --bs-border-radius-sm: 0.25rem;\n --bs-border-radius-lg: 0.5rem;\n --bs-border-radius-xl: 1rem;\n --bs-border-radius-xxl: 2rem;\n --bs-border-radius-2xl: var(--bs-border-radius-xxl);\n --bs-border-radius-pill: 50rem;\n --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);\n --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);\n --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);\n --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);\n --bs-focus-ring-width: 0.25rem;\n --bs-focus-ring-opacity: 0.25;\n --bs-focus-ring-color: rgba(13, 110, 253, 0.25);\n --bs-form-valid-color: #198754;\n --bs-form-valid-border-color: #198754;\n --bs-form-invalid-color: #dc3545;\n --bs-form-invalid-border-color: #dc3545;\n}\n\n[data-bs-theme=dark] {\n color-scheme: dark;\n --bs-body-color: #dee2e6;\n --bs-body-color-rgb: 222, 226, 230;\n --bs-body-bg: #212529;\n --bs-body-bg-rgb: 33, 37, 41;\n --bs-emphasis-color: #fff;\n --bs-emphasis-color-rgb: 255, 255, 255;\n --bs-secondary-color: rgba(222, 226, 230, 0.75);\n --bs-secondary-color-rgb: 222, 226, 230;\n --bs-secondary-bg: #343a40;\n --bs-secondary-bg-rgb: 52, 58, 64;\n --bs-tertiary-color: rgba(222, 226, 230, 0.5);\n --bs-tertiary-color-rgb: 222, 226, 230;\n --bs-tertiary-bg: #2b3035;\n --bs-tertiary-bg-rgb: 43, 48, 53;\n --bs-primary-text-emphasis: #6ea8fe;\n --bs-secondary-text-emphasis: #a7acb1;\n --bs-success-text-emphasis: #75b798;\n --bs-info-text-emphasis: #6edff6;\n --bs-warning-text-emphasis: #ffda6a;\n --bs-danger-text-emphasis: #ea868f;\n --bs-light-text-emphasis: #f8f9fa;\n --bs-dark-text-emphasis: #dee2e6;\n --bs-primary-bg-subtle: #031633;\n --bs-secondary-bg-subtle: #161719;\n --bs-success-bg-subtle: #051b11;\n --bs-info-bg-subtle: #032830;\n --bs-warning-bg-subtle: #332701;\n --bs-danger-bg-subtle: #2c0b0e;\n --bs-light-bg-subtle: #343a40;\n --bs-dark-bg-subtle: #1a1d20;\n --bs-primary-border-subtle: #084298;\n --bs-secondary-border-subtle: #41464b;\n --bs-success-border-subtle: #0f5132;\n --bs-info-border-subtle: #087990;\n --bs-warning-border-subtle: #997404;\n --bs-danger-border-subtle: #842029;\n --bs-light-border-subtle: #495057;\n --bs-dark-border-subtle: #343a40;\n --bs-heading-color: inherit;\n --bs-link-color: #6ea8fe;\n --bs-link-hover-color: #8bb9fe;\n --bs-link-color-rgb: 110, 168, 254;\n --bs-link-hover-color-rgb: 139, 185, 254;\n --bs-code-color: #e685b5;\n --bs-highlight-color: #dee2e6;\n --bs-highlight-bg: #664d03;\n --bs-border-color: #495057;\n --bs-border-color-translucent: rgba(255, 255, 255, 0.15);\n --bs-form-valid-color: #75b798;\n --bs-form-valid-border-color: #75b798;\n --bs-form-invalid-color: #ea868f;\n --bs-form-invalid-border-color: #ea868f;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n :root {\n scroll-behavior: smooth;\n }\n}\n\nbody {\n margin: 0;\n font-family: var(--bs-body-font-family);\n font-size: var(--bs-body-font-size);\n font-weight: var(--bs-body-font-weight);\n line-height: var(--bs-body-line-height);\n color: var(--bs-body-color);\n text-align: var(--bs-body-text-align);\n background-color: var(--bs-body-bg);\n -webkit-text-size-adjust: 100%;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\nhr {\n margin: 1rem 0;\n color: inherit;\n border: 0;\n border-top: var(--bs-border-width) solid;\n opacity: 0.25;\n}\n\nh6, h5, h4, h3, h2, h1 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n font-weight: 500;\n line-height: 1.2;\n color: var(--bs-heading-color);\n}\n\nh1 {\n font-size: calc(1.375rem + 1.5vw);\n}\n@media (min-width: 1200px) {\n h1 {\n font-size: 2.5rem;\n }\n}\n\nh2 {\n font-size: calc(1.325rem + 0.9vw);\n}\n@media (min-width: 1200px) {\n h2 {\n font-size: 2rem;\n }\n}\n\nh3 {\n font-size: calc(1.3rem + 0.6vw);\n}\n@media (min-width: 1200px) {\n h3 {\n font-size: 1.75rem;\n }\n}\n\nh4 {\n font-size: calc(1.275rem + 0.3vw);\n}\n@media (min-width: 1200px) {\n h4 {\n font-size: 1.5rem;\n }\n}\n\nh5 {\n font-size: 1.25rem;\n}\n\nh6 {\n font-size: 1rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title] {\n text-decoration: underline dotted;\n cursor: help;\n text-decoration-skip-ink: none;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul {\n padding-left: 2rem;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: 0.5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 0.875em;\n}\n\nmark {\n padding: 0.1875em;\n color: var(--bs-highlight-color);\n background-color: var(--bs-highlight-bg);\n}\n\nsub,\nsup {\n position: relative;\n font-size: 0.75em;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\na {\n color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));\n text-decoration: underline;\n}\na:hover {\n --bs-link-color-rgb: var(--bs-link-hover-color-rgb);\n}\n\na:not([href]):not([class]), a:not([href]):not([class]):hover {\n color: inherit;\n text-decoration: none;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: var(--bs-font-monospace);\n font-size: 1em;\n}\n\npre {\n display: block;\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n font-size: 0.875em;\n}\npre code {\n font-size: inherit;\n color: inherit;\n word-break: normal;\n}\n\ncode {\n font-size: 0.875em;\n color: var(--bs-code-color);\n word-wrap: break-word;\n}\na > code {\n color: inherit;\n}\n\nkbd {\n padding: 0.1875rem 0.375rem;\n font-size: 0.875em;\n color: var(--bs-body-bg);\n background-color: var(--bs-body-color);\n border-radius: 0.25rem;\n}\nkbd kbd {\n padding: 0;\n font-size: 1em;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg,\nsvg {\n vertical-align: middle;\n}\n\ntable {\n caption-side: bottom;\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n color: var(--bs-secondary-color);\n text-align: left;\n}\n\nth {\n text-align: inherit;\n text-align: -webkit-match-parent;\n}\n\nthead,\ntbody,\ntfoot,\ntr,\ntd,\nth {\n border-color: inherit;\n border-style: solid;\n border-width: 0;\n}\n\nlabel {\n display: inline-block;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus:not(:focus-visible) {\n outline: 0;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\n[role=button] {\n cursor: pointer;\n}\n\nselect {\n word-wrap: normal;\n}\nselect:disabled {\n opacity: 1;\n}\n\n[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {\n display: none !important;\n}\n\nbutton,\n[type=button],\n[type=reset],\n[type=submit] {\n -webkit-appearance: button;\n}\nbutton:not(:disabled),\n[type=button]:not(:disabled),\n[type=reset]:not(:disabled),\n[type=submit]:not(:disabled) {\n cursor: pointer;\n}\n\n::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ntextarea {\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n float: left;\n width: 100%;\n padding: 0;\n margin-bottom: 0.5rem;\n font-size: calc(1.275rem + 0.3vw);\n line-height: inherit;\n}\n@media (min-width: 1200px) {\n legend {\n font-size: 1.5rem;\n }\n}\nlegend + * {\n clear: left;\n}\n\n::-webkit-datetime-edit-fields-wrapper,\n::-webkit-datetime-edit-text,\n::-webkit-datetime-edit-minute,\n::-webkit-datetime-edit-hour-field,\n::-webkit-datetime-edit-day-field,\n::-webkit-datetime-edit-month-field,\n::-webkit-datetime-edit-year-field {\n padding: 0;\n}\n\n::-webkit-inner-spin-button {\n height: auto;\n}\n\n[type=search] {\n -webkit-appearance: textfield;\n outline-offset: -2px;\n}\n\n/* rtl:raw:\n[type=\"tel\"],\n[type=\"url\"],\n[type=\"email\"],\n[type=\"number\"] {\n direction: ltr;\n}\n*/\n::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-color-swatch-wrapper {\n padding: 0;\n}\n\n::file-selector-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\niframe {\n border: 0;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[hidden] {\n display: none !important;\n}\n\n/*# sourceMappingURL=bootstrap-reboot.css.map */\n","// scss-docs-start color-mode-mixin\n@mixin color-mode($mode: light, $root: false) {\n @if $color-mode-type == \"media-query\" {\n @if $root == true {\n @media (prefers-color-scheme: $mode) {\n :root {\n @content;\n }\n }\n } @else {\n @media (prefers-color-scheme: $mode) {\n @content;\n }\n }\n } @else {\n [data-bs-theme=\"#{$mode}\"] {\n @content;\n }\n }\n}\n// scss-docs-end color-mode-mixin\n","// stylelint-disable declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\n\n\n// Reboot\n//\n// Normalization of HTML elements, manually forked from Normalize.css to remove\n// styles targeting irrelevant browsers while applying new styles.\n//\n// Normalize is licensed MIT. https://github.com/necolas/normalize.css\n\n\n// Document\n//\n// Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n\n// Root\n//\n// Ability to the value of the root font sizes, affecting the value of `rem`.\n// null by default, thus nothing is generated.\n\n:root {\n @if $font-size-root != null {\n @include font-size(var(--#{$prefix}root-font-size));\n }\n\n @if $enable-smooth-scroll {\n @media (prefers-reduced-motion: no-preference) {\n scroll-behavior: smooth;\n }\n }\n}\n\n\n// Body\n//\n// 1. Remove the margin in all browsers.\n// 2. As a best practice, apply a default `background-color`.\n// 3. Prevent adjustments of font size after orientation changes in iOS.\n// 4. Change the default tap highlight to be completely transparent in iOS.\n\n// scss-docs-start reboot-body-rules\nbody {\n margin: 0; // 1\n font-family: var(--#{$prefix}body-font-family);\n @include font-size(var(--#{$prefix}body-font-size));\n font-weight: var(--#{$prefix}body-font-weight);\n line-height: var(--#{$prefix}body-line-height);\n color: var(--#{$prefix}body-color);\n text-align: var(--#{$prefix}body-text-align);\n background-color: var(--#{$prefix}body-bg); // 2\n -webkit-text-size-adjust: 100%; // 3\n -webkit-tap-highlight-color: rgba($black, 0); // 4\n}\n// scss-docs-end reboot-body-rules\n\n\n// Content grouping\n//\n// 1. Reset Firefox's gray color\n\nhr {\n margin: $hr-margin-y 0;\n color: $hr-color; // 1\n border: 0;\n border-top: $hr-border-width solid $hr-border-color;\n opacity: $hr-opacity;\n}\n\n\n// Typography\n//\n// 1. Remove top margins from headings\n// By default, `

`-`

` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n\n%heading {\n margin-top: 0; // 1\n margin-bottom: $headings-margin-bottom;\n font-family: $headings-font-family;\n font-style: $headings-font-style;\n font-weight: $headings-font-weight;\n line-height: $headings-line-height;\n color: var(--#{$prefix}heading-color);\n}\n\nh1 {\n @extend %heading;\n @include font-size($h1-font-size);\n}\n\nh2 {\n @extend %heading;\n @include font-size($h2-font-size);\n}\n\nh3 {\n @extend %heading;\n @include font-size($h3-font-size);\n}\n\nh4 {\n @extend %heading;\n @include font-size($h4-font-size);\n}\n\nh5 {\n @extend %heading;\n @include font-size($h5-font-size);\n}\n\nh6 {\n @extend %heading;\n @include font-size($h6-font-size);\n}\n\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\n\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n\n// Abbreviations\n//\n// 1. Add the correct text decoration in Chrome, Edge, Opera, and Safari.\n// 2. Add explicit cursor to indicate changed behavior.\n// 3. Prevent the text-decoration to be skipped.\n\nabbr[title] {\n text-decoration: underline dotted; // 1\n cursor: help; // 2\n text-decoration-skip-ink: none; // 3\n}\n\n\n// Address\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\n\n// Lists\n\nol,\nul {\n padding-left: 2rem;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\n// 1. Undo browser default\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // 1\n}\n\n\n// Blockquote\n\nblockquote {\n margin: 0 0 1rem;\n}\n\n\n// Strong\n//\n// Add the correct font weight in Chrome, Edge, and Safari\n\nb,\nstrong {\n font-weight: $font-weight-bolder;\n}\n\n\n// Small\n//\n// Add the correct font size in all browsers\n\nsmall {\n @include font-size($small-font-size);\n}\n\n\n// Mark\n\nmark {\n padding: $mark-padding;\n color: var(--#{$prefix}highlight-color);\n background-color: var(--#{$prefix}highlight-bg);\n}\n\n\n// Sub and Sup\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n\nsub,\nsup {\n position: relative;\n @include font-size($sub-sup-font-size);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n// Links\n\na {\n color: rgba(var(--#{$prefix}link-color-rgb), var(--#{$prefix}link-opacity, 1));\n text-decoration: $link-decoration;\n\n &:hover {\n --#{$prefix}link-color-rgb: var(--#{$prefix}link-hover-color-rgb);\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]):not([class]) {\n &,\n &:hover {\n color: inherit;\n text-decoration: none;\n }\n}\n\n\n// Code\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-code;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n}\n\n// 1. Remove browser default top margin\n// 2. Reset browser default of `1em` to use `rem`s\n// 3. Don't allow content to break outside\n\npre {\n display: block;\n margin-top: 0; // 1\n margin-bottom: 1rem; // 2\n overflow: auto; // 3\n @include font-size($code-font-size);\n color: $pre-color;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n @include font-size(inherit);\n color: inherit;\n word-break: normal;\n }\n}\n\ncode {\n @include font-size($code-font-size);\n color: var(--#{$prefix}code-color);\n word-wrap: break-word;\n\n // Streamline the style when inside anchors to avoid broken underline and more\n a > & {\n color: inherit;\n }\n}\n\nkbd {\n padding: $kbd-padding-y $kbd-padding-x;\n @include font-size($kbd-font-size);\n color: $kbd-color;\n background-color: $kbd-bg;\n @include border-radius($border-radius-sm);\n\n kbd {\n padding: 0;\n @include font-size(1em);\n font-weight: $nested-kbd-font-weight;\n }\n}\n\n\n// Figures\n//\n// Apply a consistent margin strategy (matches our type styles).\n\nfigure {\n margin: 0 0 1rem;\n}\n\n\n// Images and content\n\nimg,\nsvg {\n vertical-align: middle;\n}\n\n\n// Tables\n//\n// Prevent double borders\n\ntable {\n caption-side: bottom;\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: $table-cell-padding-y;\n padding-bottom: $table-cell-padding-y;\n color: $table-caption-color;\n text-align: left;\n}\n\n// 1. Removes font-weight bold by inheriting\n// 2. Matches default `` alignment by inheriting `text-align`.\n// 3. Fix alignment for Safari\n\nth {\n font-weight: $table-th-font-weight; // 1\n text-align: inherit; // 2\n text-align: -webkit-match-parent; // 3\n}\n\nthead,\ntbody,\ntfoot,\ntr,\ntd,\nth {\n border-color: inherit;\n border-style: solid;\n border-width: 0;\n}\n\n\n// Forms\n//\n// 1. Allow labels to use `margin` for spacing.\n\nlabel {\n display: inline-block; // 1\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n// See https://github.com/twbs/bootstrap/issues/24093\n\nbutton {\n // stylelint-disable-next-line property-disallowed-list\n border-radius: 0;\n}\n\n// Explicitly remove focus outline in Chromium when it shouldn't be\n// visible (e.g. as result of mouse click or touch tap). It already\n// should be doing this automatically, but seems to currently be\n// confused and applies its very visible two-tone outline anyway.\n\nbutton:focus:not(:focus-visible) {\n outline: 0;\n}\n\n// 1. Remove the margin in Firefox and Safari\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // 1\n font-family: inherit;\n @include font-size(inherit);\n line-height: inherit;\n}\n\n// Remove the inheritance of text transform in Firefox\nbutton,\nselect {\n text-transform: none;\n}\n// Set the cursor for non-`